WorkItem.cs 34 KB

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