opensim – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 eva 1 using System;
2 using System.Threading;
3 using System.Diagnostics;
4  
5 namespace Amib.Threading.Internal
6 {
7 /// <summary>
8 /// Holds a callback delegate and the state for that delegate.
9 /// </summary>
10 public partial class WorkItem : IHasWorkItemPriority
11 {
12 #region WorkItemState enum
13  
14 /// <summary>
15 /// Indicates the state of the work item in the thread pool
16 /// </summary>
17 private enum WorkItemState
18 {
19 InQueue = 0, // Nexts: InProgress, Canceled
20 InProgress = 1, // Nexts: Completed, Canceled
21 Completed = 2, // Stays Completed
22 Canceled = 3, // Stays Canceled
23 }
24  
25 private static bool IsValidStatesTransition(WorkItemState currentState, WorkItemState nextState)
26 {
27 bool valid = false;
28  
29 switch (currentState)
30 {
31 case WorkItemState.InQueue:
32 valid = (WorkItemState.InProgress == nextState) || (WorkItemState.Canceled == nextState);
33 break;
34 case WorkItemState.InProgress:
35 valid = (WorkItemState.Completed == nextState) || (WorkItemState.Canceled == nextState);
36 break;
37 case WorkItemState.Completed:
38 case WorkItemState.Canceled:
39 // Cannot be changed
40 break;
41 default:
42 // Unknown state
43 Debug.Assert(false);
44 break;
45 }
46  
47 return valid;
48 }
49  
50 #endregion
51  
52 #region Fields
53  
54 /// <summary>
55 /// Callback delegate for the callback.
56 /// </summary>
57 private readonly WorkItemCallback _callback;
58  
59 /// <summary>
60 /// State with which to call the callback delegate.
61 /// </summary>
62 private object _state;
63  
64 #if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE)
65 /// <summary>
66 /// Stores the caller's context
67 /// </summary>
68 private readonly CallerThreadContext _callerContext;
69 #endif
70 /// <summary>
71 /// Holds the result of the mehtod
72 /// </summary>
73 private object _result;
74  
75 /// <summary>
76 /// Hold the exception if the method threw it
77 /// </summary>
78 private Exception _exception;
79  
80 /// <summary>
81 /// Hold the state of the work item
82 /// </summary>
83 private WorkItemState _workItemState;
84  
85 /// <summary>
86 /// A ManualResetEvent to indicate that the result is ready
87 /// </summary>
88 private ManualResetEvent _workItemCompleted;
89  
90 /// <summary>
91 /// A reference count to the _workItemCompleted.
92 /// When it reaches to zero _workItemCompleted is Closed
93 /// </summary>
94 private int _workItemCompletedRefCount;
95  
96 /// <summary>
97 /// Represents the result state of the work item
98 /// </summary>
99 private readonly WorkItemResult _workItemResult;
100  
101 /// <summary>
102 /// Work item info
103 /// </summary>
104 private readonly WorkItemInfo _workItemInfo;
105  
106 /// <summary>
107 /// Called when the WorkItem starts
108 /// </summary>
109 private event WorkItemStateCallback _workItemStartedEvent;
110  
111 /// <summary>
112 /// Called when the WorkItem completes
113 /// </summary>
114 private event WorkItemStateCallback _workItemCompletedEvent;
115  
116 /// <summary>
117 /// A reference to an object that indicates whatever the
118 /// WorkItemsGroup has been canceled
119 /// </summary>
120 private CanceledWorkItemsGroup _canceledWorkItemsGroup = CanceledWorkItemsGroup.NotCanceledWorkItemsGroup;
121  
122 /// <summary>
123 /// A reference to an object that indicates whatever the
124 /// SmartThreadPool has been canceled
125 /// </summary>
126 private CanceledWorkItemsGroup _canceledSmartThreadPool = CanceledWorkItemsGroup.NotCanceledWorkItemsGroup;
127  
128 /// <summary>
129 /// The work item group this work item belong to.
130 /// </summary>
131 private readonly IWorkItemsGroup _workItemsGroup;
132  
133 /// <summary>
134 /// The thread that executes this workitem.
135 /// This field is available for the period when the work item is executed, before and after it is null.
136 /// </summary>
137 private Thread _executingThread;
138  
139 /// <summary>
140 /// The absulote time when the work item will be timeout
141 /// </summary>
142 private long _expirationTime;
143  
144 #region Performance Counter fields
145  
146  
147  
148  
149 /// <summary>
150 /// Stores how long the work item waited on the stp queue
151 /// </summary>
152 private Stopwatch _waitingOnQueueStopwatch;
153  
154 /// <summary>
155 /// Stores how much time it took the work item to execute after it went out of the queue
156 /// </summary>
157 private Stopwatch _processingStopwatch;
158  
159 #endregion
160  
161 #endregion
162  
163 #region Properties
164  
165 public TimeSpan WaitingTime
166 {
167 get
168 {
169 return _waitingOnQueueStopwatch.Elapsed;
170 }
171 }
172  
173 public TimeSpan ProcessTime
174 {
175 get
176 {
177 return _processingStopwatch.Elapsed;
178 }
179 }
180  
181 internal WorkItemInfo WorkItemInfo
182 {
183 get
184 {
185 return _workItemInfo;
186 }
187 }
188  
189 #endregion
190  
191 #region Construction
192  
193 /// <summary>
194 /// Initialize the callback holding object.
195 /// </summary>
196 /// <param name="workItemsGroup">The workItemGroup of the workitem</param>
197 /// <param name="workItemInfo">The WorkItemInfo of te workitem</param>
198 /// <param name="callback">Callback delegate for the callback.</param>
199 /// <param name="state">State with which to call the callback delegate.</param>
200 ///
201 /// We assume that the WorkItem object is created within the thread
202 /// that meant to run the callback
203 public WorkItem(
204 IWorkItemsGroup workItemsGroup,
205 WorkItemInfo workItemInfo,
206 WorkItemCallback callback,
207 object state)
208 {
209 _workItemsGroup = workItemsGroup;
210 _workItemInfo = workItemInfo;
211  
212 #if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE)
213 if (_workItemInfo.UseCallerCallContext || _workItemInfo.UseCallerHttpContext)
214 {
215 _callerContext = CallerThreadContext.Capture(_workItemInfo.UseCallerCallContext, _workItemInfo.UseCallerHttpContext);
216 }
217 #endif
218  
219 _callback = callback;
220 _state = state;
221 _workItemResult = new WorkItemResult(this);
222 Initialize();
223 }
224  
225 internal void Initialize()
226 {
227 // The _workItemState is changed directly instead of using the SetWorkItemState
228 // method since we don't want to go throught IsValidStateTransition.
229 _workItemState = WorkItemState.InQueue;
230  
231 _workItemCompleted = null;
232 _workItemCompletedRefCount = 0;
233 _waitingOnQueueStopwatch = new Stopwatch();
234 _processingStopwatch = new Stopwatch();
235 _expirationTime =
236 _workItemInfo.Timeout > 0 ?
237 DateTime.UtcNow.Ticks + _workItemInfo.Timeout * TimeSpan.TicksPerMillisecond :
238 long.MaxValue;
239 }
240  
241 internal bool WasQueuedBy(IWorkItemsGroup workItemsGroup)
242 {
243 return (workItemsGroup == _workItemsGroup);
244 }
245  
246  
247 #endregion
248  
249 #region Methods
250  
251 internal CanceledWorkItemsGroup CanceledWorkItemsGroup
252 {
253 get { return _canceledWorkItemsGroup; }
254 set { _canceledWorkItemsGroup = value; }
255 }
256  
257 internal CanceledWorkItemsGroup CanceledSmartThreadPool
258 {
259 get { return _canceledSmartThreadPool; }
260 set { _canceledSmartThreadPool = value; }
261 }
262  
263 /// <summary>
264 /// Change the state of the work item to in progress if it wasn't canceled.
265 /// </summary>
266 /// <returns>
267 /// Return true on success or false in case the work item was canceled.
268 /// If the work item needs to run a post execute then the method will return true.
269 /// </returns>
270 public bool StartingWorkItem()
271 {
272 _waitingOnQueueStopwatch.Stop();
273 _processingStopwatch.Start();
274  
275 lock (this)
276 {
277 if (IsCanceled)
278 {
279 bool result = false;
280 if ((_workItemInfo.PostExecuteWorkItemCallback != null) &&
281 ((_workItemInfo.CallToPostExecute & CallToPostExecute.WhenWorkItemCanceled) == CallToPostExecute.WhenWorkItemCanceled))
282 {
283 result = true;
284 }
285  
286 return result;
287 }
288  
289 Debug.Assert(WorkItemState.InQueue == GetWorkItemState());
290  
291 // No need for a lock yet, only after the state has changed to InProgress
292 _executingThread = Thread.CurrentThread;
293  
294 SetWorkItemState(WorkItemState.InProgress);
295 }
296  
297 return true;
298 }
299  
300 /// <summary>
301 /// Execute the work item and the post execute
302 /// </summary>
303 public void Execute()
304 {
305 CallToPostExecute currentCallToPostExecute = 0;
306  
307 // Execute the work item if we are in the correct state
308 switch (GetWorkItemState())
309 {
310 case WorkItemState.InProgress:
311 currentCallToPostExecute |= CallToPostExecute.WhenWorkItemNotCanceled;
312 ExecuteWorkItem();
313 break;
314 case WorkItemState.Canceled:
315 currentCallToPostExecute |= CallToPostExecute.WhenWorkItemCanceled;
316 break;
317 default:
318 Debug.Assert(false);
319 throw new NotSupportedException();
320 }
321  
322 // Run the post execute as needed
323 if ((currentCallToPostExecute & _workItemInfo.CallToPostExecute) != 0)
324 {
325 PostExecute();
326 }
327  
328 _processingStopwatch.Stop();
329 }
330  
331 internal void FireWorkItemCompleted()
332 {
333 try
334 {
335 if (null != _workItemCompletedEvent)
336 {
337 _workItemCompletedEvent(this);
338 }
339 }
340 catch // Suppress exceptions
341 { }
342 }
343  
344 internal void FireWorkItemStarted()
345 {
346 try
347 {
348 if (null != _workItemStartedEvent)
349 {
350 _workItemStartedEvent(this);
351 }
352 }
353 catch // Suppress exceptions
354 { }
355 }
356  
357 /// <summary>
358 /// Execute the work item
359 /// </summary>
360 private void ExecuteWorkItem()
361 {
362  
363 #if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE)
364 CallerThreadContext ctc = null;
365 if (null != _callerContext)
366 {
367 ctc = CallerThreadContext.Capture(_callerContext.CapturedCallContext, _callerContext.CapturedHttpContext);
368 CallerThreadContext.Apply(_callerContext);
369 }
370 #endif
371  
372 Exception exception = null;
373 object result = null;
374  
375 try
376 {
377 try
378 {
379 result = _callback(_state);
380 }
381 catch (Exception e)
382 {
383 // Save the exception so we can rethrow it later
384 exception = e;
385 }
386  
387 // Remove the value of the execution thread, so it will be impossible to cancel the work item,
388 // since it is already completed.
389 // Cancelling a work item that already completed may cause the abortion of the next work item!!!
390 Thread executionThread = Interlocked.CompareExchange(ref _executingThread, null, _executingThread);
391  
392 if (null == executionThread)
393 {
394 // Oops! we are going to be aborted..., Wait here so we can catch the ThreadAbortException
395 Thread.Sleep(60 * 1000);
396  
397 // If after 1 minute this thread was not aborted then let it continue working.
398 }
399 }
400 // We must treat the ThreadAbortException or else it will be stored in the exception variable
401 catch (ThreadAbortException tae)
402 {
403 tae.GetHashCode();
404 // Check if the work item was cancelled
405 // If we got a ThreadAbortException and the STP is not shutting down, it means the
406 // work items was cancelled.
407 if (!SmartThreadPool.CurrentThreadEntry.AssociatedSmartThreadPool.IsShuttingdown)
408 {
409 #if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE)
410 Thread.ResetAbort();
411 #endif
412 }
413 }
414  
415 #if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE)
416 if (null != _callerContext)
417 {
418 CallerThreadContext.Apply(ctc);
419 }
420 #endif
421  
422 if (!SmartThreadPool.IsWorkItemCanceled)
423 {
424 SetResult(result, exception);
425 }
426 }
427  
428 /// <summary>
429 /// Runs the post execute callback
430 /// </summary>
431 private void PostExecute()
432 {
433 if (null != _workItemInfo.PostExecuteWorkItemCallback)
434 {
435 try
436 {
437 _workItemInfo.PostExecuteWorkItemCallback(_workItemResult);
438 }
439 catch (Exception e)
440 {
441 Debug.Assert(null != e);
442 }
443 }
444 }
445  
446 /// <summary>
447 /// Set the result of the work item to return
448 /// </summary>
449 /// <param name="result">The result of the work item</param>
450 /// <param name="exception">The exception that was throw while the workitem executed, null
451 /// if there was no exception.</param>
452 internal void SetResult(object result, Exception exception)
453 {
454 _result = result;
455 _exception = exception;
456 SignalComplete(false);
457 }
458  
459 /// <summary>
460 /// Returns the work item result
461 /// </summary>
462 /// <returns>The work item result</returns>
463 internal IWorkItemResult GetWorkItemResult()
464 {
465 return _workItemResult;
466 }
467  
468 /// <summary>
469 /// Wait for all work items to complete
470 /// </summary>
471 /// <param name="waitableResults">Array of work item result objects</param>
472 /// <param name="millisecondsTimeout">The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely.</param>
473 /// <param name="exitContext">
474 /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false.
475 /// </param>
476 /// <param name="cancelWaitHandle">A cancel wait handle to interrupt the wait if needed</param>
477 /// <returns>
478 /// true when every work item in waitableResults has completed; otherwise false.
479 /// </returns>
480 internal static bool WaitAll(
481 IWaitableResult[] waitableResults,
482 int millisecondsTimeout,
483 bool exitContext,
484 WaitHandle cancelWaitHandle)
485 {
486 if (0 == waitableResults.Length)
487 {
488 return true;
489 }
490  
491 bool success;
492 WaitHandle[] waitHandles = new WaitHandle[waitableResults.Length];
493 GetWaitHandles(waitableResults, waitHandles);
494  
495 if ((null == cancelWaitHandle) && (waitHandles.Length <= 64))
496 {
497 success = STPEventWaitHandle.WaitAll(waitHandles, millisecondsTimeout, exitContext);
498 }
499 else
500 {
501 success = true;
502 int millisecondsLeft = millisecondsTimeout;
503 Stopwatch stopwatch = Stopwatch.StartNew();
504  
505 WaitHandle[] whs;
506 if (null != cancelWaitHandle)
507 {
508 whs = new WaitHandle[] { null, cancelWaitHandle };
509 }
510 else
511 {
512 whs = new WaitHandle[] { null };
513 }
514  
515 bool waitInfinitely = (Timeout.Infinite == millisecondsTimeout);
516 // Iterate over the wait handles and wait for each one to complete.
517 // We cannot use WaitHandle.WaitAll directly, because the cancelWaitHandle
518 // won't affect it.
519 // Each iteration we update the time left for the timeout.
520 for (int i = 0; i < waitableResults.Length; ++i)
521 {
522 // WaitAny don't work with negative numbers
523 if (!waitInfinitely && (millisecondsLeft < 0))
524 {
525 success = false;
526 break;
527 }
528  
529 whs[0] = waitHandles[i];
530 int result = STPEventWaitHandle.WaitAny(whs, millisecondsLeft, exitContext);
531 if ((result > 0) || (STPEventWaitHandle.WaitTimeout == result))
532 {
533 success = false;
534 break;
535 }
536  
537 if (!waitInfinitely)
538 {
539 // Update the time left to wait
540 millisecondsLeft = millisecondsTimeout - (int)stopwatch.ElapsedMilliseconds;
541 }
542 }
543 }
544 // Release the wait handles
545 ReleaseWaitHandles(waitableResults);
546  
547 return success;
548 }
549  
550 /// <summary>
551 /// Waits for any of the work items in the specified array to complete, cancel, or timeout
552 /// </summary>
553 /// <param name="waitableResults">Array of work item result objects</param>
554 /// <param name="millisecondsTimeout">The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely.</param>
555 /// <param name="exitContext">
556 /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false.
557 /// </param>
558 /// <param name="cancelWaitHandle">A cancel wait handle to interrupt the wait if needed</param>
559 /// <returns>
560 /// The array index of the work item result that satisfied the wait, or WaitTimeout if no work item result satisfied the wait and a time interval equivalent to millisecondsTimeout has passed or the work item has been canceled.
561 /// </returns>
562 internal static int WaitAny(
563 IWaitableResult[] waitableResults,
564 int millisecondsTimeout,
565 bool exitContext,
566 WaitHandle cancelWaitHandle)
567 {
568 WaitHandle[] waitHandles;
569  
570 if (null != cancelWaitHandle)
571 {
572 waitHandles = new WaitHandle[waitableResults.Length + 1];
573 GetWaitHandles(waitableResults, waitHandles);
574 waitHandles[waitableResults.Length] = cancelWaitHandle;
575 }
576 else
577 {
578 waitHandles = new WaitHandle[waitableResults.Length];
579 GetWaitHandles(waitableResults, waitHandles);
580 }
581  
582 int result = STPEventWaitHandle.WaitAny(waitHandles, millisecondsTimeout, exitContext);
583  
584 // Treat cancel as timeout
585 if (null != cancelWaitHandle)
586 {
587 if (result == waitableResults.Length)
588 {
589 result = STPEventWaitHandle.WaitTimeout;
590 }
591 }
592  
593 ReleaseWaitHandles(waitableResults);
594  
595 return result;
596 }
597  
598 /// <summary>
599 /// Fill an array of wait handles with the work items wait handles.
600 /// </summary>
601 /// <param name="waitableResults">An array of work item results</param>
602 /// <param name="waitHandles">An array of wait handles to fill</param>
603 private static void GetWaitHandles(
604 IWaitableResult[] waitableResults,
605 WaitHandle[] waitHandles)
606 {
607 for (int i = 0; i < waitableResults.Length; ++i)
608 {
609 WorkItemResult wir = waitableResults[i].GetWorkItemResult() as WorkItemResult;
610 Debug.Assert(null != wir, "All waitableResults must be WorkItemResult objects");
611  
612 waitHandles[i] = wir.GetWorkItem().GetWaitHandle();
613 }
614 }
615  
616 /// <summary>
617 /// Release the work items' wait handles
618 /// </summary>
619 /// <param name="waitableResults">An array of work item results</param>
620 private static void ReleaseWaitHandles(IWaitableResult[] waitableResults)
621 {
622 for (int i = 0; i < waitableResults.Length; ++i)
623 {
624 WorkItemResult wir = (WorkItemResult)waitableResults[i].GetWorkItemResult();
625  
626 wir.GetWorkItem().ReleaseWaitHandle();
627 }
628 }
629  
630 #endregion
631  
632 #region Private Members
633  
634 private WorkItemState GetWorkItemState()
635 {
636 lock (this)
637 {
638 if (WorkItemState.Completed == _workItemState)
639 {
640 return _workItemState;
641 }
642  
643 long nowTicks = DateTime.UtcNow.Ticks;
644  
645 if (WorkItemState.Canceled != _workItemState && nowTicks > _expirationTime)
646 {
647 _workItemState = WorkItemState.Canceled;
648 }
649  
650 if (WorkItemState.InProgress == _workItemState)
651 {
652 return _workItemState;
653 }
654  
655 if (CanceledSmartThreadPool.IsCanceled || CanceledWorkItemsGroup.IsCanceled)
656 {
657 return WorkItemState.Canceled;
658 }
659  
660 return _workItemState;
661 }
662 }
663  
664  
665 /// <summary>
666 /// Sets the work item's state
667 /// </summary>
668 /// <param name="workItemState">The state to set the work item to</param>
669 private void SetWorkItemState(WorkItemState workItemState)
670 {
671 lock (this)
672 {
673 if (IsValidStatesTransition(_workItemState, workItemState))
674 {
675 _workItemState = workItemState;
676 }
677 }
678 }
679  
680 /// <summary>
681 /// Signals that work item has been completed or canceled
682 /// </summary>
683 /// <param name="canceled">Indicates that the work item has been canceled</param>
684 private void SignalComplete(bool canceled)
685 {
686 SetWorkItemState(canceled ? WorkItemState.Canceled : WorkItemState.Completed);
687 lock (this)
688 {
689 // If someone is waiting then signal.
690 if (null != _workItemCompleted)
691 {
692 _workItemCompleted.Set();
693 }
694 }
695 }
696  
697 internal void WorkItemIsQueued()
698 {
699 _waitingOnQueueStopwatch.Start();
700 }
701  
702 #endregion
703  
704 #region Members exposed by WorkItemResult
705  
706 /// <summary>
707 /// Cancel the work item if it didn't start running yet.
708 /// </summary>
709 /// <returns>Returns true on success or false if the work item is in progress or already completed</returns>
710 private bool Cancel(bool abortExecution)
711 {
712 #if (_WINDOWS_CE)
713 if(abortExecution)
714 {
715 throw new ArgumentOutOfRangeException("abortExecution", "WindowsCE doesn't support this feature");
716 }
717 #endif
718 bool success = false;
719 bool signalComplete = false;
720  
721 lock (this)
722 {
723 switch (GetWorkItemState())
724 {
725 case WorkItemState.Canceled:
726 //Debug.WriteLine("Work item already canceled");
727 if (abortExecution)
728 {
729 Thread executionThread = Interlocked.CompareExchange(ref _executingThread, null, _executingThread);
730 if (null != executionThread)
731 {
732 executionThread.Abort(); // "Cancel"
733 // No need to signalComplete, because we already cancelled this work item
734 // so it already signaled its completion.
735 //signalComplete = true;
736 }
737 }
738 success = true;
739 break;
740 case WorkItemState.Completed:
741 //Debug.WriteLine("Work item cannot be canceled");
742 break;
743 case WorkItemState.InProgress:
744 if (abortExecution)
745 {
746 Thread executionThread = Interlocked.CompareExchange(ref _executingThread, null, _executingThread);
747 if (null != executionThread)
748 {
749 executionThread.Abort(); // "Cancel"
750 success = true;
751 signalComplete = true;
752 }
753 }
754 else
755 {
756 // **************************
757 // Stock SmartThreadPool 2.2.3 sets these to true and relies on the thread to check the
758 // WorkItem cancellation status. However, OpenSimulator uses a different mechanism to notify
759 // scripts of co-operative termination and the abort code also relies on this method
760 // returning false in order to implement a small wait.
761 //
762 // Therefore, as was the case previously with STP, we will not signal successful cancellation
763 // here. It's possible that OpenSimulator code could be changed in the future to remove
764 // the need for this change.
765 // **************************
766 success = false;
767 signalComplete = false;
768 }
769 break;
770 case WorkItemState.InQueue:
771 // Signal to the wait for completion that the work
772 // item has been completed (canceled). There is no
773 // reason to wait for it to get out of the queue
774 signalComplete = true;
775 //Debug.WriteLine("Work item canceled");
776 success = true;
777 break;
778 }
779  
780 if (signalComplete)
781 {
782 SignalComplete(true);
783 }
784 }
785 return success;
786 }
787  
788 /// <summary>
789 /// Get the result of the work item.
790 /// If the work item didn't run yet then the caller waits for the result, timeout, or cancel.
791 /// In case of error the method throws and exception
792 /// </summary>
793 /// <returns>The result of the work item</returns>
794 private object GetResult(
795 int millisecondsTimeout,
796 bool exitContext,
797 WaitHandle cancelWaitHandle)
798 {
799 Exception e;
800 object result = GetResult(millisecondsTimeout, exitContext, cancelWaitHandle, out e);
801 if (null != e)
802 {
803 throw new WorkItemResultException("The work item caused an excpetion, see the inner exception for details", e);
804 }
805 return result;
806 }
807  
808 /// <summary>
809 /// Get the result of the work item.
810 /// If the work item didn't run yet then the caller waits for the result, timeout, or cancel.
811 /// In case of error the e argument is filled with the exception
812 /// </summary>
813 /// <returns>The result of the work item</returns>
814 private object GetResult(
815 int millisecondsTimeout,
816 bool exitContext,
817 WaitHandle cancelWaitHandle,
818 out Exception e)
819 {
820 e = null;
821  
822 // Check for cancel
823 if (WorkItemState.Canceled == GetWorkItemState())
824 {
825 throw new WorkItemCancelException("Work item canceled");
826 }
827  
828 // Check for completion
829 if (IsCompleted)
830 {
831 e = _exception;
832 return _result;
833 }
834  
835 // If no cancelWaitHandle is provided
836 if (null == cancelWaitHandle)
837 {
838 WaitHandle wh = GetWaitHandle();
839  
840 bool timeout = !STPEventWaitHandle.WaitOne(wh, millisecondsTimeout, exitContext);
841  
842 ReleaseWaitHandle();
843  
844 if (timeout)
845 {
846 throw new WorkItemTimeoutException("Work item timeout");
847 }
848 }
849 else
850 {
851 WaitHandle wh = GetWaitHandle();
852 int result = STPEventWaitHandle.WaitAny(new WaitHandle[] { wh, cancelWaitHandle });
853 ReleaseWaitHandle();
854  
855 switch (result)
856 {
857 case 0:
858 // The work item signaled
859 // Note that the signal could be also as a result of canceling the
860 // work item (not the get result)
861 break;
862 case 1:
863 case STPEventWaitHandle.WaitTimeout:
864 throw new WorkItemTimeoutException("Work item timeout");
865 default:
866 Debug.Assert(false);
867 break;
868  
869 }
870 }
871  
872 // Check for cancel
873 if (WorkItemState.Canceled == GetWorkItemState())
874 {
875 throw new WorkItemCancelException("Work item canceled");
876 }
877  
878 Debug.Assert(IsCompleted);
879  
880 e = _exception;
881  
882 // Return the result
883 return _result;
884 }
885  
886 /// <summary>
887 /// A wait handle to wait for completion, cancel, or timeout
888 /// </summary>
889 private WaitHandle GetWaitHandle()
890 {
891 lock (this)
892 {
893 if (null == _workItemCompleted)
894 {
895 _workItemCompleted = EventWaitHandleFactory.CreateManualResetEvent(IsCompleted);
896 }
897 ++_workItemCompletedRefCount;
898 }
899 return _workItemCompleted;
900 }
901  
902 private void ReleaseWaitHandle()
903 {
904 lock (this)
905 {
906 if (null != _workItemCompleted)
907 {
908 --_workItemCompletedRefCount;
909 if (0 == _workItemCompletedRefCount)
910 {
911 _workItemCompleted.Close();
912 _workItemCompleted = null;
913 }
914 }
915 }
916 }
917  
918 /// <summary>
919 /// Returns true when the work item has completed or canceled
920 /// </summary>
921 private bool IsCompleted
922 {
923 get
924 {
925 lock (this)
926 {
927 WorkItemState workItemState = GetWorkItemState();
928 return ((workItemState == WorkItemState.Completed) ||
929 (workItemState == WorkItemState.Canceled));
930 }
931 }
932 }
933  
934 /// <summary>
935 /// Returns true when the work item has canceled
936 /// </summary>
937 public bool IsCanceled
938 {
939 get
940 {
941 lock (this)
942 {
943 return (GetWorkItemState() == WorkItemState.Canceled);
944 }
945 }
946 }
947  
948 #endregion
949  
950 #region IHasWorkItemPriority Members
951  
952 /// <summary>
953 /// Returns the priority of the work item
954 /// </summary>
955 public WorkItemPriority WorkItemPriority
956 {
957 get
958 {
959 return _workItemInfo.WorkItemPriority;
960 }
961 }
962  
963 #endregion
964  
965 internal event WorkItemStateCallback OnWorkItemStarted
966 {
967 add
968 {
969 _workItemStartedEvent += value;
970 }
971 remove
972 {
973 _workItemStartedEvent -= value;
974 }
975 }
976  
977 internal event WorkItemStateCallback OnWorkItemCompleted
978 {
979 add
980 {
981 _workItemCompletedEvent += value;
982 }
983 remove
984 {
985 _workItemCompletedEvent -= value;
986 }
987 }
988  
989 public void DisposeOfState()
990 {
991 if (_workItemInfo.DisposeOfStateObjects)
992 {
993 IDisposable disp = _state as IDisposable;
994 if (null != disp)
995 {
996 disp.Dispose();
997 _state = null;
998 }
999 }
1000 }
1001 }
1002 }