/OpenSim/Tools/SmartThreadPool/WorkItemsGroup.cs

http://github.com/aurora-sim/Aurora-Sim · C# · 510 lines · 294 code · 63 blank · 153 comment · 21 complexity · 56ea3d7cf33988468ca989e78c01a314 MD5 · raw file

  1. // Ami Bar
  2. // amibar@gmail.com
  3. using System;
  4. using System.Diagnostics;
  5. using System.Runtime.CompilerServices;
  6. using System.Threading;
  7. namespace Amib.Threading.Internal
  8. {
  9. #region WorkItemsGroup class
  10. /// <summary>
  11. /// Summary description for WorkItemsGroup.
  12. /// </summary>
  13. public class WorkItemsGroup : IWorkItemsGroup
  14. {
  15. #region Private members
  16. /// <summary>
  17. /// Defines how many work items of this WorkItemsGroup can run at once.
  18. /// </summary>
  19. private readonly int _concurrency;
  20. /// <summary>
  21. /// Signaled when all of the WorkItemsGroup's work item completed.
  22. /// </summary>
  23. private readonly ManualResetEvent _isIdleWaitHandle = new ManualResetEvent(true);
  24. private readonly object _lock = new object();
  25. /// <summary>
  26. /// A reference to the SmartThreadPool instance that created this
  27. /// WorkItemsGroup.
  28. /// </summary>
  29. private readonly SmartThreadPool _stp;
  30. /// <summary>
  31. /// WorkItemsGroup start information
  32. /// </summary>
  33. private readonly WIGStartInfo _workItemsGroupStartInfo;
  34. /// <summary>
  35. /// Priority queue to hold work items before they are passed
  36. /// to the SmartThreadPool.
  37. /// </summary>
  38. private readonly PriorityQueue _workItemsQueue;
  39. /// <summary>
  40. /// A common object for all the work items that this work items group
  41. /// generate so we can mark them to cancel in O(1)
  42. /// </summary>
  43. private CanceledWorkItemsGroup _canceledWorkItemsGroup = new CanceledWorkItemsGroup();
  44. /// <summary>
  45. /// Contains the name of this instance of SmartThreadPool.
  46. /// Can be changed by the user.
  47. /// </summary>
  48. private string _name = "WorkItemsGroup";
  49. /// <summary>
  50. /// Indicate how many work items are currently running in the SmartThreadPool.
  51. /// This value is used with the Cancel, to calculate if we can send new
  52. /// work items to the STP.
  53. /// </summary>
  54. private int _workItemsExecutingInStp;
  55. /// <summary>
  56. /// Indicate how many work items are waiting in the SmartThreadPool
  57. /// queue.
  58. /// This value is used to apply the concurrency.
  59. /// </summary>
  60. private int _workItemsInStpQueue;
  61. /// <summary>
  62. /// The OnIdle event
  63. /// </summary>
  64. private event WorkItemsGroupIdleHandler _onIdle;
  65. #endregion
  66. #region Construction
  67. public WorkItemsGroup(
  68. SmartThreadPool stp,
  69. int concurrency,
  70. WIGStartInfo wigStartInfo)
  71. {
  72. if (concurrency <= 0)
  73. {
  74. throw new ArgumentOutOfRangeException("concurrency", concurrency,
  75. "concurrency must be greater than zero");
  76. }
  77. _stp = stp;
  78. _concurrency = concurrency;
  79. _workItemsGroupStartInfo = new WIGStartInfo(wigStartInfo);
  80. _workItemsQueue = new PriorityQueue();
  81. // The _workItemsInStpQueue gets the number of currently executing work items,
  82. // because once a work item is executing, it cannot be cancelled.
  83. _workItemsInStpQueue = _workItemsExecutingInStp;
  84. }
  85. #endregion
  86. #region IWorkItemsGroup implementation
  87. /// <summary>
  88. /// Get/Set the name of the SmartThreadPool instance
  89. /// </summary>
  90. public string Name
  91. {
  92. get { return _name; }
  93. set { _name = value; }
  94. }
  95. /// <summary>
  96. /// Queue a work item
  97. /// </summary>
  98. /// <param name = "callback">A callback to execute</param>
  99. /// <returns>Returns a work item result</returns>
  100. public IWorkItemResult QueueWorkItem(WorkItemCallback callback)
  101. {
  102. WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _workItemsGroupStartInfo, callback);
  103. EnqueueToSTPNextWorkItem(workItem);
  104. return workItem.GetWorkItemResult();
  105. }
  106. /// <summary>
  107. /// Queue a work item
  108. /// </summary>
  109. /// <param name = "callback">A callback to execute</param>
  110. /// <param name = "workItemPriority">The priority of the work item</param>
  111. /// <returns>Returns a work item result</returns>
  112. public IWorkItemResult QueueWorkItem(WorkItemCallback callback, WorkItemPriority workItemPriority)
  113. {
  114. WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _workItemsGroupStartInfo, callback,
  115. workItemPriority);
  116. EnqueueToSTPNextWorkItem(workItem);
  117. return workItem.GetWorkItemResult();
  118. }
  119. /// <summary>
  120. /// Queue a work item
  121. /// </summary>
  122. /// <param name = "workItemInfo">Work item info</param>
  123. /// <param name = "callback">A callback to execute</param>
  124. /// <returns>Returns a work item result</returns>
  125. public IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback)
  126. {
  127. WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _workItemsGroupStartInfo, workItemInfo, callback);
  128. EnqueueToSTPNextWorkItem(workItem);
  129. return workItem.GetWorkItemResult();
  130. }
  131. /// <summary>
  132. /// Queue a work item
  133. /// </summary>
  134. /// <param name = "callback">A callback to execute</param>
  135. /// <param name = "state">
  136. /// The context object of the work item. Used for passing arguments to the work item.
  137. /// </param>
  138. /// <returns>Returns a work item result</returns>
  139. public IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state)
  140. {
  141. WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _workItemsGroupStartInfo, callback, state);
  142. EnqueueToSTPNextWorkItem(workItem);
  143. return workItem.GetWorkItemResult();
  144. }
  145. /// <summary>
  146. /// Queue a work item
  147. /// </summary>
  148. /// <param name = "callback">A callback to execute</param>
  149. /// <param name = "state">
  150. /// The context object of the work item. Used for passing arguments to the work item.
  151. /// </param>
  152. /// <param name = "workItemPriority">The work item priority</param>
  153. /// <returns>Returns a work item result</returns>
  154. public IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, WorkItemPriority workItemPriority)
  155. {
  156. WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _workItemsGroupStartInfo, callback, state,
  157. workItemPriority);
  158. EnqueueToSTPNextWorkItem(workItem);
  159. return workItem.GetWorkItemResult();
  160. }
  161. /// <summary>
  162. /// Queue a work item
  163. /// </summary>
  164. /// <param name = "workItemInfo">Work item information</param>
  165. /// <param name = "callback">A callback to execute</param>
  166. /// <param name = "state">
  167. /// The context object of the work item. Used for passing arguments to the work item.
  168. /// </param>
  169. /// <returns>Returns a work item result</returns>
  170. public IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback, object state)
  171. {
  172. WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _workItemsGroupStartInfo, workItemInfo, callback,
  173. state);
  174. EnqueueToSTPNextWorkItem(workItem);
  175. return workItem.GetWorkItemResult();
  176. }
  177. /// <summary>
  178. /// Queue a work item
  179. /// </summary>
  180. /// <param name = "callback">A callback to execute</param>
  181. /// <param name = "state">
  182. /// The context object of the work item. Used for passing arguments to the work item.
  183. /// </param>
  184. /// <param name = "postExecuteWorkItemCallback">
  185. /// A delegate to call after the callback completion
  186. /// </param>
  187. /// <returns>Returns a work item result</returns>
  188. public IWorkItemResult QueueWorkItem(
  189. WorkItemCallback callback,
  190. object state,
  191. PostExecuteWorkItemCallback postExecuteWorkItemCallback)
  192. {
  193. WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _workItemsGroupStartInfo, callback, state,
  194. postExecuteWorkItemCallback);
  195. EnqueueToSTPNextWorkItem(workItem);
  196. return workItem.GetWorkItemResult();
  197. }
  198. /// <summary>
  199. /// Queue a work item
  200. /// </summary>
  201. /// <param name = "callback">A callback to execute</param>
  202. /// <param name = "state">
  203. /// The context object of the work item. Used for passing arguments to the work item.
  204. /// </param>
  205. /// <param name = "postExecuteWorkItemCallback">
  206. /// A delegate to call after the callback completion
  207. /// </param>
  208. /// <param name = "workItemPriority">The work item priority</param>
  209. /// <returns>Returns a work item result</returns>
  210. public IWorkItemResult QueueWorkItem(
  211. WorkItemCallback callback,
  212. object state,
  213. PostExecuteWorkItemCallback postExecuteWorkItemCallback,
  214. WorkItemPriority workItemPriority)
  215. {
  216. WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _workItemsGroupStartInfo, callback, state,
  217. postExecuteWorkItemCallback, workItemPriority);
  218. EnqueueToSTPNextWorkItem(workItem);
  219. return workItem.GetWorkItemResult();
  220. }
  221. /// <summary>
  222. /// Queue a work item
  223. /// </summary>
  224. /// <param name = "callback">A callback to execute</param>
  225. /// <param name = "state">
  226. /// The context object of the work item. Used for passing arguments to the work item.
  227. /// </param>
  228. /// <param name = "postExecuteWorkItemCallback">
  229. /// A delegate to call after the callback completion
  230. /// </param>
  231. /// <param name = "callToPostExecute">Indicates on which cases to call to the post execute callback</param>
  232. /// <returns>Returns a work item result</returns>
  233. public IWorkItemResult QueueWorkItem(
  234. WorkItemCallback callback,
  235. object state,
  236. PostExecuteWorkItemCallback postExecuteWorkItemCallback,
  237. CallToPostExecute callToPostExecute)
  238. {
  239. WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _workItemsGroupStartInfo, callback, state,
  240. postExecuteWorkItemCallback, callToPostExecute);
  241. EnqueueToSTPNextWorkItem(workItem);
  242. return workItem.GetWorkItemResult();
  243. }
  244. /// <summary>
  245. /// Queue a work item
  246. /// </summary>
  247. /// <param name = "callback">A callback to execute</param>
  248. /// <param name = "state">
  249. /// The context object of the work item. Used for passing arguments to the work item.
  250. /// </param>
  251. /// <param name = "postExecuteWorkItemCallback">
  252. /// A delegate to call after the callback completion
  253. /// </param>
  254. /// <param name = "callToPostExecute">Indicates on which cases to call to the post execute callback</param>
  255. /// <param name = "workItemPriority">The work item priority</param>
  256. /// <returns>Returns a work item result</returns>
  257. public IWorkItemResult QueueWorkItem(
  258. WorkItemCallback callback,
  259. object state,
  260. PostExecuteWorkItemCallback postExecuteWorkItemCallback,
  261. CallToPostExecute callToPostExecute,
  262. WorkItemPriority workItemPriority)
  263. {
  264. WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _workItemsGroupStartInfo, callback, state,
  265. postExecuteWorkItemCallback, callToPostExecute,
  266. workItemPriority);
  267. EnqueueToSTPNextWorkItem(workItem);
  268. return workItem.GetWorkItemResult();
  269. }
  270. /// <summary>
  271. /// Wait for the thread pool to be idle
  272. /// </summary>
  273. public void WaitForIdle()
  274. {
  275. WaitForIdle(Timeout.Infinite);
  276. }
  277. /// <summary>
  278. /// Wait for the thread pool to be idle
  279. /// </summary>
  280. public bool WaitForIdle(TimeSpan timeout)
  281. {
  282. return WaitForIdle((int) timeout.TotalMilliseconds);
  283. }
  284. /// <summary>
  285. /// Wait for the thread pool to be idle
  286. /// </summary>
  287. public bool WaitForIdle(int millisecondsTimeout)
  288. {
  289. _stp.ValidateWorkItemsGroupWaitForIdle(this);
  290. return _isIdleWaitHandle.WaitOne(millisecondsTimeout, false);
  291. }
  292. public int WaitingCallbacks
  293. {
  294. get { return _workItemsQueue.Count; }
  295. }
  296. public event WorkItemsGroupIdleHandler OnIdle
  297. {
  298. add { _onIdle += value; }
  299. remove { _onIdle -= value; }
  300. }
  301. public void Cancel()
  302. {
  303. lock (_lock)
  304. {
  305. _canceledWorkItemsGroup.IsCanceled = true;
  306. _workItemsQueue.Clear();
  307. _workItemsInStpQueue = 0;
  308. _canceledWorkItemsGroup = new CanceledWorkItemsGroup();
  309. }
  310. }
  311. public void Start()
  312. {
  313. lock (this)
  314. {
  315. if (!_workItemsGroupStartInfo.StartSuspended)
  316. {
  317. return;
  318. }
  319. _workItemsGroupStartInfo.StartSuspended = false;
  320. }
  321. for (int i = 0; i < _concurrency; ++i)
  322. {
  323. EnqueueToSTPNextWorkItem(null, false);
  324. }
  325. }
  326. #endregion
  327. #region Private methods
  328. private void RegisterToWorkItemCompletion(IWorkItemResult wir)
  329. {
  330. IInternalWorkItemResult iwir = wir as IInternalWorkItemResult;
  331. iwir.OnWorkItemStarted += OnWorkItemStartedCallback;
  332. iwir.OnWorkItemCompleted += OnWorkItemCompletedCallback;
  333. }
  334. public void OnSTPIsStarting()
  335. {
  336. lock (this)
  337. {
  338. if (_workItemsGroupStartInfo.StartSuspended)
  339. {
  340. return;
  341. }
  342. }
  343. for (int i = 0; i < _concurrency; ++i)
  344. {
  345. EnqueueToSTPNextWorkItem(null, false);
  346. }
  347. }
  348. private object FireOnIdle(object state)
  349. {
  350. FireOnIdleImpl(_onIdle);
  351. return null;
  352. }
  353. [MethodImpl(MethodImplOptions.NoInlining)]
  354. private void FireOnIdleImpl(WorkItemsGroupIdleHandler onIdle)
  355. {
  356. if (null == onIdle)
  357. {
  358. return;
  359. }
  360. Delegate[] delegates = onIdle.GetInvocationList();
  361. foreach (WorkItemsGroupIdleHandler eh in delegates)
  362. {
  363. try
  364. {
  365. eh(this);
  366. }
  367. // Ignore exceptions
  368. catch
  369. {
  370. }
  371. }
  372. }
  373. private void OnWorkItemStartedCallback(WorkItem workItem)
  374. {
  375. lock (_lock)
  376. {
  377. ++_workItemsExecutingInStp;
  378. }
  379. }
  380. private void OnWorkItemCompletedCallback(WorkItem workItem)
  381. {
  382. EnqueueToSTPNextWorkItem(null, true);
  383. }
  384. private void EnqueueToSTPNextWorkItem(WorkItem workItem)
  385. {
  386. EnqueueToSTPNextWorkItem(workItem, false);
  387. }
  388. private void EnqueueToSTPNextWorkItem(WorkItem workItem, bool decrementWorkItemsInStpQueue)
  389. {
  390. lock (_lock)
  391. {
  392. // Got here from OnWorkItemCompletedCallback()
  393. if (decrementWorkItemsInStpQueue)
  394. {
  395. --_workItemsInStpQueue;
  396. if (_workItemsInStpQueue < 0)
  397. {
  398. _workItemsInStpQueue = 0;
  399. }
  400. --_workItemsExecutingInStp;
  401. if (_workItemsExecutingInStp < 0)
  402. {
  403. _workItemsExecutingInStp = 0;
  404. }
  405. }
  406. // If the work item is not null then enqueue it
  407. if (null != workItem)
  408. {
  409. workItem.CanceledWorkItemsGroup = _canceledWorkItemsGroup;
  410. RegisterToWorkItemCompletion(workItem.GetWorkItemResult());
  411. _workItemsQueue.Enqueue(workItem);
  412. //_stp.IncrementWorkItemsCount();
  413. if ((1 == _workItemsQueue.Count) &&
  414. (0 == _workItemsInStpQueue))
  415. {
  416. _stp.RegisterWorkItemsGroup(this);
  417. Trace.WriteLine("WorkItemsGroup " + Name + " is NOT idle");
  418. _isIdleWaitHandle.Reset();
  419. }
  420. }
  421. // If the work items queue of the group is empty than quit
  422. if (0 == _workItemsQueue.Count)
  423. {
  424. if (0 == _workItemsInStpQueue)
  425. {
  426. _stp.UnregisterWorkItemsGroup(this);
  427. Trace.WriteLine("WorkItemsGroup " + Name + " is idle");
  428. _isIdleWaitHandle.Set();
  429. _stp.QueueWorkItem(this.FireOnIdle);
  430. }
  431. return;
  432. }
  433. if (!_workItemsGroupStartInfo.StartSuspended)
  434. {
  435. if (_workItemsInStpQueue < _concurrency)
  436. {
  437. WorkItem nextWorkItem = _workItemsQueue.Dequeue() as WorkItem;
  438. _stp.Enqueue(nextWorkItem, true);
  439. ++_workItemsInStpQueue;
  440. }
  441. }
  442. }
  443. }
  444. #endregion
  445. }
  446. #endregion
  447. }