WorkItem.cs 33 KB

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