aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/ThirdParty/SmartThreadPool/WorkItemsQueue.cs
diff options
context:
space:
mode:
authorJustin Clark-Casey (justincc)2013-05-01 19:01:43 +0100
committerJustin Clark-Casey (justincc)2013-05-01 19:01:43 +0100
commit206fb306a7820cf593570e35ddfa8e7c5a10e449 (patch)
tree0ef0fdf42ddc0b63224af52b62b0bad42f62e352 /ThirdParty/SmartThreadPool/WorkItemsQueue.cs
parentFix CAPS to work like they should - do not send caps to the viewer if they're... (diff)
downloadopensim-SC_OLD-206fb306a7820cf593570e35ddfa8e7c5a10e449.zip
opensim-SC_OLD-206fb306a7820cf593570e35ddfa8e7c5a10e449.tar.gz
opensim-SC_OLD-206fb306a7820cf593570e35ddfa8e7c5a10e449.tar.bz2
opensim-SC_OLD-206fb306a7820cf593570e35ddfa8e7c5a10e449.tar.xz
Update SmartThreadPool to latest version 2.2.3 with a major and minor change.
SmartThreadPool code comes from http://www.codeproject.com/Articles/7933/Smart-Thread-Pool This version implements thread abort (via WorkItem.Cancel(true)), threadpool naming, max thread stack, etc. so we no longer need to manually patch those. However, two changes have been made to stock 2.2.3. Major change: WorkItem.Cancel(bool abortExecution) in our version does not succeed if the work item was in progress and thread abort was not specified. This is to match previous behaviour where we handle co-operative termination via another mechanism rather than checking WorkItem.IsCanceled. Minor change: Did not add STP's StopWatch implementation as this is only used WinCE and Silverlight and causes a build clash with System.Diagnostics.StopWatch The reason for updating is to see if this improves http://opensimulator.org/mantis/view.php?id=6557 and http://opensimulator.org/mantis/view.php?id=6586
Diffstat (limited to 'ThirdParty/SmartThreadPool/WorkItemsQueue.cs')
-rw-r--r--ThirdParty/SmartThreadPool/WorkItemsQueue.cs1245
1 files changed, 645 insertions, 600 deletions
diff --git a/ThirdParty/SmartThreadPool/WorkItemsQueue.cs b/ThirdParty/SmartThreadPool/WorkItemsQueue.cs
index af5af07..156a131 100644
--- a/ThirdParty/SmartThreadPool/WorkItemsQueue.cs
+++ b/ThirdParty/SmartThreadPool/WorkItemsQueue.cs
@@ -1,600 +1,645 @@
1// Ami Bar 1using System;
2// amibar@gmail.com 2using System.Collections.Generic;
3 3using System.Threading;
4using System; 4
5using System.Threading; 5namespace Amib.Threading.Internal
6 6{
7namespace Amib.Threading.Internal 7 #region WorkItemsQueue class
8{ 8
9 #region WorkItemsQueue class 9 /// <summary>
10 10 /// WorkItemsQueue class.
11 /// <summary> 11 /// </summary>
12 /// WorkItemsQueue class. 12 public class WorkItemsQueue : IDisposable
13 /// </summary> 13 {
14 public class WorkItemsQueue : IDisposable 14 #region Member variables
15 { 15
16 #region Member variables 16 /// <summary>
17 17 /// Waiters queue (implemented as stack).
18 /// <summary> 18 /// </summary>
19 /// Waiters queue (implemented as stack). 19 private readonly WaiterEntry _headWaiterEntry = new WaiterEntry();
20 /// </summary> 20
21 private WaiterEntry _headWaiterEntry = new WaiterEntry(); 21 /// <summary>
22 22 /// Waiters count
23 /// <summary> 23 /// </summary>
24 /// Waiters count 24 private int _waitersCount = 0;
25 /// </summary> 25
26 private int _waitersCount = 0; 26 /// <summary>
27 27 /// Work items queue
28 /// <summary> 28 /// </summary>
29 /// Work items queue 29 private readonly PriorityQueue _workItems = new PriorityQueue();
30 /// </summary> 30
31 private PriorityQueue _workItems = new PriorityQueue(); 31 /// <summary>
32 32 /// Indicate that work items are allowed to be queued
33 /// <summary> 33 /// </summary>
34 /// Indicate that work items are allowed to be queued 34 private bool _isWorkItemsQueueActive = true;
35 /// </summary> 35
36 private bool _isWorkItemsQueueActive = true; 36
37 37#if (WINDOWS_PHONE)
38 /// <summary> 38 private static readonly Dictionary<int, WaiterEntry> _waiterEntries = new Dictionary<int, WaiterEntry>();
39 /// Each thread in the thread pool keeps its own waiter entry. 39#elif (_WINDOWS_CE)
40 /// </summary> 40 private static LocalDataStoreSlot _waiterEntrySlot = Thread.AllocateDataSlot();
41 [ThreadStatic] 41#else
42 private static WaiterEntry _waiterEntry; 42
43 43 [ThreadStatic]
44 /// <summary> 44 private static WaiterEntry _waiterEntry;
45 /// A flag that indicates if the WorkItemsQueue has been disposed. 45#endif
46 /// </summary> 46
47 private bool _isDisposed = false; 47
48 48 /// <summary>
49 #endregion 49 /// Each thread in the thread pool keeps its own waiter entry.
50 50 /// </summary>
51 #region Public properties 51 private static WaiterEntry CurrentWaiterEntry
52 52 {
53 /// <summary> 53#if (WINDOWS_PHONE)
54 /// Returns the current number of work items in the queue 54 get
55 /// </summary> 55 {
56 public int Count 56 lock (_waiterEntries)
57 { 57 {
58 get 58 WaiterEntry waiterEntry;
59 { 59 if (_waiterEntries.TryGetValue(Thread.CurrentThread.ManagedThreadId, out waiterEntry))
60 lock(this) 60 {
61 { 61 return waiterEntry;
62 ValidateNotDisposed(); 62 }
63 return _workItems.Count; 63 }
64 } 64 return null;
65 } 65 }
66 } 66 set
67 67 {
68 /// <summary> 68 lock (_waiterEntries)
69 /// Returns the current number of waiters 69 {
70 /// </summary> 70 _waiterEntries[Thread.CurrentThread.ManagedThreadId] = value;
71 public int WaitersCount 71 }
72 { 72 }
73 get 73#elif (_WINDOWS_CE)
74 { 74 get
75 lock(this) 75 {
76 { 76 return Thread.GetData(_waiterEntrySlot) as WaiterEntry;
77 ValidateNotDisposed(); 77 }
78 return _waitersCount; 78 set
79 } 79 {
80 } 80 Thread.SetData(_waiterEntrySlot, value);
81 } 81 }
82 82#else
83 83 get
84 #endregion 84 {
85 85 return _waiterEntry;
86 #region Public methods 86 }
87 87 set
88 /// <summary> 88 {
89 /// Enqueue a work item to the queue. 89 _waiterEntry = value;
90 /// </summary> 90 }
91 public bool EnqueueWorkItem(WorkItem workItem) 91#endif
92 { 92 }
93 // A work item cannot be null, since null is used in the 93
94 // WaitForWorkItem() method to indicate timeout or cancel 94 /// <summary>
95 if (null == workItem) 95 /// A flag that indicates if the WorkItemsQueue has been disposed.
96 { 96 /// </summary>
97 throw new ArgumentNullException("workItem" , "workItem cannot be null"); 97 private bool _isDisposed = false;
98 } 98
99 99 #endregion
100 bool enqueue = true; 100
101 101 #region Public properties
102 // First check if there is a waiter waiting for work item. During 102
103 // the check, timed out waiters are ignored. If there is no 103 /// <summary>
104 // waiter then the work item is queued. 104 /// Returns the current number of work items in the queue
105 lock(this) 105 /// </summary>
106 { 106 public int Count
107 ValidateNotDisposed(); 107 {
108 108 get
109 if (!_isWorkItemsQueueActive) 109 {
110 { 110 return _workItems.Count;
111 return false; 111 }
112 } 112 }
113 113
114 while(_waitersCount > 0) 114 /// <summary>
115 { 115 /// Returns the current number of waiters
116 // Dequeue a waiter. 116 /// </summary>
117 WaiterEntry waiterEntry = PopWaiter(); 117 public int WaitersCount
118 118 {
119 // Signal the waiter. On success break the loop 119 get
120 if (waiterEntry.Signal(workItem)) 120 {
121 { 121 return _waitersCount;
122 enqueue = false; 122 }
123 break; 123 }
124 } 124
125 } 125
126 126 #endregion
127 if (enqueue) 127
128 { 128 #region Public methods
129 // Enqueue the work item 129
130 _workItems.Enqueue(workItem); 130 /// <summary>
131 } 131 /// Enqueue a work item to the queue.
132 } 132 /// </summary>
133 return true; 133 public bool EnqueueWorkItem(WorkItem workItem)
134 } 134 {
135 135 // A work item cannot be null, since null is used in the
136 136 // WaitForWorkItem() method to indicate timeout or cancel
137 /// <summary> 137 if (null == workItem)
138 /// Waits for a work item or exits on timeout or cancel 138 {
139 /// </summary> 139 throw new ArgumentNullException("workItem" , "workItem cannot be null");
140 /// <param name="millisecondsTimeout">Timeout in milliseconds</param> 140 }
141 /// <param name="cancelEvent">Cancel wait handle</param> 141
142 /// <returns>Returns true if the resource was granted</returns> 142 bool enqueue = true;
143 public WorkItem DequeueWorkItem( 143
144 int millisecondsTimeout, 144 // First check if there is a waiter waiting for work item. During
145 WaitHandle cancelEvent) 145 // the check, timed out waiters are ignored. If there is no
146 { 146 // waiter then the work item is queued.
147 /// This method cause the caller to wait for a work item. 147 lock(this)
148 /// If there is at least one waiting work item then the 148 {
149 /// method returns immidiately with true. 149 ValidateNotDisposed();
150 /// 150
151 /// If there are no waiting work items then the caller 151 if (!_isWorkItemsQueueActive)
152 /// is queued between other waiters for a work item to arrive. 152 {
153 /// 153 return false;
154 /// If a work item didn't come within millisecondsTimeout or 154 }
155 /// the user canceled the wait by signaling the cancelEvent 155
156 /// then the method returns false to indicate that the caller 156 while(_waitersCount > 0)
157 /// didn't get a work item. 157 {
158 158 // Dequeue a waiter.
159 WaiterEntry waiterEntry = null; 159 WaiterEntry waiterEntry = PopWaiter();
160 WorkItem workItem = null; 160
161 161 // Signal the waiter. On success break the loop
162 lock(this) 162 if (waiterEntry.Signal(workItem))
163 { 163 {
164 ValidateNotDisposed(); 164 enqueue = false;
165 165 break;
166 // If there are waiting work items then take one and return. 166 }
167 if (_workItems.Count > 0) 167 }
168 { 168
169 workItem = _workItems.Dequeue() as WorkItem; 169 if (enqueue)
170 return workItem; 170 {
171 } 171 // Enqueue the work item
172 // No waiting work items ... 172 _workItems.Enqueue(workItem);
173 else 173 }
174 { 174 }
175 // Get the wait entry for the waiters queue 175 return true;
176 waiterEntry = GetThreadWaiterEntry(); 176 }
177 177
178 // Put the waiter with the other waiters 178
179 PushWaiter(waiterEntry); 179 /// <summary>
180 } 180 /// Waits for a work item or exits on timeout or cancel
181 } 181 /// </summary>
182 182 /// <param name="millisecondsTimeout">Timeout in milliseconds</param>
183 // Prepare array of wait handle for the WaitHandle.WaitAny() 183 /// <param name="cancelEvent">Cancel wait handle</param>
184 WaitHandle [] waitHandles = new WaitHandle [] { 184 /// <returns>Returns true if the resource was granted</returns>
185 waiterEntry.WaitHandle, 185 public WorkItem DequeueWorkItem(
186 cancelEvent }; 186 int millisecondsTimeout,
187 187 WaitHandle cancelEvent)
188 // Wait for an available resource, cancel event, or timeout. 188 {
189 189 // This method cause the caller to wait for a work item.
190 // During the wait we are supposes to exit the synchronization 190 // If there is at least one waiting work item then the
191 // domain. (Placing true as the third argument of the WaitAny()) 191 // method returns immidiately with it.
192 // It just doesn't work, I don't know why, so I have lock(this) 192 //
193 // statments insted of one. 193 // If there are no waiting work items then the caller
194 194 // is queued between other waiters for a work item to arrive.
195 int index = WaitHandle.WaitAny( 195 //
196 waitHandles, 196 // If a work item didn't come within millisecondsTimeout or
197 millisecondsTimeout, 197 // the user canceled the wait by signaling the cancelEvent
198 true); 198 // then the method returns null to indicate that the caller
199 199 // didn't get a work item.
200 lock(this) 200
201 { 201 WaiterEntry waiterEntry;
202 // success is true if it got a work item. 202 WorkItem workItem = null;
203 bool success = (0 == index); 203 lock (this)
204 204 {
205 // The timeout variable is used only for readability. 205 ValidateNotDisposed();
206 // (We treat cancel as timeout) 206
207 bool timeout = !success; 207 // If there are waiting work items then take one and return.
208 208 if (_workItems.Count > 0)
209 // On timeout update the waiterEntry that it is timed out 209 {
210 if (timeout) 210 workItem = _workItems.Dequeue() as WorkItem;
211 { 211 return workItem;
212 // The Timeout() fails if the waiter has already been signaled 212 }
213 timeout = waiterEntry.Timeout(); 213
214 214 // No waiting work items ...
215 // On timeout remove the waiter from the queue. 215
216 // Note that the complexity is O(1). 216 // Get the waiter entry for the waiters queue
217 if(timeout) 217 waiterEntry = GetThreadWaiterEntry();
218 { 218
219 RemoveWaiter(waiterEntry, false); 219 // Put the waiter with the other waiters
220 } 220 PushWaiter(waiterEntry);
221 221 }
222 // Again readability 222
223 success = !timeout; 223 // Prepare array of wait handle for the WaitHandle.WaitAny()
224 } 224 WaitHandle [] waitHandles = new WaitHandle[] {
225 225 waiterEntry.WaitHandle,
226 // On success return the work item 226 cancelEvent };
227 if (success) 227
228 { 228 // Wait for an available resource, cancel event, or timeout.
229 workItem = waiterEntry.WorkItem; 229
230 230 // During the wait we are supposes to exit the synchronization
231 if (null == workItem) 231 // domain. (Placing true as the third argument of the WaitAny())
232 { 232 // It just doesn't work, I don't know why, so I have two lock(this)
233 workItem = _workItems.Dequeue() as WorkItem; 233 // statments instead of one.
234 } 234
235 } 235 int index = STPEventWaitHandle.WaitAny(
236 } 236 waitHandles,
237 // On failure return null. 237 millisecondsTimeout,
238 return workItem; 238 true);
239 } 239
240 240 lock(this)
241 /// <summary> 241 {
242 /// Cleanup the work items queue, hence no more work 242 // success is true if it got a work item.
243 /// items are allowed to be queue 243 bool success = (0 == index);
244 /// </summary> 244
245 protected virtual void Cleanup() 245 // The timeout variable is used only for readability.
246 { 246 // (We treat cancel as timeout)
247 lock(this) 247 bool timeout = !success;
248 { 248
249 // Deactivate only once 249 // On timeout update the waiterEntry that it is timed out
250 if (!_isWorkItemsQueueActive) 250 if (timeout)
251 { 251 {
252 return; 252 // The Timeout() fails if the waiter has already been signaled
253 } 253 timeout = waiterEntry.Timeout();
254 254
255 // Don't queue more work items 255 // On timeout remove the waiter from the queue.
256 _isWorkItemsQueueActive = false; 256 // Note that the complexity is O(1).
257 257 if(timeout)
258 foreach(WorkItem workItem in _workItems) 258 {
259 { 259 RemoveWaiter(waiterEntry, false);
260 workItem.DisposeOfState(); 260 }
261 } 261
262 262 // Again readability
263 // Clear the work items that are already queued 263 success = !timeout;
264 _workItems.Clear(); 264 }
265 265
266 // Note: 266 // On success return the work item
267 // I don't iterate over the queue and dispose of work items's states, 267 if (success)
268 // since if a work item has a state object that is still in use in the 268 {
269 // application then I must not dispose it. 269 workItem = waiterEntry.WorkItem;
270 270
271 // Tell the waiters that they were timed out. 271 if (null == workItem)
272 // It won't signal them to exit, but to ignore their 272 {
273 // next work item. 273 workItem = _workItems.Dequeue() as WorkItem;
274 while(_waitersCount > 0) 274 }
275 { 275 }
276 WaiterEntry waiterEntry = PopWaiter(); 276 }
277 waiterEntry.Timeout(); 277 // On failure return null.
278 } 278 return workItem;
279 } 279 }
280 } 280
281 281 /// <summary>
282 #endregion 282 /// Cleanup the work items queue, hence no more work
283 283 /// items are allowed to be queue
284 #region Private methods 284 /// </summary>
285 285 private void Cleanup()
286 /// <summary> 286 {
287 /// Returns the WaiterEntry of the current thread 287 lock(this)
288 /// </summary> 288 {
289 /// <returns></returns> 289 // Deactivate only once
290 /// In order to avoid creation and destuction of WaiterEntry 290 if (!_isWorkItemsQueueActive)
291 /// objects each thread has its own WaiterEntry object. 291 {
292 private WaiterEntry GetThreadWaiterEntry() 292 return;
293 { 293 }
294 if (null == _waiterEntry) 294
295 { 295 // Don't queue more work items
296 _waiterEntry = new WaiterEntry(); 296 _isWorkItemsQueueActive = false;
297 } 297
298 _waiterEntry.Reset(); 298 foreach(WorkItem workItem in _workItems)
299 return _waiterEntry; 299 {
300 } 300 workItem.DisposeOfState();
301 301 }
302 #region Waiters stack methods 302
303 303 // Clear the work items that are already queued
304 /// <summary> 304 _workItems.Clear();
305 /// Push a new waiter into the waiter's stack 305
306 /// </summary> 306 // Note:
307 /// <param name="newWaiterEntry">A waiter to put in the stack</param> 307 // I don't iterate over the queue and dispose of work items's states,
308 public void PushWaiter(WaiterEntry newWaiterEntry) 308 // since if a work item has a state object that is still in use in the
309 { 309 // application then I must not dispose it.
310 // Remove the waiter if it is already in the stack and 310
311 // update waiter's count as needed 311 // Tell the waiters that they were timed out.
312 RemoveWaiter(newWaiterEntry, false); 312 // It won't signal them to exit, but to ignore their
313 313 // next work item.
314 // If the stack is empty then newWaiterEntry is the new head of the stack 314 while(_waitersCount > 0)
315 if (null == _headWaiterEntry._nextWaiterEntry) 315 {
316 { 316 WaiterEntry waiterEntry = PopWaiter();
317 _headWaiterEntry._nextWaiterEntry = newWaiterEntry; 317 waiterEntry.Timeout();
318 newWaiterEntry._prevWaiterEntry = _headWaiterEntry; 318 }
319 319 }
320 } 320 }
321 // If the stack is not empty then put newWaiterEntry as the new head 321
322 // of the stack. 322 public object[] GetStates()
323 else 323 {
324 { 324 lock (this)
325 // Save the old first waiter entry 325 {
326 WaiterEntry oldFirstWaiterEntry = _headWaiterEntry._nextWaiterEntry; 326 object[] states = new object[_workItems.Count];
327 327 int i = 0;
328 // Update the links 328 foreach (WorkItem workItem in _workItems)
329 _headWaiterEntry._nextWaiterEntry = newWaiterEntry; 329 {
330 newWaiterEntry._nextWaiterEntry = oldFirstWaiterEntry; 330 states[i] = workItem.GetWorkItemResult().State;
331 newWaiterEntry._prevWaiterEntry = _headWaiterEntry; 331 ++i;
332 oldFirstWaiterEntry._prevWaiterEntry = newWaiterEntry; 332 }
333 } 333 return states;
334 334 }
335 // Increment the number of waiters 335 }
336 ++_waitersCount; 336
337 } 337 #endregion
338 338
339 /// <summary> 339 #region Private methods
340 /// Pop a waiter from the waiter's stack 340
341 /// </summary> 341 /// <summary>
342 /// <returns>Returns the first waiter in the stack</returns> 342 /// Returns the WaiterEntry of the current thread
343 private WaiterEntry PopWaiter() 343 /// </summary>
344 { 344 /// <returns></returns>
345 // Store the current stack head 345 /// In order to avoid creation and destuction of WaiterEntry
346 WaiterEntry oldFirstWaiterEntry = _headWaiterEntry._nextWaiterEntry; 346 /// objects each thread has its own WaiterEntry object.
347 347 private static WaiterEntry GetThreadWaiterEntry()
348 // Store the new stack head 348 {
349 WaiterEntry newHeadWaiterEntry = oldFirstWaiterEntry._nextWaiterEntry; 349 if (null == CurrentWaiterEntry)
350 350 {
351 // Update the old stack head list links and decrement the number 351 CurrentWaiterEntry = new WaiterEntry();
352 // waiters. 352 }
353 RemoveWaiter(oldFirstWaiterEntry, true); 353 CurrentWaiterEntry.Reset();
354 354 return CurrentWaiterEntry;
355 // Update the new stack head 355 }
356 _headWaiterEntry._nextWaiterEntry = newHeadWaiterEntry; 356
357 if (null != newHeadWaiterEntry) 357 #region Waiters stack methods
358 { 358
359 newHeadWaiterEntry._prevWaiterEntry = _headWaiterEntry; 359 /// <summary>
360 } 360 /// Push a new waiter into the waiter's stack
361 361 /// </summary>
362 // Return the old stack head 362 /// <param name="newWaiterEntry">A waiter to put in the stack</param>
363 return oldFirstWaiterEntry; 363 public void PushWaiter(WaiterEntry newWaiterEntry)
364 } 364 {
365 365 // Remove the waiter if it is already in the stack and
366 /// <summary> 366 // update waiter's count as needed
367 /// Remove a waiter from the stack 367 RemoveWaiter(newWaiterEntry, false);
368 /// </summary> 368
369 /// <param name="waiterEntry">A waiter entry to remove</param> 369 // If the stack is empty then newWaiterEntry is the new head of the stack
370 /// <param name="popDecrement">If true the waiter count is always decremented</param> 370 if (null == _headWaiterEntry._nextWaiterEntry)
371 private void RemoveWaiter(WaiterEntry waiterEntry, bool popDecrement) 371 {
372 { 372 _headWaiterEntry._nextWaiterEntry = newWaiterEntry;
373 // Store the prev entry in the list 373 newWaiterEntry._prevWaiterEntry = _headWaiterEntry;
374 WaiterEntry prevWaiterEntry = waiterEntry._prevWaiterEntry; 374
375 375 }
376 // Store the next entry in the list 376 // If the stack is not empty then put newWaiterEntry as the new head
377 WaiterEntry nextWaiterEntry = waiterEntry._nextWaiterEntry; 377 // of the stack.
378 378 else
379 // A flag to indicate if we need to decrement the waiters count. 379 {
380 // If we got here from PopWaiter then we must decrement. 380 // Save the old first waiter entry
381 // If we got here from PushWaiter then we decrement only if 381 WaiterEntry oldFirstWaiterEntry = _headWaiterEntry._nextWaiterEntry;
382 // the waiter was already in the stack. 382
383 bool decrementCounter = popDecrement; 383 // Update the links
384 384 _headWaiterEntry._nextWaiterEntry = newWaiterEntry;
385 // Null the waiter's entry links 385 newWaiterEntry._nextWaiterEntry = oldFirstWaiterEntry;
386 waiterEntry._prevWaiterEntry = null; 386 newWaiterEntry._prevWaiterEntry = _headWaiterEntry;
387 waiterEntry._nextWaiterEntry = null; 387 oldFirstWaiterEntry._prevWaiterEntry = newWaiterEntry;
388 388 }
389 // If the waiter entry had a prev link then update it. 389
390 // It also means that the waiter is already in the list and we 390 // Increment the number of waiters
391 // need to decrement the waiters count. 391 ++_waitersCount;
392 if (null != prevWaiterEntry) 392 }
393 { 393
394 prevWaiterEntry._nextWaiterEntry = nextWaiterEntry; 394 /// <summary>
395 decrementCounter = true; 395 /// Pop a waiter from the waiter's stack
396 } 396 /// </summary>
397 397 /// <returns>Returns the first waiter in the stack</returns>
398 // If the waiter entry had a next link then update it. 398 private WaiterEntry PopWaiter()
399 // It also means that the waiter is already in the list and we 399 {
400 // need to decrement the waiters count. 400 // Store the current stack head
401 if (null != nextWaiterEntry) 401 WaiterEntry oldFirstWaiterEntry = _headWaiterEntry._nextWaiterEntry;
402 { 402
403 nextWaiterEntry._prevWaiterEntry = prevWaiterEntry; 403 // Store the new stack head
404 decrementCounter = true; 404 WaiterEntry newHeadWaiterEntry = oldFirstWaiterEntry._nextWaiterEntry;
405 } 405
406 406 // Update the old stack head list links and decrement the number
407 // Decrement the waiters count if needed 407 // waiters.
408 if (decrementCounter) 408 RemoveWaiter(oldFirstWaiterEntry, true);
409 { 409
410 --_waitersCount; 410 // Update the new stack head
411 } 411 _headWaiterEntry._nextWaiterEntry = newHeadWaiterEntry;
412 } 412 if (null != newHeadWaiterEntry)
413 413 {
414 #endregion 414 newHeadWaiterEntry._prevWaiterEntry = _headWaiterEntry;
415 415 }
416 #endregion 416
417 417 // Return the old stack head
418 #region WaiterEntry class 418 return oldFirstWaiterEntry;
419 419 }
420 // A waiter entry in the _waiters queue. 420
421 public class WaiterEntry : IDisposable 421 /// <summary>
422 { 422 /// Remove a waiter from the stack
423 #region Member variables 423 /// </summary>
424 424 /// <param name="waiterEntry">A waiter entry to remove</param>
425 /// <summary> 425 /// <param name="popDecrement">If true the waiter count is always decremented</param>
426 /// Event to signal the waiter that it got the work item. 426 private void RemoveWaiter(WaiterEntry waiterEntry, bool popDecrement)
427 /// </summary> 427 {
428 private AutoResetEvent _waitHandle = new AutoResetEvent(false); 428 // Store the prev entry in the list
429 429 WaiterEntry prevWaiterEntry = waiterEntry._prevWaiterEntry;
430 /// <summary> 430
431 /// Flag to know if this waiter already quited from the queue 431 // Store the next entry in the list
432 /// because of a timeout. 432 WaiterEntry nextWaiterEntry = waiterEntry._nextWaiterEntry;
433 /// </summary> 433
434 private bool _isTimedout = false; 434 // A flag to indicate if we need to decrement the waiters count.
435 435 // If we got here from PopWaiter then we must decrement.
436 /// <summary> 436 // If we got here from PushWaiter then we decrement only if
437 /// Flag to know if the waiter was signaled and got a work item. 437 // the waiter was already in the stack.
438 /// </summary> 438 bool decrementCounter = popDecrement;
439 private bool _isSignaled = false; 439
440 440 // Null the waiter's entry links
441 /// <summary> 441 waiterEntry._prevWaiterEntry = null;
442 /// A work item that passed directly to the waiter withou going 442 waiterEntry._nextWaiterEntry = null;
443 /// through the queue 443
444 /// </summary> 444 // If the waiter entry had a prev link then update it.
445 private WorkItem _workItem = null; 445 // It also means that the waiter is already in the list and we
446 446 // need to decrement the waiters count.
447 private bool _isDisposed = false; 447 if (null != prevWaiterEntry)
448 448 {
449 // Linked list members 449 prevWaiterEntry._nextWaiterEntry = nextWaiterEntry;
450 internal WaiterEntry _nextWaiterEntry = null; 450 decrementCounter = true;
451 internal WaiterEntry _prevWaiterEntry = null; 451 }
452 452
453 #endregion 453 // If the waiter entry had a next link then update it.
454 454 // It also means that the waiter is already in the list and we
455 #region Construction 455 // need to decrement the waiters count.
456 456 if (null != nextWaiterEntry)
457 public WaiterEntry() 457 {
458 { 458 nextWaiterEntry._prevWaiterEntry = prevWaiterEntry;
459 Reset(); 459 decrementCounter = true;
460 } 460 }
461 461
462 #endregion 462 // Decrement the waiters count if needed
463 463 if (decrementCounter)
464 #region Public methods 464 {
465 465 --_waitersCount;
466 public WaitHandle WaitHandle 466 }
467 { 467 }
468 get { return _waitHandle; } 468
469 } 469 #endregion
470 470
471 public WorkItem WorkItem 471 #endregion
472 { 472
473 get 473 #region WaiterEntry class
474 { 474
475 lock(this) 475 // A waiter entry in the _waiters queue.
476 { 476 public sealed class WaiterEntry : IDisposable
477 return _workItem; 477 {
478 } 478 #region Member variables
479 } 479
480 } 480 /// <summary>
481 481 /// Event to signal the waiter that it got the work item.
482 /// <summary> 482 /// </summary>
483 /// Signal the waiter that it got a work item. 483 //private AutoResetEvent _waitHandle = new AutoResetEvent(false);
484 /// </summary> 484 private AutoResetEvent _waitHandle = EventWaitHandleFactory.CreateAutoResetEvent();
485 /// <returns>Return true on success</returns> 485
486 /// The method fails if Timeout() preceded its call 486 /// <summary>
487 public bool Signal(WorkItem workItem) 487 /// Flag to know if this waiter already quited from the queue
488 { 488 /// because of a timeout.
489 lock(this) 489 /// </summary>
490 { 490 private bool _isTimedout = false;
491 if (!_isTimedout) 491
492 { 492 /// <summary>
493 _workItem = workItem; 493 /// Flag to know if the waiter was signaled and got a work item.
494 _isSignaled = true; 494 /// </summary>
495 _waitHandle.Set(); 495 private bool _isSignaled = false;
496 return true; 496
497 } 497 /// <summary>
498 } 498 /// A work item that passed directly to the waiter withou going
499 return false; 499 /// through the queue
500 } 500 /// </summary>
501 501 private WorkItem _workItem = null;
502 /// <summary> 502
503 /// Mark the wait entry that it has been timed out 503 private bool _isDisposed = false;
504 /// </summary> 504
505 /// <returns>Return true on success</returns> 505 // Linked list members
506 /// The method fails if Signal() preceded its call 506 internal WaiterEntry _nextWaiterEntry = null;
507 public bool Timeout() 507 internal WaiterEntry _prevWaiterEntry = null;
508 { 508
509 lock(this) 509 #endregion
510 { 510
511 // Time out can happen only if the waiter wasn't marked as 511 #region Construction
512 // signaled 512
513 if (!_isSignaled) 513 public WaiterEntry()
514 { 514 {
515 // We don't remove the waiter from the queue, the DequeueWorkItem 515 Reset();
516 // method skips _waiters that were timed out. 516 }
517 _isTimedout = true; 517
518 return true; 518 #endregion
519 } 519
520 } 520 #region Public methods
521 return false; 521
522 } 522 public WaitHandle WaitHandle
523 523 {
524 /// <summary> 524 get { return _waitHandle; }
525 /// Reset the wait entry so it can be used again 525 }
526 /// </summary> 526
527 public void Reset() 527 public WorkItem WorkItem
528 { 528 {
529 _workItem = null; 529 get
530 _isTimedout = false; 530 {
531 _isSignaled = false; 531 return _workItem;
532 _waitHandle.Reset(); 532 }
533 } 533 }
534 534
535 /// <summary> 535 /// <summary>
536 /// Free resources 536 /// Signal the waiter that it got a work item.
537 /// </summary> 537 /// </summary>
538 public void Close() 538 /// <returns>Return true on success</returns>
539 { 539 /// The method fails if Timeout() preceded its call
540 if (null != _waitHandle) 540 public bool Signal(WorkItem workItem)
541 { 541 {
542 _waitHandle.Close(); 542 lock(this)
543 _waitHandle = null; 543 {
544 } 544 if (!_isTimedout)
545 } 545 {
546 546 _workItem = workItem;
547 #endregion 547 _isSignaled = true;
548 548 _waitHandle.Set();
549 #region IDisposable Members 549 return true;
550 550 }
551 public void Dispose() 551 }
552 { 552 return false;
553 if (!_isDisposed) 553 }
554 { 554
555 Close(); 555 /// <summary>
556 _isDisposed = true; 556 /// Mark the wait entry that it has been timed out
557 } 557 /// </summary>
558 } 558 /// <returns>Return true on success</returns>
559 559 /// The method fails if Signal() preceded its call
560 ~WaiterEntry() 560 public bool Timeout()
561 { 561 {
562 Dispose(); 562 lock(this)
563 } 563 {
564 564 // Time out can happen only if the waiter wasn't marked as
565 #endregion 565 // signaled
566 } 566 if (!_isSignaled)
567 567 {
568 #endregion 568 // We don't remove the waiter from the queue, the DequeueWorkItem
569 569 // method skips _waiters that were timed out.
570 #region IDisposable Members 570 _isTimedout = true;
571 571 return true;
572 public void Dispose() 572 }
573 { 573 }
574 if (!_isDisposed) 574 return false;
575 { 575 }
576 Cleanup(); 576
577 _isDisposed = true; 577 /// <summary>
578 GC.SuppressFinalize(this); 578 /// Reset the wait entry so it can be used again
579 } 579 /// </summary>
580 } 580 public void Reset()
581 581 {
582 ~WorkItemsQueue() 582 _workItem = null;
583 { 583 _isTimedout = false;
584 Cleanup(); 584 _isSignaled = false;
585 } 585 _waitHandle.Reset();
586 586 }
587 private void ValidateNotDisposed() 587
588 { 588 /// <summary>
589 if(_isDisposed) 589 /// Free resources
590 { 590 /// </summary>
591 throw new ObjectDisposedException(GetType().ToString(), "The SmartThreadPool has been shutdown"); 591 public void Close()
592 } 592 {
593 } 593 if (null != _waitHandle)
594 594 {
595 #endregion 595 _waitHandle.Close();
596 } 596 _waitHandle = null;
597 597 }
598 #endregion 598 }
599} 599
600 600 #endregion
601
602 #region IDisposable Members
603
604 public void Dispose()
605 {
606 lock (this)
607 {
608 if (!_isDisposed)
609 {
610 Close();
611 }
612 _isDisposed = true;
613 }
614 }
615
616 #endregion
617 }
618
619 #endregion
620
621 #region IDisposable Members
622
623 public void Dispose()
624 {
625 if (!_isDisposed)
626 {
627 Cleanup();
628 }
629 _isDisposed = true;
630 }
631
632 private void ValidateNotDisposed()
633 {
634 if(_isDisposed)
635 {
636 throw new ObjectDisposedException(GetType().ToString(), "The SmartThreadPool has been shutdown");
637 }
638 }
639
640 #endregion
641 }
642
643 #endregion
644}
645