1 /**************************************************************************
3 * Copyright 2013-2014 RAD Game Tools and Valve Software
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:
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
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
24 **************************************************************************/
35 using namespace queues;
47 this->Purge(); // Should delete whatever is remaining in the queue
53 (void)pthread_mutex_destroy(&m_mutex); // Nothing to look at
58 // Creates up the empty queue, sets the location for empty (m_iHead = m_iTail), creates the
59 // locking mutex and returns.
62 MTQ_CODE MtQueue::Initialize(unsigned int nElementCount)
64 MTQ_CODE mtqCode = MTQ_NONE;
65 pthread_mutexattr_t mAttr;
66 int ec = 0; // error code for mutex calls
68 m_cElements = nElementCount + 1;
70 // Malloc up the list to hold the queue
71 m_pbDataList = (QELEM *)malloc(m_cElements * sizeof(QELEM));
72 m_iTail = m_iHead = 0;
74 if (NULL == m_pbDataList)
76 mtqCode = MTQ_MEMERROR;
81 // Create our protective mutex
82 // setup recursive mutex for mutex attribute - this is most like, in behavior, to Windows critical sections
84 // PTHREAD_MUTEX_RECURSIVE_NP means that the mutex can be used recursively.
86 The pthread_mutexattr_settype() function shall fail if:
87 EINVAL The value type is invalid.
89 ec = pthread_mutexattr_init(&mAttr);
92 printf("Error initializing mutex attribute: ec = %d\n", ec);
93 mtqCode = MTQ_SYSERROR;
97 ec = pthread_mutexattr_settype(&mAttr, PTHREAD_MUTEX_RECURSIVE_NP);
100 printf("Error setting mutex attribute: ec = %d\n", ec);
101 mtqCode = MTQ_SYSERROR;
104 // Use the mutex attribute to create the mutex
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.
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.
115 ec = pthread_mutex_init(&m_mutex, &mAttr);
118 printf("Error creating mutex: ec = %d\n", ec);
119 mtqCode = MTQ_SYSERROR;
124 if (MTQ_NONE != mtqCode)
138 // Insert at m_iHead.
140 MTQ_CODE MtQueue::Enqueue(unsigned int cb, char *pb, unsigned int timeoutMilSec, unsigned int nRetries)
142 MTQ_CODE mtqCode = MTQ_NONE;
145 unsigned int cTries = 0;
152 while (cTries < nRetries)
155 ec = pthread_mutex_lock(&m_mutex);
159 // We have the lock, now to add the element to the list
163 pthread_mutex_unlock(&m_mutex);
165 usleep(timeoutMilSec);
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.
172 if (cTries == nRetries)
175 // The queue never became unfull...
180 // We should have the mutex locked at this point
181 pbT = (char *)malloc(cb);
184 printf("MTQUEUE: unable to enqueue data - unable to alloc memory\n");
185 mtqCode = MTQ_MEMERROR;
191 m_pbDataList[m_iHead].cb = cb;
192 m_pbDataList[m_iHead].pb = pbT;
194 m_iHead = ((m_iHead + 1) % m_cElements);
197 pthread_mutex_unlock(&m_mutex);
205 MTQ_CODE MtQueue::Dequeue(unsigned int *pcb, char **ppb, unsigned int timeoutMilSec, unsigned int nRetries)
207 MTQ_CODE mtqCode = MTQ_NONE;
210 unsigned int cTries = 0;
215 while (cTries < nRetries)
218 ec = pthread_mutex_lock(&m_mutex);
221 // We have the lock, now to add the element to the list
226 pthread_mutex_unlock(&m_mutex);
228 usleep(timeoutMilSec);
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.
235 if (cTries == nRetries)
238 // The queue never had anything in it...
243 // We should have the mutex locked at this point
244 *pcb = m_pbDataList[m_iTail].cb;
245 *ppb = m_pbDataList[m_iTail].pb;
247 m_iTail = ((m_iTail + 1) % m_cElements);
250 pthread_mutex_unlock(&m_mutex);
256 // go from iHead->iTail and simply release everything.
257 // set it back to empty (iHead = iTail)
258 MTQ_CODE MtQueue::Purge()
260 MTQ_CODE mtqCode = MTQ_NONE;
265 ec = pthread_mutex_lock(&m_mutex);
268 // optimization...the queue is already empty
274 free(m_pbDataList[m_iHead].pb);
275 m_pbDataList[m_iHead].cb = 0;
276 m_pbDataList[m_iHead].pb = NULL;
278 m_iHead = ((m_iHead + 1) % m_cElements);
280 } while (m_iHead != m_iTail);
285 printf("MtQueue::Purge: oops...internal state mucked up...should be an empty queue.\n");
289 pthread_mutex_unlock(&m_mutex);
295 bool MtQueue::IsFull()
297 if (((m_iHead + 1) % m_cElements) == m_iTail)
303 bool MtQueue::IsEmpty()
305 return (m_iHead == m_iTail);