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;
            }
        }