123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354 |
- using System;
- using System.Collections.Generic;
- using System.Threading;
- using System.Runtime.CompilerServices;
- using System.Diagnostics;
- namespace Amib.Threading.Internal
- {
- #region WorkItemsGroup class
- /// <summary>
- /// Summary description for WorkItemsGroup.
- /// </summary>
- public class WorkItemsGroup : WorkItemsGroupBase
- {
- #region Private members
- private readonly object _lock = new();
- /// <summary>
- /// A reference to the SmartThreadPool instance that created this
- /// WorkItemsGroup.
- /// </summary>
- private readonly SmartThreadPool _stp;
- /// <summary>
- /// The OnIdle event
- /// </summary>
- private event WorkItemsGroupIdleHandler _onIdle;
- /// <summary>
- /// A flag to indicate if the Work Items Group is now suspended.
- /// </summary>
- private bool _isSuspended;
- /// <summary>
- /// Defines how many work items of this WorkItemsGroup can run at once.
- /// </summary>
- private int _concurrency;
- /// <summary>
- /// Priority queue to hold work items before they are passed
- /// to the SmartThreadPool.
- /// </summary>
- private readonly Queue<WorkItem> _workItemsQueue;
- /// <summary>
- /// Indicate how many work items are waiting in the SmartThreadPool
- /// queue.
- /// This value is used to apply the concurrency.
- /// </summary>
- private int _workItemsInStpQueue;
- /// <summary>
- /// Indicate how many work items are currently running in the SmartThreadPool.
- /// This value is used with the Cancel, to calculate if we can send new
- /// work items to the STP.
- /// </summary>
- private int _workItemsExecutingInStp = 0;
- /// <summary>
- /// WorkItemsGroup start information
- /// </summary>
- private readonly WIGStartInfo _workItemsGroupStartInfo;
- /// <summary>
- /// Signaled when all of the WorkItemsGroup's work item completed.
- /// </summary>
- private readonly ManualResetEvent _isIdleWaitHandle = new(true);
- /// <summary>
- /// A common object for all the work items that this work items group
- /// generate so we can mark them to cancel in O(1)
- /// </summary>
- private CanceledWorkItemsGroup _canceledWorkItemsGroup = new();
- #endregion
- #region Construction
- public WorkItemsGroup(SmartThreadPool stp, int concurrency, WIGStartInfo wigStartInfo)
- {
- if (concurrency <= 0)
- {
- throw new ArgumentOutOfRangeException(
- "concurrency",
- concurrency,
- "concurrency must be greater than zero");
- }
- _stp = stp;
- _concurrency = concurrency;
- _workItemsGroupStartInfo = new WIGStartInfo(wigStartInfo).AsReadOnly();
- _workItemsQueue = new Queue<WorkItem>();
- Name = "WorkItemsGroup";
- // The _workItemsInStpQueue gets the number of currently executing work items,
- // because once a work item is executing, it cannot be cancelled.
- _workItemsInStpQueue = _workItemsExecutingInStp;
- _isSuspended = _workItemsGroupStartInfo.StartSuspended;
- }
- #endregion
- #region WorkItemsGroupBase Overrides
- public override int Concurrency
- {
- get { return _concurrency; }
- set
- {
- Debug.Assert(value > 0);
- int diff = value - _concurrency;
- _concurrency = value;
- if (diff > 0)
- {
- EnqueueToSTPNextNWorkItem(diff);
- }
- }
- }
- public override int WaitingCallbacks
- {
- get { return _workItemsQueue.Count; }
- }
- public override object[] GetStates()
- {
- lock (_lock)
- {
- object[] states = new object[_workItemsQueue.Count];
- int i = 0;
- foreach (WorkItem workItem in _workItemsQueue)
- {
- states[i] = workItem.GetWorkItemResult().State;
- ++i;
- }
- return states;
- }
- }
- /// <summary>
- /// WorkItemsGroup start information
- /// </summary>
- public override WIGStartInfo WIGStartInfo
- {
- get { return _workItemsGroupStartInfo; }
- }
- /// <summary>
- /// Start the Work Items Group if it was started suspended
- /// </summary>
- public override void Start()
- {
- // If the Work Items Group already started then quit
- if (!_isSuspended)
- {
- return;
- }
- _isSuspended = false;
-
- EnqueueToSTPNextNWorkItem(Math.Min(_workItemsQueue.Count, _concurrency));
- }
- public override void Cancel(bool abortExecution)
- {
- lock (_lock)
- {
- _canceledWorkItemsGroup.IsCanceled = true;
- _workItemsQueue.Clear();
- _workItemsInStpQueue = 0;
- _canceledWorkItemsGroup = new CanceledWorkItemsGroup();
- }
- if (abortExecution)
- {
- _stp.CancelAbortWorkItemsGroup(this);
- }
- }
- /// <summary>
- /// Wait for the thread pool to be idle
- /// </summary>
- public override bool WaitForIdle(int millisecondsTimeout)
- {
- SmartThreadPool.ValidateWorkItemsGroupWaitForIdle(this);
- return STPEventWaitHandle.WaitOne(_isIdleWaitHandle, millisecondsTimeout, false);
- }
- public override event WorkItemsGroupIdleHandler OnIdle
- {
- add { _onIdle += value; }
- remove { _onIdle -= value; }
- }
- #endregion
- #region Private methods
- private void RegisterToWorkItemCompletion(IWorkItemResult wir)
- {
- IInternalWorkItemResult iwir = (IInternalWorkItemResult)wir;
- iwir.OnWorkItemStarted += OnWorkItemStartedCallback;
- iwir.OnWorkItemCompleted += OnWorkItemCompletedCallback;
- }
- public void OnSTPIsStarting()
- {
- if (_isSuspended)
- {
- return;
- }
-
- EnqueueToSTPNextNWorkItem(_concurrency);
- }
- public void EnqueueToSTPNextNWorkItem(int count)
- {
- for (int i = 0; i < count; ++i)
- {
- EnqueueToSTPNextWorkItem(null, false);
- }
- }
- private object FireOnIdle(object state)
- {
- FireOnIdleImpl(_onIdle);
- return null;
- }
- [MethodImpl(MethodImplOptions.NoInlining)]
- private void FireOnIdleImpl(WorkItemsGroupIdleHandler onIdle)
- {
- if(onIdle is null)
- return;
- Delegate[] delegates = onIdle.GetInvocationList();
- foreach(WorkItemsGroupIdleHandler eh in delegates)
- {
- try
- {
- eh(this);
- }
- catch { } // Suppress exceptions
- }
- }
- private void OnWorkItemStartedCallback(WorkItem workItem)
- {
- lock(_lock)
- {
- ++_workItemsExecutingInStp;
- }
- }
- private void OnWorkItemCompletedCallback(WorkItem workItem)
- {
- EnqueueToSTPNextWorkItem(null, true);
- }
- internal override void Enqueue(WorkItem workItem)
- {
- EnqueueToSTPNextWorkItem(workItem);
- }
- private void EnqueueToSTPNextWorkItem(WorkItem workItem)
- {
- EnqueueToSTPNextWorkItem(workItem, false);
- }
- private void EnqueueToSTPNextWorkItem(WorkItem workItem, bool decrementWorkItemsInStpQueue)
- {
- lock(_lock)
- {
- // Got here from OnWorkItemCompletedCallback()
- if (decrementWorkItemsInStpQueue)
- {
- --_workItemsInStpQueue;
- if(_workItemsInStpQueue < 0)
- {
- _workItemsInStpQueue = 0;
- }
- --_workItemsExecutingInStp;
- if(_workItemsExecutingInStp < 0)
- {
- _workItemsExecutingInStp = 0;
- }
- }
- // If the work item is not null then enqueue it
- if (workItem is not null)
- {
- workItem.CanceledWorkItemsGroup = _canceledWorkItemsGroup;
- RegisterToWorkItemCompletion(workItem.GetWorkItemResult());
- _workItemsQueue.Enqueue(workItem);
- //_stp.IncrementWorkItemsCount();
- if ((1 == _workItemsQueue.Count) &&
- (0 == _workItemsInStpQueue))
- {
- _stp.RegisterWorkItemsGroup(this);
- IsIdle = false;
- _isIdleWaitHandle.Reset();
- }
- }
- // If the work items queue of the group is empty than quit
- if (0 == _workItemsQueue.Count)
- {
- if (0 == _workItemsInStpQueue)
- {
- _stp.UnregisterWorkItemsGroup(this);
- IsIdle = true;
- _isIdleWaitHandle.Set();
- if (decrementWorkItemsInStpQueue && _onIdle is not null && _onIdle.GetInvocationList().Length > 0)
- {
- _stp.QueueWorkItem(new WorkItemCallback(FireOnIdle));
- }
- }
- return;
- }
- if (!_isSuspended)
- {
- if (_workItemsInStpQueue < _concurrency)
- {
- WorkItem nextWorkItem = _workItemsQueue.Dequeue();
- try
- {
- _stp.Enqueue(nextWorkItem);
- }
- catch (ObjectDisposedException e)
- {
- e.GetHashCode();
- // The STP has been shutdown
- }
- ++_workItemsInStpQueue;
- }
- }
- }
- }
- #endregion
- }
- #endregion
- }
|