diff options
Diffstat (limited to '')
-rw-r--r-- | ThirdParty/SmartThreadPool/WorkItemsQueue.cs | 871 |
1 files changed, 436 insertions, 435 deletions
diff --git a/ThirdParty/SmartThreadPool/WorkItemsQueue.cs b/ThirdParty/SmartThreadPool/WorkItemsQueue.cs index e0bc916..21403a0 100644 --- a/ThirdParty/SmartThreadPool/WorkItemsQueue.cs +++ b/ThirdParty/SmartThreadPool/WorkItemsQueue.cs | |||
@@ -4,29 +4,29 @@ using System.Threading; | |||
4 | 4 | ||
5 | namespace Amib.Threading.Internal | 5 | namespace Amib.Threading.Internal |
6 | { | 6 | { |
7 | #region WorkItemsQueue class | 7 | #region WorkItemsQueue class |
8 | 8 | ||
9 | /// <summary> | 9 | /// <summary> |
10 | /// WorkItemsQueue class. | 10 | /// WorkItemsQueue class. |
11 | /// </summary> | 11 | /// </summary> |
12 | public class WorkItemsQueue : IDisposable | 12 | public class WorkItemsQueue : IDisposable |
13 | { | 13 | { |
14 | #region Member variables | 14 | #region Member variables |
15 | 15 | ||
16 | /// <summary> | 16 | /// <summary> |
17 | /// Waiters queue (implemented as stack). | 17 | /// Waiters queue (implemented as stack). |
18 | /// </summary> | 18 | /// </summary> |
19 | private readonly WaiterEntry _headWaiterEntry = new WaiterEntry(); | 19 | private readonly WaiterEntry _headWaiterEntry = new WaiterEntry(); |
20 | 20 | ||
21 | /// <summary> | 21 | /// <summary> |
22 | /// Waiters count | 22 | /// Waiters count |
23 | /// </summary> | 23 | /// </summary> |
24 | private int _waitersCount = 0; | 24 | private int _waitersCount = 0; |
25 | 25 | ||
26 | /// <summary> | 26 | /// <summary> |
27 | /// Work items queue | 27 | /// Work items queue |
28 | /// </summary> | 28 | /// </summary> |
29 | private readonly PriorityQueue _workItems = new PriorityQueue(); | 29 | private readonly PriorityQueue _workItems = new PriorityQueue(); |
30 | 30 | ||
31 | /// <summary> | 31 | /// <summary> |
32 | /// Indicate that work items are allowed to be queued | 32 | /// Indicate that work items are allowed to be queued |
@@ -34,7 +34,7 @@ namespace Amib.Threading.Internal | |||
34 | private bool _isWorkItemsQueueActive = true; | 34 | private bool _isWorkItemsQueueActive = true; |
35 | 35 | ||
36 | 36 | ||
37 | #if (WINDOWS_PHONE) | 37 | #if (WINDOWS_PHONE) |
38 | private static readonly Dictionary<int, WaiterEntry> _waiterEntries = new Dictionary<int, WaiterEntry>(); | 38 | private static readonly Dictionary<int, WaiterEntry> _waiterEntries = new Dictionary<int, WaiterEntry>(); |
39 | #elif (_WINDOWS_CE) | 39 | #elif (_WINDOWS_CE) |
40 | private static LocalDataStoreSlot _waiterEntrySlot = Thread.AllocateDataSlot(); | 40 | private static LocalDataStoreSlot _waiterEntrySlot = Thread.AllocateDataSlot(); |
@@ -50,7 +50,7 @@ namespace Amib.Threading.Internal | |||
50 | /// </summary> | 50 | /// </summary> |
51 | private static WaiterEntry CurrentWaiterEntry | 51 | private static WaiterEntry CurrentWaiterEntry |
52 | { | 52 | { |
53 | #if (WINDOWS_PHONE) | 53 | #if (WINDOWS_PHONE) |
54 | get | 54 | get |
55 | { | 55 | { |
56 | lock (_waiterEntries) | 56 | lock (_waiterEntries) |
@@ -92,60 +92,60 @@ namespace Amib.Threading.Internal | |||
92 | } | 92 | } |
93 | 93 | ||
94 | /// <summary> | 94 | /// <summary> |
95 | /// A flag that indicates if the WorkItemsQueue has been disposed. | 95 | /// A flag that indicates if the WorkItemsQueue has been disposed. |
96 | /// </summary> | 96 | /// </summary> |
97 | private bool _isDisposed = false; | 97 | private bool _isDisposed = false; |
98 | 98 | ||
99 | #endregion | 99 | #endregion |
100 | 100 | ||
101 | #region Public properties | 101 | #region Public properties |
102 | 102 | ||
103 | /// <summary> | 103 | /// <summary> |
104 | /// Returns the current number of work items in the queue | 104 | /// Returns the current number of work items in the queue |
105 | /// </summary> | 105 | /// </summary> |
106 | public int Count | 106 | public int Count |
107 | { | 107 | { |
108 | get | 108 | get |
109 | { | 109 | { |
110 | return _workItems.Count; | 110 | return _workItems.Count; |
111 | } | 111 | } |
112 | } | 112 | } |
113 | 113 | ||
114 | /// <summary> | 114 | /// <summary> |
115 | /// Returns the current number of waiters | 115 | /// Returns the current number of waiters |
116 | /// </summary> | 116 | /// </summary> |
117 | public int WaitersCount | 117 | public int WaitersCount |
118 | { | 118 | { |
119 | get | 119 | get |
120 | { | 120 | { |
121 | return _waitersCount; | 121 | return _waitersCount; |
122 | } | 122 | } |
123 | } | 123 | } |
124 | 124 | ||
125 | 125 | ||
126 | #endregion | 126 | #endregion |
127 | 127 | ||
128 | #region Public methods | 128 | #region Public methods |
129 | 129 | ||
130 | /// <summary> | 130 | /// <summary> |
131 | /// Enqueue a work item to the queue. | 131 | /// Enqueue a work item to the queue. |
132 | /// </summary> | 132 | /// </summary> |
133 | public bool EnqueueWorkItem(WorkItem workItem) | 133 | public bool EnqueueWorkItem(WorkItem workItem) |
134 | { | 134 | { |
135 | // A work item cannot be null, since null is used in the | 135 | // A work item cannot be null, since null is used in the |
136 | // WaitForWorkItem() method to indicate timeout or cancel | 136 | // WaitForWorkItem() method to indicate timeout or cancel |
137 | if (null == workItem) | 137 | if (null == workItem) |
138 | { | 138 | { |
139 | throw new ArgumentNullException("workItem" , "workItem cannot be null"); | 139 | throw new ArgumentNullException("workItem" , "workItem cannot be null"); |
140 | } | 140 | } |
141 | 141 | ||
142 | bool enqueue = true; | 142 | bool enqueue = true; |
143 | 143 | ||
144 | // First check if there is a waiter waiting for work item. During | 144 | // First check if there is a waiter waiting for work item. During |
145 | // the check, timed out waiters are ignored. If there is no | 145 | // the check, timed out waiters are ignored. If there is no |
146 | // waiter then the work item is queued. | 146 | // waiter then the work item is queued. |
147 | lock(this) | 147 | lock(this) |
148 | { | 148 | { |
149 | ValidateNotDisposed(); | 149 | ValidateNotDisposed(); |
150 | 150 | ||
151 | if (!_isWorkItemsQueueActive) | 151 | if (!_isWorkItemsQueueActive) |
@@ -153,55 +153,55 @@ namespace Amib.Threading.Internal | |||
153 | return false; | 153 | return false; |
154 | } | 154 | } |
155 | 155 | ||
156 | while(_waitersCount > 0) | 156 | while(_waitersCount > 0) |
157 | { | 157 | { |
158 | // Dequeue a waiter. | 158 | // Dequeue a waiter. |
159 | WaiterEntry waiterEntry = PopWaiter(); | 159 | WaiterEntry waiterEntry = PopWaiter(); |
160 | 160 | ||
161 | // Signal the waiter. On success break the loop | 161 | // Signal the waiter. On success break the loop |
162 | if (waiterEntry.Signal(workItem)) | 162 | if (waiterEntry.Signal(workItem)) |
163 | { | 163 | { |
164 | enqueue = false; | 164 | enqueue = false; |
165 | break; | 165 | break; |
166 | } | 166 | } |
167 | } | 167 | } |
168 | 168 | ||
169 | if (enqueue) | 169 | if (enqueue) |
170 | { | 170 | { |
171 | // Enqueue the work item | 171 | // Enqueue the work item |
172 | _workItems.Enqueue(workItem); | 172 | _workItems.Enqueue(workItem); |
173 | } | 173 | } |
174 | } | 174 | } |
175 | return true; | 175 | return true; |
176 | } | 176 | } |
177 | 177 | ||
178 | 178 | ||
179 | /// <summary> | 179 | /// <summary> |
180 | /// Waits for a work item or exits on timeout or cancel | 180 | /// Waits for a work item or exits on timeout or cancel |
181 | /// </summary> | 181 | /// </summary> |
182 | /// <param name="millisecondsTimeout">Timeout in milliseconds</param> | 182 | /// <param name="millisecondsTimeout">Timeout in milliseconds</param> |
183 | /// <param name="cancelEvent">Cancel wait handle</param> | 183 | /// <param name="cancelEvent">Cancel wait handle</param> |
184 | /// <returns>Returns true if the resource was granted</returns> | 184 | /// <returns>Returns true if the resource was granted</returns> |
185 | public WorkItem DequeueWorkItem( | 185 | public WorkItem DequeueWorkItem( |
186 | int millisecondsTimeout, | 186 | int millisecondsTimeout, |
187 | WaitHandle cancelEvent) | 187 | WaitHandle cancelEvent) |
188 | { | 188 | { |
189 | // This method cause the caller to wait for a work item. | 189 | // This method cause the caller to wait for a work item. |
190 | // If there is at least one waiting work item then the | 190 | // If there is at least one waiting work item then the |
191 | // method returns immidiately with it. | 191 | // method returns immidiately with it. |
192 | // | 192 | // |
193 | // If there are no waiting work items then the caller | 193 | // If there are no waiting work items then the caller |
194 | // is queued between other waiters for a work item to arrive. | 194 | // is queued between other waiters for a work item to arrive. |
195 | // | 195 | // |
196 | // If a work item didn't come within millisecondsTimeout or | 196 | // If a work item didn't come within millisecondsTimeout or |
197 | // the user canceled the wait by signaling the cancelEvent | 197 | // the user canceled the wait by signaling the cancelEvent |
198 | // then the method returns null to indicate that the caller | 198 | // then the method returns null to indicate that the caller |
199 | // didn't get a work item. | 199 | // didn't get a work item. |
200 | 200 | ||
201 | WaiterEntry waiterEntry; | 201 | WaiterEntry waiterEntry; |
202 | WorkItem workItem = null; | 202 | WorkItem workItem = null; |
203 | lock (this) | 203 | lock (this) |
204 | { | 204 | { |
205 | ValidateNotDisposed(); | 205 | ValidateNotDisposed(); |
206 | 206 | ||
207 | // If there are waiting work items then take one and return. | 207 | // If there are waiting work items then take one and return. |
@@ -218,68 +218,68 @@ namespace Amib.Threading.Internal | |||
218 | 218 | ||
219 | // Put the waiter with the other waiters | 219 | // Put the waiter with the other waiters |
220 | PushWaiter(waiterEntry); | 220 | PushWaiter(waiterEntry); |
221 | } | 221 | } |
222 | 222 | ||
223 | // Prepare array of wait handle for the WaitHandle.WaitAny() | 223 | // Prepare array of wait handle for the WaitHandle.WaitAny() |
224 | WaitHandle [] waitHandles = new WaitHandle[] { | 224 | WaitHandle [] waitHandles = new WaitHandle[] { |
225 | waiterEntry.WaitHandle, | 225 | waiterEntry.WaitHandle, |
226 | cancelEvent }; | 226 | cancelEvent }; |
227 | 227 | ||
228 | // Wait for an available resource, cancel event, or timeout. | 228 | // Wait for an available resource, cancel event, or timeout. |
229 | 229 | ||
230 | // During the wait we are supposes to exit the synchronization | 230 | // During the wait we are supposes to exit the synchronization |
231 | // domain. (Placing true as the third argument of the WaitAny()) | 231 | // domain. (Placing true as the third argument of the WaitAny()) |
232 | // It just doesn't work, I don't know why, so I have two lock(this) | 232 | // It just doesn't work, I don't know why, so I have two lock(this) |
233 | // statments instead of one. | 233 | // statments instead of one. |
234 | 234 | ||
235 | int index = STPEventWaitHandle.WaitAny( | 235 | int index = STPEventWaitHandle.WaitAny( |
236 | waitHandles, | 236 | waitHandles, |
237 | millisecondsTimeout, | 237 | millisecondsTimeout, |
238 | true); | 238 | true); |
239 | 239 | ||
240 | lock(this) | 240 | lock(this) |
241 | { | 241 | { |
242 | // success is true if it got a work item. | 242 | // success is true if it got a work item. |
243 | bool success = (0 == index); | 243 | bool success = (0 == index); |
244 | 244 | ||
245 | // The timeout variable is used only for readability. | 245 | // The timeout variable is used only for readability. |
246 | // (We treat cancel as timeout) | 246 | // (We treat cancel as timeout) |
247 | bool timeout = !success; | 247 | bool timeout = !success; |
248 | 248 | ||
249 | // On timeout update the waiterEntry that it is timed out | 249 | // On timeout update the waiterEntry that it is timed out |
250 | if (timeout) | 250 | if (timeout) |
251 | { | 251 | { |
252 | // The Timeout() fails if the waiter has already been signaled | 252 | // The Timeout() fails if the waiter has already been signaled |
253 | timeout = waiterEntry.Timeout(); | 253 | timeout = waiterEntry.Timeout(); |
254 | 254 | ||
255 | // On timeout remove the waiter from the queue. | 255 | // On timeout remove the waiter from the queue. |
256 | // Note that the complexity is O(1). | 256 | // Note that the complexity is O(1). |
257 | if(timeout) | 257 | if(timeout) |
258 | { | 258 | { |
259 | RemoveWaiter(waiterEntry, false); | 259 | RemoveWaiter(waiterEntry, false); |
260 | } | 260 | } |
261 | 261 | ||
262 | // Again readability | 262 | // Again readability |
263 | success = !timeout; | 263 | success = !timeout; |
264 | } | 264 | } |
265 | 265 | ||
266 | // On success return the work item | 266 | // On success return the work item |
267 | if (success) | 267 | if (success) |
268 | { | 268 | { |
269 | workItem = waiterEntry.WorkItem; | 269 | workItem = waiterEntry.WorkItem; |
270 | 270 | ||
271 | if (null == workItem) | 271 | if (null == workItem) |
272 | { | 272 | { |
273 | workItem = _workItems.Dequeue() as WorkItem; | 273 | workItem = _workItems.Dequeue() as WorkItem; |
274 | } | 274 | } |
275 | } | 275 | } |
276 | } | 276 | } |
277 | // On failure return null. | 277 | // On failure return null. |
278 | return workItem; | 278 | return workItem; |
279 | } | 279 | } |
280 | 280 | ||
281 | /// <summary> | 281 | /// <summary> |
282 | /// Cleanup the work items queue, hence no more work | 282 | /// Cleanup the work items queue, hence no more work |
283 | /// items are allowed to be queue | 283 | /// items are allowed to be queue |
284 | /// </summary> | 284 | /// </summary> |
285 | private void Cleanup() | 285 | private void Cleanup() |
@@ -303,19 +303,19 @@ namespace Amib.Threading.Internal | |||
303 | // Clear the work items that are already queued | 303 | // Clear the work items that are already queued |
304 | _workItems.Clear(); | 304 | _workItems.Clear(); |
305 | 305 | ||
306 | // Note: | 306 | // Note: |
307 | // I don't iterate over the queue and dispose of work items's states, | 307 | // I don't iterate over the queue and dispose of work items's states, |
308 | // since if a work item has a state object that is still in use in the | 308 | // since if a work item has a state object that is still in use in the |
309 | // application then I must not dispose it. | 309 | // application then I must not dispose it. |
310 | 310 | ||
311 | // Tell the waiters that they were timed out. | 311 | // Tell the waiters that they were timed out. |
312 | // It won't signal them to exit, but to ignore their | 312 | // It won't signal them to exit, but to ignore their |
313 | // next work item. | 313 | // next work item. |
314 | while(_waitersCount > 0) | 314 | while(_waitersCount > 0) |
315 | { | 315 | { |
316 | WaiterEntry waiterEntry = PopWaiter(); | 316 | WaiterEntry waiterEntry = PopWaiter(); |
317 | waiterEntry.Timeout(); | 317 | waiterEntry.Timeout(); |
318 | } | 318 | } |
319 | } | 319 | } |
320 | } | 320 | } |
321 | 321 | ||
@@ -334,275 +334,275 @@ namespace Amib.Threading.Internal | |||
334 | } | 334 | } |
335 | } | 335 | } |
336 | 336 | ||
337 | #endregion | 337 | #endregion |
338 | 338 | ||
339 | #region Private methods | 339 | #region Private methods |
340 | 340 | ||
341 | /// <summary> | 341 | /// <summary> |
342 | /// Returns the WaiterEntry of the current thread | 342 | /// Returns the WaiterEntry of the current thread |
343 | /// </summary> | 343 | /// </summary> |
344 | /// <returns></returns> | 344 | /// <returns></returns> |
345 | /// In order to avoid creation and destuction of WaiterEntry | 345 | /// In order to avoid creation and destuction of WaiterEntry |
346 | /// objects each thread has its own WaiterEntry object. | 346 | /// objects each thread has its own WaiterEntry object. |
347 | private static WaiterEntry GetThreadWaiterEntry() | 347 | private static WaiterEntry GetThreadWaiterEntry() |
348 | { | 348 | { |
349 | if (null == CurrentWaiterEntry) | 349 | if (null == CurrentWaiterEntry) |
350 | { | 350 | { |
351 | CurrentWaiterEntry = new WaiterEntry(); | 351 | CurrentWaiterEntry = new WaiterEntry(); |
352 | } | 352 | } |
353 | CurrentWaiterEntry.Reset(); | 353 | CurrentWaiterEntry.Reset(); |
354 | return CurrentWaiterEntry; | 354 | return CurrentWaiterEntry; |
355 | } | 355 | } |
356 | 356 | ||
357 | #region Waiters stack methods | 357 | #region Waiters stack methods |
358 | 358 | ||
359 | /// <summary> | 359 | /// <summary> |
360 | /// Push a new waiter into the waiter's stack | 360 | /// Push a new waiter into the waiter's stack |
361 | /// </summary> | 361 | /// </summary> |
362 | /// <param name="newWaiterEntry">A waiter to put in the stack</param> | 362 | /// <param name="newWaiterEntry">A waiter to put in the stack</param> |
363 | public void PushWaiter(WaiterEntry newWaiterEntry) | 363 | public void PushWaiter(WaiterEntry newWaiterEntry) |
364 | { | 364 | { |
365 | // Remove the waiter if it is already in the stack and | 365 | // Remove the waiter if it is already in the stack and |
366 | // update waiter's count as needed | 366 | // update waiter's count as needed |
367 | RemoveWaiter(newWaiterEntry, false); | 367 | RemoveWaiter(newWaiterEntry, false); |
368 | 368 | ||
369 | // If the stack is empty then newWaiterEntry is the new head of the stack | 369 | // If the stack is empty then newWaiterEntry is the new head of the stack |
370 | if (null == _headWaiterEntry._nextWaiterEntry) | 370 | if (null == _headWaiterEntry._nextWaiterEntry) |
371 | { | 371 | { |
372 | _headWaiterEntry._nextWaiterEntry = newWaiterEntry; | 372 | _headWaiterEntry._nextWaiterEntry = newWaiterEntry; |
373 | newWaiterEntry._prevWaiterEntry = _headWaiterEntry; | 373 | newWaiterEntry._prevWaiterEntry = _headWaiterEntry; |
374 | 374 | ||
375 | } | 375 | } |
376 | // If the stack is not empty then put newWaiterEntry as the new head | 376 | // If the stack is not empty then put newWaiterEntry as the new head |
377 | // of the stack. | 377 | // of the stack. |
378 | else | 378 | else |
379 | { | 379 | { |
380 | // Save the old first waiter entry | 380 | // Save the old first waiter entry |
381 | WaiterEntry oldFirstWaiterEntry = _headWaiterEntry._nextWaiterEntry; | 381 | WaiterEntry oldFirstWaiterEntry = _headWaiterEntry._nextWaiterEntry; |
382 | 382 | ||
383 | // Update the links | 383 | // Update the links |
384 | _headWaiterEntry._nextWaiterEntry = newWaiterEntry; | 384 | _headWaiterEntry._nextWaiterEntry = newWaiterEntry; |
385 | newWaiterEntry._nextWaiterEntry = oldFirstWaiterEntry; | 385 | newWaiterEntry._nextWaiterEntry = oldFirstWaiterEntry; |
386 | newWaiterEntry._prevWaiterEntry = _headWaiterEntry; | 386 | newWaiterEntry._prevWaiterEntry = _headWaiterEntry; |
387 | oldFirstWaiterEntry._prevWaiterEntry = newWaiterEntry; | 387 | oldFirstWaiterEntry._prevWaiterEntry = newWaiterEntry; |
388 | } | 388 | } |
389 | 389 | ||
390 | // Increment the number of waiters | 390 | // Increment the number of waiters |
391 | ++_waitersCount; | 391 | ++_waitersCount; |
392 | } | 392 | } |
393 | 393 | ||
394 | /// <summary> | 394 | /// <summary> |
395 | /// Pop a waiter from the waiter's stack | 395 | /// Pop a waiter from the waiter's stack |
396 | /// </summary> | 396 | /// </summary> |
397 | /// <returns>Returns the first waiter in the stack</returns> | 397 | /// <returns>Returns the first waiter in the stack</returns> |
398 | private WaiterEntry PopWaiter() | 398 | private WaiterEntry PopWaiter() |
399 | { | 399 | { |
400 | // Store the current stack head | 400 | // Store the current stack head |
401 | WaiterEntry oldFirstWaiterEntry = _headWaiterEntry._nextWaiterEntry; | 401 | WaiterEntry oldFirstWaiterEntry = _headWaiterEntry._nextWaiterEntry; |
402 | 402 | ||
403 | // Store the new stack head | 403 | // Store the new stack head |
404 | WaiterEntry newHeadWaiterEntry = oldFirstWaiterEntry._nextWaiterEntry; | 404 | WaiterEntry newHeadWaiterEntry = oldFirstWaiterEntry._nextWaiterEntry; |
405 | 405 | ||
406 | // Update the old stack head list links and decrement the number | 406 | // Update the old stack head list links and decrement the number |
407 | // waiters. | 407 | // waiters. |
408 | RemoveWaiter(oldFirstWaiterEntry, true); | 408 | RemoveWaiter(oldFirstWaiterEntry, true); |
409 | 409 | ||
410 | // Update the new stack head | 410 | // Update the new stack head |
411 | _headWaiterEntry._nextWaiterEntry = newHeadWaiterEntry; | 411 | _headWaiterEntry._nextWaiterEntry = newHeadWaiterEntry; |
412 | if (null != newHeadWaiterEntry) | 412 | if (null != newHeadWaiterEntry) |
413 | { | 413 | { |
414 | newHeadWaiterEntry._prevWaiterEntry = _headWaiterEntry; | 414 | newHeadWaiterEntry._prevWaiterEntry = _headWaiterEntry; |
415 | } | 415 | } |
416 | 416 | ||
417 | // Return the old stack head | 417 | // Return the old stack head |
418 | return oldFirstWaiterEntry; | 418 | return oldFirstWaiterEntry; |
419 | } | 419 | } |
420 | 420 | ||
421 | /// <summary> | 421 | /// <summary> |
422 | /// Remove a waiter from the stack | 422 | /// Remove a waiter from the stack |
423 | /// </summary> | 423 | /// </summary> |
424 | /// <param name="waiterEntry">A waiter entry to remove</param> | 424 | /// <param name="waiterEntry">A waiter entry to remove</param> |
425 | /// <param name="popDecrement">If true the waiter count is always decremented</param> | 425 | /// <param name="popDecrement">If true the waiter count is always decremented</param> |
426 | private void RemoveWaiter(WaiterEntry waiterEntry, bool popDecrement) | 426 | private void RemoveWaiter(WaiterEntry waiterEntry, bool popDecrement) |
427 | { | 427 | { |
428 | // Store the prev entry in the list | 428 | // Store the prev entry in the list |
429 | WaiterEntry prevWaiterEntry = waiterEntry._prevWaiterEntry; | 429 | WaiterEntry prevWaiterEntry = waiterEntry._prevWaiterEntry; |
430 | 430 | ||
431 | // Store the next entry in the list | 431 | // Store the next entry in the list |
432 | WaiterEntry nextWaiterEntry = waiterEntry._nextWaiterEntry; | 432 | WaiterEntry nextWaiterEntry = waiterEntry._nextWaiterEntry; |
433 | 433 | ||
434 | // A flag to indicate if we need to decrement the waiters count. | 434 | // A flag to indicate if we need to decrement the waiters count. |
435 | // If we got here from PopWaiter then we must decrement. | 435 | // If we got here from PopWaiter then we must decrement. |
436 | // If we got here from PushWaiter then we decrement only if | 436 | // If we got here from PushWaiter then we decrement only if |
437 | // the waiter was already in the stack. | 437 | // the waiter was already in the stack. |
438 | bool decrementCounter = popDecrement; | 438 | bool decrementCounter = popDecrement; |
439 | 439 | ||
440 | // Null the waiter's entry links | 440 | // Null the waiter's entry links |
441 | waiterEntry._prevWaiterEntry = null; | 441 | waiterEntry._prevWaiterEntry = null; |
442 | waiterEntry._nextWaiterEntry = null; | 442 | waiterEntry._nextWaiterEntry = null; |
443 | 443 | ||
444 | // If the waiter entry had a prev link then update it. | 444 | // If the waiter entry had a prev link then update it. |
445 | // It also means that the waiter is already in the list and we | 445 | // It also means that the waiter is already in the list and we |
446 | // need to decrement the waiters count. | 446 | // need to decrement the waiters count. |
447 | if (null != prevWaiterEntry) | 447 | if (null != prevWaiterEntry) |
448 | { | 448 | { |
449 | prevWaiterEntry._nextWaiterEntry = nextWaiterEntry; | 449 | prevWaiterEntry._nextWaiterEntry = nextWaiterEntry; |
450 | decrementCounter = true; | 450 | decrementCounter = true; |
451 | } | 451 | } |
452 | 452 | ||
453 | // If the waiter entry had a next link then update it. | 453 | // If the waiter entry had a next link then update it. |
454 | // It also means that the waiter is already in the list and we | 454 | // It also means that the waiter is already in the list and we |
455 | // need to decrement the waiters count. | 455 | // need to decrement the waiters count. |
456 | if (null != nextWaiterEntry) | 456 | if (null != nextWaiterEntry) |
457 | { | 457 | { |
458 | nextWaiterEntry._prevWaiterEntry = prevWaiterEntry; | 458 | nextWaiterEntry._prevWaiterEntry = prevWaiterEntry; |
459 | decrementCounter = true; | 459 | decrementCounter = true; |
460 | } | 460 | } |
461 | 461 | ||
462 | // Decrement the waiters count if needed | 462 | // Decrement the waiters count if needed |
463 | if (decrementCounter) | 463 | if (decrementCounter) |
464 | { | 464 | { |
465 | --_waitersCount; | 465 | --_waitersCount; |
466 | } | 466 | } |
467 | } | 467 | } |
468 | 468 | ||
469 | #endregion | 469 | #endregion |
470 | 470 | ||
471 | #endregion | 471 | #endregion |
472 | 472 | ||
473 | #region WaiterEntry class | 473 | #region WaiterEntry class |
474 | 474 | ||
475 | // A waiter entry in the _waiters queue. | 475 | // A waiter entry in the _waiters queue. |
476 | public sealed class WaiterEntry : IDisposable | 476 | public sealed class WaiterEntry : IDisposable |
477 | { | 477 | { |
478 | #region Member variables | 478 | #region Member variables |
479 | 479 | ||
480 | /// <summary> | 480 | /// <summary> |
481 | /// Event to signal the waiter that it got the work item. | 481 | /// Event to signal the waiter that it got the work item. |
482 | /// </summary> | 482 | /// </summary> |
483 | //private AutoResetEvent _waitHandle = new AutoResetEvent(false); | 483 | //private AutoResetEvent _waitHandle = new AutoResetEvent(false); |
484 | private AutoResetEvent _waitHandle = EventWaitHandleFactory.CreateAutoResetEvent(); | 484 | private AutoResetEvent _waitHandle = EventWaitHandleFactory.CreateAutoResetEvent(); |
485 | 485 | ||
486 | /// <summary> | 486 | /// <summary> |
487 | /// Flag to know if this waiter already quited from the queue | 487 | /// Flag to know if this waiter already quited from the queue |
488 | /// because of a timeout. | 488 | /// because of a timeout. |
489 | /// </summary> | 489 | /// </summary> |
490 | private bool _isTimedout = false; | 490 | private bool _isTimedout = false; |
491 | 491 | ||
492 | /// <summary> | 492 | /// <summary> |
493 | /// Flag to know if the waiter was signaled and got a work item. | 493 | /// Flag to know if the waiter was signaled and got a work item. |
494 | /// </summary> | 494 | /// </summary> |
495 | private bool _isSignaled = false; | 495 | private bool _isSignaled = false; |
496 | 496 | ||
497 | /// <summary> | 497 | /// <summary> |
498 | /// A work item that passed directly to the waiter withou going | 498 | /// A work item that passed directly to the waiter withou going |
499 | /// through the queue | 499 | /// through the queue |
500 | /// </summary> | 500 | /// </summary> |
501 | private WorkItem _workItem = null; | 501 | private WorkItem _workItem = null; |
502 | 502 | ||
503 | private bool _isDisposed = false; | 503 | private bool _isDisposed = false; |
504 | 504 | ||
505 | // Linked list members | 505 | // Linked list members |
506 | internal WaiterEntry _nextWaiterEntry = null; | 506 | internal WaiterEntry _nextWaiterEntry = null; |
507 | internal WaiterEntry _prevWaiterEntry = null; | 507 | internal WaiterEntry _prevWaiterEntry = null; |
508 | 508 | ||
509 | #endregion | 509 | #endregion |
510 | 510 | ||
511 | #region Construction | 511 | #region Construction |
512 | 512 | ||
513 | public WaiterEntry() | 513 | public WaiterEntry() |
514 | { | 514 | { |
515 | Reset(); | 515 | Reset(); |
516 | } | 516 | } |
517 | 517 | ||
518 | #endregion | 518 | #endregion |
519 | 519 | ||
520 | #region Public methods | 520 | #region Public methods |
521 | 521 | ||
522 | public WaitHandle WaitHandle | 522 | public WaitHandle WaitHandle |
523 | { | 523 | { |
524 | get { return _waitHandle; } | 524 | get { return _waitHandle; } |
525 | } | 525 | } |
526 | 526 | ||
527 | public WorkItem WorkItem | 527 | public WorkItem WorkItem |
528 | { | 528 | { |
529 | get | 529 | get |
530 | { | 530 | { |
531 | return _workItem; | 531 | return _workItem; |
532 | } | 532 | } |
533 | } | 533 | } |
534 | 534 | ||
535 | /// <summary> | 535 | /// <summary> |
536 | /// Signal the waiter that it got a work item. | 536 | /// Signal the waiter that it got a work item. |
537 | /// </summary> | 537 | /// </summary> |
538 | /// <returns>Return true on success</returns> | 538 | /// <returns>Return true on success</returns> |
539 | /// The method fails if Timeout() preceded its call | 539 | /// The method fails if Timeout() preceded its call |
540 | public bool Signal(WorkItem workItem) | 540 | public bool Signal(WorkItem workItem) |
541 | { | 541 | { |
542 | lock(this) | 542 | lock(this) |
543 | { | 543 | { |
544 | if (!_isTimedout) | 544 | if (!_isTimedout) |
545 | { | 545 | { |
546 | _workItem = workItem; | 546 | _workItem = workItem; |
547 | _isSignaled = true; | 547 | _isSignaled = true; |
548 | _waitHandle.Set(); | 548 | _waitHandle.Set(); |
549 | return true; | 549 | return true; |
550 | } | 550 | } |
551 | } | 551 | } |
552 | return false; | 552 | return false; |
553 | } | 553 | } |
554 | 554 | ||
555 | /// <summary> | 555 | /// <summary> |
556 | /// Mark the wait entry that it has been timed out | 556 | /// Mark the wait entry that it has been timed out |
557 | /// </summary> | 557 | /// </summary> |
558 | /// <returns>Return true on success</returns> | 558 | /// <returns>Return true on success</returns> |
559 | /// The method fails if Signal() preceded its call | 559 | /// The method fails if Signal() preceded its call |
560 | public bool Timeout() | 560 | public bool Timeout() |
561 | { | 561 | { |
562 | lock(this) | 562 | lock(this) |
563 | { | 563 | { |
564 | // Time out can happen only if the waiter wasn't marked as | 564 | // Time out can happen only if the waiter wasn't marked as |
565 | // signaled | 565 | // signaled |
566 | if (!_isSignaled) | 566 | if (!_isSignaled) |
567 | { | 567 | { |
568 | // We don't remove the waiter from the queue, the DequeueWorkItem | 568 | // We don't remove the waiter from the queue, the DequeueWorkItem |
569 | // method skips _waiters that were timed out. | 569 | // method skips _waiters that were timed out. |
570 | _isTimedout = true; | 570 | _isTimedout = true; |
571 | return true; | 571 | return true; |
572 | } | 572 | } |
573 | } | 573 | } |
574 | return false; | 574 | return false; |
575 | } | 575 | } |
576 | 576 | ||
577 | /// <summary> | 577 | /// <summary> |
578 | /// Reset the wait entry so it can be used again | 578 | /// Reset the wait entry so it can be used again |
579 | /// </summary> | 579 | /// </summary> |
580 | public void Reset() | 580 | public void Reset() |
581 | { | 581 | { |
582 | _workItem = null; | 582 | _workItem = null; |
583 | _isTimedout = false; | 583 | _isTimedout = false; |
584 | _isSignaled = false; | 584 | _isSignaled = false; |
585 | _waitHandle.Reset(); | 585 | _waitHandle.Reset(); |
586 | } | 586 | } |
587 | 587 | ||
588 | /// <summary> | 588 | /// <summary> |
589 | /// Free resources | 589 | /// Free resources |
590 | /// </summary> | 590 | /// </summary> |
591 | public void Close() | 591 | public void Close() |
592 | { | 592 | { |
593 | if (null != _waitHandle) | 593 | if (null != _waitHandle) |
594 | { | 594 | { |
595 | _waitHandle.Close(); | 595 | _waitHandle.Close(); |
596 | _waitHandle = null; | 596 | _waitHandle = null; |
597 | } | 597 | } |
598 | } | 598 | } |
599 | 599 | ||
600 | #endregion | 600 | #endregion |
601 | 601 | ||
602 | #region IDisposable Members | 602 | #region IDisposable Members |
603 | 603 | ||
604 | public void Dispose() | 604 | public void Dispose() |
605 | { | 605 | { |
606 | lock (this) | 606 | lock (this) |
607 | { | 607 | { |
608 | if (!_isDisposed) | 608 | if (!_isDisposed) |
@@ -613,10 +613,10 @@ namespace Amib.Threading.Internal | |||
613 | } | 613 | } |
614 | } | 614 | } |
615 | 615 | ||
616 | #endregion | 616 | #endregion |
617 | } | 617 | } |
618 | 618 | ||
619 | #endregion | 619 | #endregion |
620 | 620 | ||
621 | #region IDisposable Members | 621 | #region IDisposable Members |
622 | 622 | ||
@@ -625,6 +625,7 @@ namespace Amib.Threading.Internal | |||
625 | if (!_isDisposed) | 625 | if (!_isDisposed) |
626 | { | 626 | { |
627 | Cleanup(); | 627 | Cleanup(); |
628 | _headWaiterEntry.Close(); | ||
628 | } | 629 | } |
629 | _isDisposed = true; | 630 | _isDisposed = true; |
630 | } | 631 | } |
@@ -640,6 +641,6 @@ namespace Amib.Threading.Internal | |||
640 | #endregion | 641 | #endregion |
641 | } | 642 | } |
642 | 643 | ||
643 | #endregion | 644 | #endregion |
644 | } | 645 | } |
645 | 646 | ||