WorkItem.cs 34 KB

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