Example #1
0
        /// <summary>
        /// Runs an action asynchronously.  No assumption is made about the identity under which the action is run.
        /// </summary>
        /// <param name="action">The action to perform asynchronously.</param>
        /// <param name="exceptionCallback">An optional second action to call if the first action throws an exception.</param>
        /// <returns><b>true</b> if the item was scheduled to run asynchronously, or <b>false</b> if no worker threads were available and the operation was run synchronously.</returns>
        /// <remarks>Does not throw if the specified action throws an exception.</remarks>
        public bool RunAsync(Action work, Action <Exception> exceptionCallback = null)
        {
Retry:
            bool synchronous;
            // try to get a ready thread
            Worker worker = _readyWorkerList.Pop();

            System.Diagnostics.Debug.Assert(worker == null || !worker.IsBusy);
            // no ready workers and not too many workers already?
            if ((synchronous = (worker == null)))
            {
                // too many busy workers already?
                if (_busyWorkers > MaxWorkerThreads)
                {
                    // just wait for things to change--we're overloaded and we're not going to make things better by starting up more threads!
                    System.Threading.Thread.Sleep(100);
                    goto Retry;
                }
                // create a new one right now--it will be added to the ready worker list when we're done
                worker = CreateWorker();
                // record this miss
                System.Threading.Interlocked.Exchange(ref _lastSynchronousExecutionTicks, Environment.TickCount);
                // wake the pool master immediately
                _wakePoolMasterThread.Set();
            }
            worker.RunAsync(
                delegate()
            {
                // we now have a worker that is busy
                System.Threading.Interlocked.Increment(ref _busyWorkers);
                // keep track of the maximum concurrent usage
                InterlockedHelper.Max(ref _peakConcurrentUsageSinceLastRetirementCheck, _busyWorkers);
                try
                {
                    work();
                }
                catch (Exception ex)
                {
                    if (exceptionCallback != null)
                    {
                        exceptionCallback(ex);
                    }
                    else
                    {
                        Program.Error("Exception running '" + _poolName + "' pool async operation with no exception handler", ex.ToString());
                    }
                }
                finally
                {
                    // the worker is no longer busy
                    System.Threading.Interlocked.Decrement(ref _busyWorkers);
                }
            });
            return(!synchronous);
        }
Example #2
0
 private void PoolMaster()
 {
     RunWithLogException(
         () =>
     {
         int lastRetirementTicks = Environment.TickCount;
         int lastCreationTicks   = Environment.TickCount;
         // loop forever (we're a background thread, so if the process exits, no problem!)
         for (bool stop = false; !stop;)
         {
             RunWithLogException(
                 () =>
             {
                 // get the wakt pool master signal first before we check to see if we've been disposed
                 ManualResetEvent wakePoolMasterThread = _wakePoolMasterThread;
                 // are we being disposed of?
                 if (_disposing != 0)
                 {
                     // exit now!
                     stop = true;
                     return;
                 }
                 // sleep for up to one second or until we are awakened
                 if (wakePoolMasterThread.WaitOne(1000))
                 {
                     wakePoolMasterThread.Reset();
                 }
                 // are we being disposed of?
                 if (_disposing != 0)
                 {
                     // exit now!
                     stop = true;
                     return;
                 }
                 // do we need to add more workers?
                 int totalWorkers = _workers;
                 int readyWorkers = _readyWorkerList.Count;
                 int busyWorkers  = _busyWorkers;
                 if (readyWorkers <= Math.Min(1, PoolChunkSize / 2))
                 {
                     // too many workers already?
                     if (totalWorkers > MaxWorkerThreads)
                     {
                         // have we NOT already logged that we are using an insane number of threads in the past 60 minutes?
                         int now         = Environment.TickCount;
                         int lastWarning = _highThreadsWarningTickCount;
                         if (TimeElapsed(lastWarning, now) > HighThreadCountWarningEnvironmentTicks)
                         {
                             // race to log a warning--did we win the race?
                             if (System.Threading.Interlocked.CompareExchange(ref _highThreadsWarningTickCount, now, lastWarning) == lastWarning)
                             {
                                 Program.LogVerbose("'" + _poolName + "' Worker Pool Warning: There are already " + (readyWorkers + busyWorkers).ToString() + " workers in this pool.  Given the number of CPUs on this computer, workers are no longer added after around " + MaxWorkerThreads.ToString() + "!");
                             }
                         }
                         // now we will just carry on because we will not expand beyond the number of threads we have now
                     }
                     else
                     {
                         // initialize some workers
                         for (int i = 0; i < PoolChunkSize; ++i)
                         {
                             // create a new worker
                             Worker worker = CreateWorker();
                             System.Diagnostics.Debug.Assert(!worker.IsBusy);
                             _readyWorkerList.Push(worker);
                         }
                         // update the high water mark if needed
                         InterlockedHelper.Max(ref _workersHighWaterMark, _workers);
                         System.Diagnostics.Debug.WriteLine(_poolName + " workers:" + _workers.ToString());
                         // record the expansion time so we don't retire anything for at least a minute
                         lastCreationTicks = Environment.TickCount;
                     }
                 }
                 // enough ready workers that we could get by with less
                 else if (readyWorkers > Math.Max(3, PoolChunkSize * 2))
                 {
                     int ticksNow                       = Environment.TickCount;
                     int ticksSinceCreation             = TimeElapsed(lastCreationTicks, ticksNow);
                     int ticksSinceSynchronousExecution = TimeElapsed(_lastSynchronousExecutionTicks, ticksNow);
                     int ticksSinceRetirement           = TimeElapsed(lastRetirementTicks, ticksNow);
                     bool retireThreadsImmediately      = !_retireThreads.WaitOne(0);
                     // haven't had a synchronous execution or new worker creation or removed any workers for a while?
                     if (retireThreadsImmediately || (
                             ticksSinceCreation > RetireCheckAfterCreationMilliseconds &&
                             ticksSinceSynchronousExecution > RetireCheckAfterCreationMilliseconds &&
                             ticksSinceRetirement > RetireCheckMilliseconds
                             ))
                     {
                         // was the highest concurrency since the last time we checked such that we didn't need many of the threads?
                         if (retireThreadsImmediately || _peakConcurrentUsageSinceLastRetirementCheck < Math.Max(1, totalWorkers - PoolChunkSize))
                         {
                             // remove some workers
                             for (int i = 0; i < PoolChunkSize; ++i)
                             {
                                 // retire one worker--did we remove too many (this could happen if there was a sudden upsurge in worker need while we were stopping workers)
                                 if (!RetireOneWorker())
                                 {
                                     // bail out now--we removed too many (this could happen if there was a sudden upsurge in worker need while we were stopping workers)
                                     break;
                                 }
                             }
                             System.Diagnostics.Debug.WriteLine(_poolName + " workers:" + _workers.ToString());
                             // record when we retired threads
                             lastRetirementTicks = Environment.TickCount;
                         }
                         // reset the concurrency
                         System.Threading.Interlocked.Exchange(ref _peakConcurrentUsageSinceLastRetirementCheck, 0);
                     }
                 }
             }, "'{0}' Pool Master Exception");
         }
     }, "Critical '{0}' Pool Master Exception");
 }
