WorkItem.cs 33 KB

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