]> git.cworth.org Git - vogl/blob - src/common/mtqueue.cpp
Initial vogl checkin
[vogl] / src / common / mtqueue.cpp
1 /**************************************************************************
2  *
3  * Copyright 2013-2014 RAD Game Tools and Valve Software
4  * All Rights Reserved.
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to deal
8  * in the Software without restriction, including without limitation the rights
9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  * copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22  * THE SOFTWARE.
23  *
24  **************************************************************************/
25
26
27 #include <stdio.h>
28 #include <time.h>
29 #include <string.h>
30 #include <unistd.h>
31 #include <malloc.h>
32 #include <pthread.h>
33 #include "mtqueue.h"
34
35 using namespace queues;
36
37 MtQueue::MtQueue()
38 {
39     m_pbDataList = NULL;
40     m_iHead = 0;
41     m_iTail = 0;
42     m_cElements = 0;
43 }
44
45 MtQueue::~MtQueue()
46 {
47     this->Purge(); // Should delete whatever is remaining in the queue
48
49     if (m_pbDataList)
50     {
51         free(m_pbDataList);
52
53         (void)pthread_mutex_destroy(&m_mutex); //  Nothing to look at
54     }
55 }
56
57 //
58 //  Creates up the empty queue, sets the location for empty (m_iHead = m_iTail), creates the
59 //  locking mutex and returns.
60 //
61
62 MTQ_CODE MtQueue::Initialize(unsigned int nElementCount)
63 {
64     MTQ_CODE mtqCode = MTQ_NONE;
65     pthread_mutexattr_t mAttr;
66     int ec = 0; // error code for mutex calls
67
68     m_cElements = nElementCount + 1;
69
70     //  Malloc up the list to hold the queue
71     m_pbDataList = (QELEM *)malloc(m_cElements * sizeof(QELEM));
72     m_iTail = m_iHead = 0;
73
74     if (NULL == m_pbDataList)
75     {
76         mtqCode = MTQ_MEMERROR;
77
78         goto out;
79     }
80
81     //  Create our protective mutex
82     // setup recursive mutex for mutex attribute - this is most like, in behavior, to Windows critical sections
83     //
84     //  PTHREAD_MUTEX_RECURSIVE_NP means that the mutex can be used recursively.
85     /*
86                 The pthread_mutexattr_settype() function shall fail if:
87                 EINVAL  The value type is invalid.
88         */
89     ec = pthread_mutexattr_init(&mAttr);
90     if (0 != ec)
91     {
92         printf("Error initializing mutex attribute:  ec = %d\n", ec);
93         mtqCode = MTQ_SYSERROR;
94         goto out;
95     }
96
97     ec = pthread_mutexattr_settype(&mAttr, PTHREAD_MUTEX_RECURSIVE_NP);
98     if (0 != ec)
99     {
100         printf("Error setting mutex attribute:  ec = %d\n", ec);
101         mtqCode = MTQ_SYSERROR;
102         goto out;
103     }
104     // Use the mutex attribute to create the mutex
105     /* 
106                 The pthread_mutex_init() function shall fail if:
107                 EAGAIN  The system lacked the necessary resources (other than memory) to initialize another mutex.
108                 ENOMEM  Insufficient memory exists to initialize the mutex.
109                 EPERM   The caller does not have the privilege to perform the operation.
110
111                 The pthread_mutex_init() function may fail if:
112                 EBUSY   The implementation has detected an attempt to reinitialize the object referenced by mutex, a previously initialized, but not yet destroyed, mutex.
113                 EINVAL  The value specified by attr is invalid.
114         */
115     ec = pthread_mutex_init(&m_mutex, &mAttr);
116     if (0 != ec)
117     {
118         printf("Error creating mutex:  ec = %d\n", ec);
119         mtqCode = MTQ_SYSERROR;
120         goto out;
121     }
122
123 out:
124     if (MTQ_NONE != mtqCode)
125     {
126         //  Clean up
127         if (m_pbDataList)
128         {
129             free(m_pbDataList);
130             m_pbDataList = NULL;
131         }
132     }
133
134     return mtqCode;
135 }
136
137 //
138 //  Insert at m_iHead.
139 //
140 MTQ_CODE MtQueue::Enqueue(unsigned int cb, char *pb, unsigned int timeoutMilSec, unsigned int nRetries)
141 {
142     MTQ_CODE mtqCode = MTQ_NONE;
143     int ec = 0;
144     (void)ec;
145     unsigned int cTries = 0;
146
147     char *pbT = NULL;
148
149     if (0 == nRetries)
150         nRetries = 1;
151
152     while (cTries < nRetries)
153     {
154         // acquire mutex
155         ec = pthread_mutex_lock(&m_mutex);
156
157         if (!this->IsFull())
158         {
159             // We have the lock, now to add the element to the list
160             break;
161         }
162
163         pthread_mutex_unlock(&m_mutex);
164
165         usleep(timeoutMilSec);
166         cTries++;
167     }
168
169     //  Note:  The only way we have the mutex is if the queue has room for us to enqueue.
170     //  Otherwise, we cannot have the mutex.
171
172     if (cTries == nRetries)
173     {
174         //
175         //  The queue never became unfull...
176         mtqCode = MTQ_FULL;
177         goto out;
178     }
179
180     //  We should have the mutex locked at this point
181     pbT = (char *)malloc(cb);
182     if (NULL == pbT)
183     {
184         printf("MTQUEUE: unable to enqueue data - unable to alloc memory\n");
185         mtqCode = MTQ_MEMERROR;
186
187         goto release;
188     }
189     memcpy(pbT, pb, cb);
190
191     m_pbDataList[m_iHead].cb = cb;
192     m_pbDataList[m_iHead].pb = pbT;
193
194     m_iHead = ((m_iHead + 1) % m_cElements);
195
196 release:
197     pthread_mutex_unlock(&m_mutex);
198 out:
199     return mtqCode;
200 }
201
202 //
203 //  Remove at m_iTail
204 //
205 MTQ_CODE MtQueue::Dequeue(unsigned int *pcb, char **ppb, unsigned int timeoutMilSec, unsigned int nRetries)
206 {
207     MTQ_CODE mtqCode = MTQ_NONE;
208     int ec = 0;
209     (void)ec;
210     unsigned int cTries = 0;
211
212     if (0 == nRetries)
213         nRetries = 1;
214
215     while (cTries < nRetries)
216     {
217         // acquire mutex
218         ec = pthread_mutex_lock(&m_mutex);
219         if (!IsEmpty())
220         {
221             // We have the lock, now to add the element to the list
222
223             break;
224         }
225
226         pthread_mutex_unlock(&m_mutex);
227
228         usleep(timeoutMilSec);
229         cTries++;
230     }
231
232     //  Note:  The only way we have the mutex is if the queue has room for us to enqueue.
233     //  Otherwise, we cannot have the mutex.
234
235     if (cTries == nRetries)
236     {
237         //
238         //  The queue never had anything in it...
239         mtqCode = MTQ_EMPTY;
240         goto out;
241     }
242
243     //  We should have the mutex locked at this point
244     *pcb = m_pbDataList[m_iTail].cb;
245     *ppb = m_pbDataList[m_iTail].pb;
246
247     m_iTail = ((m_iTail + 1) % m_cElements);
248
249     // release:
250     pthread_mutex_unlock(&m_mutex);
251
252 out:
253     return mtqCode;
254 }
255
256 //  go from iHead->iTail and simply release everything.
257 //  set it back to empty (iHead = iTail)
258 MTQ_CODE MtQueue::Purge()
259 {
260     MTQ_CODE mtqCode = MTQ_NONE;
261     int ec = 0;
262     (void)ec;
263
264     // acquire mutex
265     ec = pthread_mutex_lock(&m_mutex);
266     if (IsEmpty())
267     {
268         // optimization...the queue is already empty
269         goto release;
270     }
271
272     do
273     {
274         free(m_pbDataList[m_iHead].pb);
275         m_pbDataList[m_iHead].cb = 0;
276         m_pbDataList[m_iHead].pb = NULL;
277
278         m_iHead = ((m_iHead + 1) % m_cElements);
279
280     } while (m_iHead != m_iTail);
281
282     //  Just checking
283     if (!IsEmpty())
284     {
285         printf("MtQueue::Purge:  oops...internal state mucked up...should be an empty queue.\n");
286     }
287
288 release:
289     pthread_mutex_unlock(&m_mutex);
290
291     // out:
292     return mtqCode;
293 }
294
295 bool MtQueue::IsFull()
296 {
297     if (((m_iHead + 1) % m_cElements) == m_iTail)
298         return true;
299
300     return false;
301 }
302
303 bool MtQueue::IsEmpty()
304 {
305     return (m_iHead == m_iTail);
306 }