Example #3
0
 private void WorkerFunc()
 {
     try
     {
         RunWithLogException(
             () =>
         {
             long maxInvokeTicks = 0;
             System.Diagnostics.Debug.WriteLine("Starting " + _id);
             long startTicks      = WorkerPool.Ticks;
             long completionTicks = startTicks;
             // loop until we're told to stop
             while (_stop == 0)
             {
                 startTicks      = WorkerPool.Ticks;
                 completionTicks = startTicks;
                 // wait for the wake thread event to be signalled so we can start some work
                 _pool.WaitForWork(_wakeThread);
                 // stop now?
                 if (_stop != 0)
                 {
                     break;
                 }
                 // record the start time
                 startTicks = WorkerPool.Ticks;
                 // NO work to execute? (just in case--I don't think this can ever really happen)
                 if (_actionToPerform == null)
                 {
                     // nothing to do, so we're done
                     completionTicks = WorkerPool.Ticks;
                 }
                 else
                 {
                     RunWithLogException(
                         () =>
                     {
                         // perform the work
                         _actionToPerform.DynamicInvoke();
                     },
                         "An unexpected error occured during a '{0}' thread worker dynamic invocation");
                     // mark the time
                     completionTicks = WorkerPool.Ticks;
                 }
                 // finish work in succes case
                 FinishWork();
                 // record statistics
                 long invokeTime = _invokeTicks;
                 // ignore how long it took if we never got invoked
                 if (invokeTime > 0)
                 {
                     long invokeTicks    = startTicks - invokeTime;
                     long executionTicks = completionTicks - startTicks;
                     if (invokeTicks > maxInvokeTicks)
                     {
                         maxInvokeTicks = invokeTicks;
                         InterlockedHelper.Max(ref sSlowestInvocation, invokeTicks);
                     }
                 }
             }
             System.Diagnostics.Debug.WriteLine("Exiting '" + _id + "' worker thread: max invocation wait=" + (maxInvokeTicks * 1000.0f / WorkerPool.TicksPerSecond).ToString() + "ms");
         }, "Exception in '{0}' WorkerFunc");
     }
     catch (OutOfMemoryException e)
     {
         Program.RecordAsyncException(e);
     }
     finally
     {
         // wait for up to one second for the stopper to be done accessing us
         _allowDisposal.WaitOne(1000);
         Dispose();
     }
 }