/// <summary> /// Main worker run loop. Repeatedly gets tasks from queue and /// executes them, while coping with a number of issues: /// /// 1. We may start out with an initial task, in which case we /// don't need to get the first one. Otherwise, as long as pool is /// running, we get tasks from GetTask. If it returns null then the /// worker exits due to changed pool state or configuration /// parameters. Other exits result from exception throws in /// external code, in which case completedAbruptly holds, which /// usually leads ProcessWorkerExit to replace this thread. /// /// 2. Before running any task, the lock is acquired to prevent /// other pool interrupts while the task is executing, and /// ClearInterruptsForTaskRun called to ensure that unless pool is /// stopping, this thread does not have its interrupt set. /// /// 3. Each task run is preceded by a call to BeforeExecute, which /// might throw an exception, in which case we cause thread to die /// (breaking loop with completedAbruptly true) without processing /// the task. /// /// 4. Assuming BeforeExecute completes normally, we run the task, /// gathering any of its thrown exceptions to send to /// AfterExecute. We separately handle RuntimeException, Error /// (both of which the specs guarantee that we trap) and arbitrary /// Throwables. Because we cannot rethrow Throwables within /// Runnable.run, we wrap them within Errors on the way out (to the /// thread's UncaughtExceptionHandler). Any thrown exception also /// conservatively causes thread to die. /// /// 5. After task.run completes, we call AfterExecute, which may /// also throw an exception, which will also cause thread to /// die. According to JLS Sec 14.20, this exception is the one that /// will be in effect even if task.run throws. /// /// The net effect of the exception mechanics is that AfterExecute /// and the thread's UncaughtExceptionHandler have as accurate /// information as we can provide about any problems encountered by /// user code. /// <param name="worker">the worker to run</param> /// </summary> private void RunWorker(Worker worker) { var task = worker.FirstTask; worker.FirstTask = null; var completedAbruptly = true; try { while (task != null || (task = GetTask()) != null) { worker.Lock(); ClearInterruptsForTaskRun(); try { BeforeExecute(worker.Thread, task); Exception thrown = null; try { task.Run(); } catch (Exception x) { thrown = x; OnThreadException(task, x); } finally { AfterExecute(task, thrown); } } finally { task = null; worker.CompletedTasks++; worker.Unlock(); } } completedAbruptly = false; } finally { ProcessWorkerExit(worker, completedAbruptly); } }
/// <summary> /// Main worker run loop. Repeatedly gets tasks from queue and /// executes them, while coping with a number of issues: /// /// 1. We may start out with an initial task, in which case we /// don't need to get the first one. Otherwise, as long as pool is /// running, we get tasks from getTask. If it returns null then the /// worker exits due to changed pool state or configuration /// parameters. Other exits result from exception throws in /// external code, in which case completedAbruptly holds, which /// usually leads processWorkerExit to replace this thread. /// /// 2. Before running any task, the lock is acquired to prevent /// other pool interrupts while the task is executing, and /// clearInterruptsForTaskRun called to ensure that unless pool is /// stopping, this thread does not have its interrupt set. /// /// 3. Each task run is preceded by a call to beforeExecute, which /// might throw an exception, in which case we cause thread to die /// (breaking loop with completedAbruptly true) without processing /// the task. /// /// 4. Assuming beforeExecute completes normally, we run the task, /// gathering any of its thrown exceptions to send to /// afterExecute. We separately handle RuntimeException, Error /// (both of which the specs guarantee that we trap) and arbitrary /// Throwables. Because we cannot rethrow Throwables within /// Runnable.run, we wrap them within Errors on the way out (to the /// thread's UncaughtExceptionHandler). Any thrown exception also /// conservatively causes thread to die. /// /// 5. After task.run completes, we call afterExecute, which may /// also throw an exception, which will also cause thread to /// die. According to JLS Sec 14.20, this exception is the one that /// will be in effect even if task.run throws. /// /// The net effect of the exception mechanics is that afterExecute /// and the thread's UncaughtExceptionHandler have as accurate /// information as we can provide about any problems encountered by /// user code. /// /// <param name="worker">the worker to run</param> /// </summary> protected void runWorker(Worker worker) { IRunnable task = worker.FirstTask; worker.FirstTask = null; bool completedAbruptly = true; try { while (task != null || (task = getTask()) != null) { worker.Lock(); clearInterruptsForTaskRun(); try { beforeExecute(worker.Thread, task); Exception thrown = null; try { task.Run(); } catch (Exception x) { thrown = x; throw; } finally { afterExecute(task, thrown); } } finally { task = null; worker.CompletedTasks++; worker.Unlock(); } } completedAbruptly = false; } finally { processWorkerExit(worker, completedAbruptly); } }