Error Handling
Worker exceptions never propagate to the caller's thread. The pool captures them, keeps running, and lets you inspect them after the fact or react as they happen.
There are two distinct error surfaces:
- Submission errors raised synchronously on the calling thread (
EQueueFullException,EThreadPoolShutdown). - Worker errors collected asynchronously (
LastError,Errors,OnError), plus per-taskErrorMessagefor submitted tasks.
Inspecting failures after WaitForAll#
The simplest pattern polls the pool after work completes:
Pool.ClearErrors;
Pool.Queue(@RiskyWork);
Pool.WaitForAll;
if Pool.LastError <> '' then
WriteLn('The most recent failure was: ', Pool.LastError);
for Msg in Pool.Errors do
WriteLn('All failures, oldest first: ', Msg);| Member | Meaning |
|---|---|
LastError | The most recent captured message; empty if none |
Errors | Oldest-first snapshot of every captured message |
ErrorCount | Number of messages currently in Errors |
ClearErrors | Clears Errors and resets LastError |
ClearLastError | Legacy single-value reset (same effect as above) |
Errors is capped at MAX_STORED_ERRORS = 1000; the oldest entries are dropped so a flood of failures cannot exhaust memory. LastError always reflects the most recent failure regardless of the cap.
Reacting as failures happen#
Assign OnError to be notified on the worker thread the moment a task fails:
Pool.OnError := @Handler.OnTaskError;The handler runs synchronously on the worker that caught the exception. Keep it short, bounded, and thread-safe; synchronize if it touches the UI or shared state.
Handler exceptions are contained#
An exception raised inside your OnError handler is caught by the pool. It cannot terminate a worker or prevent task completion accounting, and the original task error is already recorded.
Per-task error messages#
A task submitted with Submit stores its failure in Task.ErrorMessage, independently of the pool collection:
Task := Pool.Submit(@RiskyWork);
Task.WaitFor;
if Task.State = ttsFailed then
WriteLn('That task failed: ', Task.ErrorMessage);See Tasks & Batches.
Queue-full errors (bounded pool only)#
Queue on ThreadPool.ProducerConsumer raises EQueueFullException when the queue stays full until the compatibility deadline expires. Catch it around each Queue call, never around WaitForAll:
try
Pool.Queue(@MyProcedure);
except
on E: EQueueFullException do
WriteLn('Queue is saturated: ', E.Message);
end;
Pool.WaitForAll;Catch by exception type, never by message string.
Prefer TryQueue(..., TimeoutMS) when saturation is expected; it returns False instead of raising. See Backpressure.
Shutdown errors#
Once Shutdown begins, Queue, TryQueue, Submit, and TrySubmit raise EThreadPoolShutdown. See Lifecycle & Shutdown.
Rules that prevent most surprises#
- Check
LastErrororErrorsafterWaitForAll, and clear them before reusing the pool so stale messages do not linger. - Give blocking operations an application-level timeout or cancellation; the pool cannot time a callback out.
- Do not place
WaitForAllinside aEQueueFullExceptionhandler — drain and retry separately. - Keep
OnErrorhandlers non-blocking and thread-safe.
Related#
- Common Recipes includes a compiled failure-reporting program.
- Simple API
- Producer-Consumer API