using System; using System.Threading; namespace CommandMessenger { public class AsyncWorker { public enum WorkerState { Stopped, Running, Suspended } /// /// Main worker method to do some work. /// /// true is there is more work to do, otherwise false and worker will wait until signalled with SignalWorker(). public delegate bool AsyncWorkerJob(); private bool _isFaulted; private volatile WorkerState _state = WorkerState.Stopped; private volatile WorkerState _requestedState = WorkerState.Stopped; private readonly object _lock = new object(); private readonly EventWaiter _eventWaiter = new EventWaiter(); private readonly AsyncWorkerJob _workerJob; private Thread _workerTask; public string Name { get; private set; } public WorkerState State { get { return _state; } } public bool IsRunning { get { return _state == WorkerState.Running; } } public bool IsSuspended { get { return _state == WorkerState.Suspended; } } public AsyncWorker(AsyncWorkerJob workerJob, string workerName = null) { if (workerJob == null) throw new ArgumentNullException("workerJob"); _workerJob = workerJob; Name = workerName; } public void Start() { lock (_lock) { if (_state == WorkerState.Stopped) { _requestedState = _state = WorkerState.Running; _eventWaiter.Reset(); _workerTask = new Thread(() => { while (true) { if (_state == WorkerState.Stopped) break; bool haveMoreWork = false; if (_state == WorkerState.Running) { try { haveMoreWork = _workerJob(); } catch { _requestedState = _state = WorkerState.Stopped; _isFaulted = true; throw; } // Check if state has been changed in workerJob thread. if (_requestedState != _state && _requestedState == WorkerState.Stopped) { _state = _requestedState; break; } } if (!haveMoreWork || _state == WorkerState.Suspended) _eventWaiter.WaitOne(Timeout.Infinite); _state = _requestedState; } }); _workerTask.Name = Name; _workerTask.IsBackground = true; _workerTask.Start(); SpinWait.SpinUntil(() => _workerTask.IsAlive); } else { throw new InvalidOperationException("The worker is already started."); } } } public void Stop() { lock (_lock) { if (_state == WorkerState.Running || _state == WorkerState.Suspended) { _requestedState = WorkerState.Stopped; // Prevent deadlock by checking is we stopping from worker task or not. if (Thread.CurrentThread.ManagedThreadId != _workerTask.ManagedThreadId) { _eventWaiter.Set(); _workerTask.Join(); } } else if (!_isFaulted) { // Probably not needed, added as a precaution. throw new InvalidOperationException("The worker is already stopped."); } } } public void Suspend() { lock (_lock) { if (_state == WorkerState.Running) { _requestedState = WorkerState.Suspended; _eventWaiter.Set(); SpinWait.SpinUntil(() => _requestedState == _state); } else { // Probably not needed, added as a precaution. throw new InvalidOperationException("The worker is not running."); } } } public void Resume() { lock (_lock) { if (_state == WorkerState.Suspended) { _requestedState = WorkerState.Running; _eventWaiter.Set(); SpinWait.SpinUntil(() => _requestedState == _state); } else { // Probably not needed, added as a precaution. throw new InvalidOperationException("The worker is not in suspended state."); } } } /// /// Signal worker to continue processing. /// public void Signal() { if (IsRunning) _eventWaiter.Set(); } } }