WorkItem.cs 34 KB

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