Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

373 lines
11 KiB

  1. //
  2. // LOCKQ.H
  3. //
  4. // This file contains classes which define a queue mechanism which will
  5. // safely synchronize additions and removals from the queue, where every
  6. // thread which appends to the Queue must be prepared to deal with owning the
  7. // queue. Additionally, elements will come off the queue in the same order
  8. // that they are appended.
  9. //
  10. // The structure of a thread using this stuff should be the following :
  11. //
  12. // class CUsefull : public CQElement { } ;
  13. // template CLockQueue< CUsefull > UsefullQ ;
  14. //
  15. //
  16. // if( UsefullQ.Append( pUsefull ) ) {
  17. //
  18. // while( UsefullQ.GetHead( &pUsefullWorkItem ) ) {
  19. // /* Do some usefull work. */
  20. //
  21. // UsefullQ.Remove() ;
  22. // }
  23. // }
  24. //
  25. // Implementation Schedule for all classes defined in this file :
  26. // 1 day
  27. //
  28. // Unit Test schedule for all classes defined in this file :
  29. // 1 day
  30. // Unit Testing should consist of a multi theaded appli
  31. //
  32. //
  33. #ifndef _LOCKQ_H_
  34. #define _LOCKQ_H_
  35. #include "qbase.h"
  36. #ifdef DEBUG
  37. #define LOCKQ_DEBUG
  38. #endif
  39. #ifdef _DEBUG
  40. #ifndef LOCKQ_DEBUG
  41. #define LOCKQ_DEBUG
  42. #endif
  43. #endif
  44. //------------------------------------------------
  45. class CQueueLockV1 {
  46. //
  47. // This class implements a queue which is multi-threaded safe for append operations.
  48. // In addition, this queue will synchronize removal of elements amongst those
  49. // threads which are appending to the queue. Each thread that appends to the
  50. // queue potentially becomes the owner of the removal lock of the queue.
  51. // (If Append() returns TRUE the thread owns the removal lock of the queue.)
  52. // There is no way to relinquish the removal lock except to empty the queue.
  53. // Emptying the queue must be done as a series of GetHead(), Remove() calls.
  54. // A GetHead() call MUST preceed each Remove() call.
  55. //
  56. // Each time the thread calls GetHead, it will be
  57. // told whether it still owns the removallock (when the queue is emptied the lock is
  58. // relinquished.)
  59. // Owning the removal lock in no way interferes with other threads appending to the queue.
  60. //
  61. // The class uses InterlockedExchange to handle all synchronization issues.
  62. //
  63. // For appending - InterlockedExchange does the following :
  64. // Exchange the tail pointer with what we want to be the new tail.
  65. // Make the old tail that we got from the exchange point to the new element.
  66. // The list is now intact. Because there is an extra element in the queue
  67. // (m_special) the tail pointer never becomes NULL.
  68. //
  69. // For synchronization safety -
  70. // InterlockedExchange is used on the element next pointers in order to
  71. // determine what thread has the removal lock.
  72. // Each call to GetHead Exchanges the head's next pointer with LOCKVAL.
  73. // Each Append call will also Exchange the old tail elements next value
  74. // with the new m_pTail value. So there are only two threads which can
  75. // be exchanging the next pointer at any given instant.
  76. //
  77. // The remove thread either gets 0 or a NON-NULL pointer after its Exchange.
  78. //
  79. // If it gets Zero, it knows it Exchanged before the Appending thread,
  80. // in which case it loses the lock (since it can't safely do anything.)
  81. // If it gets a Non-Zero value, the other thread exchanged first.
  82. // In this case, the remover still has the lock and repairs the list.
  83. //
  84. // In the case of the appending thread - after its Exchange it either gets
  85. // zero or LOCKVAL.
  86. //
  87. // If the appending thread gets zero, it Exchanged first so the other thread
  88. // should hold onto the lock. If the appending thread gets LOCKVAL then it
  89. // owns the lock.
  90. //
  91. // Finally, note that there is ALWAYS AT LEAST ONE ELEMENT in the Queue (m_special).
  92. // This means that if there is anything of value in the queue at all then
  93. // there must be at least TWO elements in the queue (and m_pHead->p is non null).
  94. //
  95. //
  96. // Usage should be the following in each worker thread :
  97. // CQueueLockV1 *pQueue ;
  98. //
  99. // if( pQueue->Append( p ) ) {
  100. // CQueueElement *pWork ;
  101. //
  102. // while( pQueue->GetHead( pWork ) ) {
  103. // /* Do work on pWork It is safe to do further calls to pQueue->Append()
  104. // while doing whatever work. That does not mean those Appended
  105. // elements will be processed on this thread. */
  106. //
  107. // pQueue->Remove() ;
  108. // }
  109. // }
  110. //
  111. private :
  112. enum CONSTANTS {
  113. LOCKVAL = -1,
  114. } ;
  115. CQElement m_special ; // element used ensure that the Queue always contains
  116. // something.
  117. CQElement m_release ; // element used with ReleaseLock calls to give up the
  118. // RemovalLock on the Queue.
  119. // This pointer is set after a call to ReleaseLock() - and
  120. // will pointer to the queue element before the m_release element in
  121. // the queue.
  122. CQElement *m_pHead ;
  123. CQElement *m_pTail ;
  124. #ifdef LOCKQ_DEBUG
  125. DWORD m_dwOwningThread ;
  126. LONG m_lock ;
  127. #endif
  128. BOOL OfferOwnership( CQElement* p ) ;
  129. CQElement* GetFront( ) ;
  130. public :
  131. CQueueLockV1( ) ;
  132. ~CQueueLockV1( ) ;
  133. //
  134. // Append - returns TRUE if we have added the first element into the queue
  135. // and we now own the lock.
  136. //
  137. BOOL Append( CQElement *p ) ;
  138. //
  139. // Remove - takes the head element off the list. The head element should be
  140. // examined with GetHead(). As long as we are calling GetHead() at least once
  141. // before each call to Remove(), the synchronization aspects of the queue will
  142. // work.
  143. //
  144. void Remove( ) ;
  145. //
  146. // GetHead - returns TRUE as long as there is an element to be had at the front
  147. // of the queue. The element pointer is returned through the reference to a pointer.
  148. //
  149. BOOL GetHead( CQElement *&p ) ;
  150. //
  151. // RemoveRelease - takes the head element off the list.
  152. // Also offers the removal lock to any other thread out there.
  153. // If the function returns TRUE then this thread still has the lock,
  154. // otherwise another thread has it.
  155. //
  156. BOOL RemoveAndRelease( ) ;
  157. } ;
  158. #ifndef _NO_TEMPLATES_
  159. //--------------------------------------------------
  160. template< class Element >
  161. class TLockQueueV1 {
  162. //
  163. // This template is designed for use with Elements derived from CQElement.
  164. // This will use the CQueueLockV1 class above to implement a locking queue containing
  165. // elements of type 'Element'.
  166. //
  167. private :
  168. CQueueLockV1 m_queue ;
  169. public :
  170. inline TLockQueueV1() ;
  171. inline ~TLockQueueV1() ;
  172. // Add an element to the Queue - if this returns TRUE we own the lock.
  173. inline BOOL Append( Element* p ) ;
  174. // remove an element from the Queue - lock ownership does not change.
  175. inline void Remove( ) ;
  176. inline BOOL GetHead( Element *&p ) ;
  177. inline BOOL RemoveAndRelease( ) ;
  178. } ;
  179. #endif
  180. #define DECLARE_LOCKQ( Type ) \
  181. class LockQ ## Type { \
  182. private : \
  183. CQueueLockV1 m_queue ; \
  184. public : \
  185. LockQ ## Type () {} \
  186. ~LockQ ## Type () {} \
  187. BOOL Append( Type * p ) { return m_queue.Append( (CQElement*)p ) ; } \
  188. void Remove( ) { m_queue.Remove() ; } \
  189. BOOL GetHead( Type *&p ) { \
  190. CQElement* pTemp = 0; \
  191. BOOL fTemp = m_queue.GetHead( pTemp ) ; \
  192. p = (Type*)pTemp ; \
  193. return fTemp ; \
  194. } \
  195. BOOL RemoveAndRelease( ) { \
  196. return m_queue.RemoveAndRelease() ; \
  197. } \
  198. } ;
  199. #define INVOKE_LOCKQ( Type ) LockQ ## Type
  200. class CQueueLock {
  201. //
  202. // This class implements a queue which is multi-threaded safe for append operations.
  203. // In addition, this queue will synchronize removal of elements amongst those
  204. // threads which are appending to the queue. Each thread that appends to the
  205. // queue potentially becomes the owner of the removal lock of the queue.
  206. // (If Append() returns TRUE the thread owns the removal lock of the queue.)
  207. // There is no way to relinquish the removal lock except to empty the queue.
  208. //
  209. // Usage should be the following in each worker thread :
  210. // CQueueLock *pQueue ;
  211. //
  212. // if( pQueue->Append( p ) ) {
  213. // CQueueElement *pWork ;
  214. //
  215. // while( (pWork = pQueue->RemoveAndRelease( )) ) {
  216. // /* Do work on pWork It is safe to do further calls to pQueue->Append()
  217. // while doing whatever work. That does not mean those Appended
  218. // elements will be processed on this thread. */
  219. // }
  220. // }
  221. //
  222. private :
  223. //
  224. // Class constants - LOCKVAL that special value marking the queue element
  225. // which is ready to be grabbed !
  226. //
  227. enum CONSTANTS {
  228. LOCKVAL = -1,
  229. } ;
  230. //
  231. // Element which always remains in the list !
  232. //
  233. CQElement m_special ;
  234. //
  235. // Head of the list
  236. //
  237. CQElement *m_pHead ;
  238. //
  239. // Tail of the list !
  240. //
  241. CQElement *m_pTail ;
  242. public :
  243. //
  244. // Initialize the queue to an empty state -
  245. // and also sets things up so that the first Append
  246. // will own the lock !
  247. //
  248. inline CQueueLock( ) ;
  249. //
  250. // Also initializes to an empty state, however
  251. // allows caller to specify whether queue can be
  252. // grabbed on the first Append !
  253. //
  254. inline CQueueLock( BOOL fSet ) ;
  255. #ifdef LOCKQ_DEBUG
  256. //
  257. // Check that the queue is emptied when it is destroyed !
  258. //
  259. inline ~CQueueLock( ) ;
  260. #endif
  261. //
  262. // Set the lock to the lockable state where the next
  263. // call to Append() will acquire the lock !
  264. // This function is not thread safe and should only
  265. // be called when we are sure there is only one thread
  266. // using the object !
  267. //
  268. inline void Reset() ;
  269. //
  270. // Append - returns TRUE if we have added the first element into the queue
  271. // and we now own the lock.
  272. //
  273. inline BOOL Append( CQElement *p ) ;
  274. //
  275. // return the head of the Queue - if we return NULL then some other thread
  276. // may own the lcok the queue implicitly implies !
  277. //
  278. inline CQElement* RemoveAndRelease( ) ;
  279. //
  280. // return the head of the Queue - but don't let any other threads
  281. // grab the queue !
  282. //
  283. inline CQElement* Remove( ) ;
  284. } ;
  285. template< class Element >
  286. class TLockQueue {
  287. //
  288. // This template is designed for use with Elements derived from CQElement.
  289. // This will use the CQueueLock class above to implement a locking queue containing
  290. // elements of type 'Element'.
  291. //
  292. private :
  293. CQueueLock m_queue ;
  294. public :
  295. //
  296. // Create an empty queue
  297. //
  298. inline TLockQueue() ;
  299. //
  300. // Create empty queue and specify whether the
  301. // lock is initially available
  302. //
  303. inline TLockQueue( BOOL fSet ) ;
  304. //
  305. // Caller must already have lock and be only thread
  306. // using object - make the lock available !
  307. //
  308. inline void Reset() ;
  309. //
  310. // Add an element to the Queue - if this returns TRUE we own the lock.
  311. //
  312. inline BOOL Append( Element* p ) ;
  313. //
  314. // remove an element from the Queue - lock ownership does not change unless this
  315. // returns NULL !
  316. //
  317. inline Element* RemoveAndRelease( ) ;
  318. //
  319. // remove an element from the Queue - but don't relinquish lock !
  320. //
  321. inline Element* Remove() ;
  322. } ;
  323. #include "lockq.inl"
  324. #endif // _LOCKQ_H_