public void QueueOp(AssetOp op) { if (_threadAborted) { throw new Exception("Thread has already been aborted for this op manager!"); } op.SetStatus(OpStatus.Queued); _opQueue.Enqueue(op); OpStatusChanged?.Invoke(this, op); lock (_threadLock) { if (_thread != null && !_thread.IsAlive) { Log.LogErr("Thread in opmanager was stopped but not null!"); try { _thread.Abort(); _opQueue.Where(x => x.Status == OpStatus.Started).ToList().ForEach(x => { x.SetStatus(OpStatus.Failed, new Exception("Op was in a Started state when the thread died. Failing it.")); Log.LogErr("Op was found in a started state while the thread had died!"); }); _thread = null; } catch { } } if (_thread == null) { _thread = new Thread(WorkerThreadProc); _thread.Start(); } } _queueEvent.Set(); }
private void WorkerThreadProc(object o) { bool entered = false; //I *think* this covers all the race conditions where the thread would stop as an item was being enqueued, probably need to re-review try { do { if (_threadAborted) { Log.LogMsg("Opmanager worker thread has been aborted"); return; } AssetOp op = null; bool dequeued = false; bool threadAborting = false; dequeued = _opQueue.TryDequeue(out op); if (!dequeued) { if (entered) { Log.LogMsg("trying to exit on thread " + Thread.CurrentThread.ManagedThreadId); Monitor.Exit(_context.Manager); entered = false; } _queueEvent.WaitOne(2000); continue; } if (!entered) { entered = Monitor.TryEnter(_context.Manager); if (!entered) { continue; } Log.LogMsg("entered on thread " + Thread.CurrentThread.ManagedThreadId); } try { Stopwatch sw = new Stopwatch(); op.SetStatus(OpStatus.Started); OpStatusChanged?.Invoke(this, op); Log.LogMsg($"AssetOpManager starting op of type {op.GetType().Name}"); sw.Start(); op.PerformOp(_context); sw.Stop(); Log.LogMsg($"AssetOpManager completed op of type {op.GetType().Name} in {sw.ElapsedMilliseconds}ms"); op.SetStatus(OpStatus.Complete); OpStatusChanged?.Invoke(this, op); } catch (ThreadAbortException) { //just die if being aborted _thread = null; threadAborting = true; Log.LogMsg("Op thread aborting"); return; } catch (Exception ex) { Log.LogErr($"Error handling Op type {op.GetType()}, operation threw an exception.", ex); op.SetStatus(OpStatus.Failed, ex); } finally { if (!threadAborting) { OpStatusChanged?.Invoke(this, op); } } } while (true); } catch (ThreadAbortException) { //Thread aborting, so let it Log.LogErr("Op manager thread is aborting"); _thread = null; return; } catch (Exception ex) { Log.LogErr("Exception in op manager's worker thread proc!", ex); } finally { if (entered) { Monitor.Exit(_context.Manager); } //just to make sure it gets set null in all cases _thread = null; } }