TPdfCancellationReason = (pcrNone, pcrUser, pcrTimeout, pcrLinked,
pcrShutdown, pcrSuperseded, pcrOther);
IPdfCancellationTokenEx = interface(IPdfCancellationToken)
function RegisterCallbackManaged(const ACallback: TThreadMethod): IPdfCancellationRegistration;
function WaitForCancellation(ATimeoutMs: Cardinal): Boolean;
property Reason: TPdfCancellationReason read GetReason;
property CancellationMessage: string read GetCancellationMessage;
property CancelledAtTick: UInt64 read GetCancelledAtTick;
property CallbackCount: Integer read GetCallbackCount;
property HasCallbacks: Boolean read GetHasCallbacks;
end;
IPdfCancellationTokenSourceEx = interface(IPdfCancellationTokenSource)
function TryCancel: Boolean;
function CancelWithReason(AReason: TPdfCancellationReason; const AMessage: string): Boolean;
property TokenEx: IPdfCancellationTokenEx read GetTokenEx;
end;
TPdfCancellationTokenSource = class
destructor Destroy; override;
class function New: IPdfCancellationTokenSource; static;
class function NewEx: IPdfCancellationTokenSourceEx; static;
class function NewTimeout(AMilliseconds: Cardinal; const AMessage: string = ''): IPdfCancellationTokenSourceEx; static;
end;
TPdfFutureOptions = record
DispatchMode: TPdfFutureDispatchMode;
ThreadPriority: TThreadPriority;
class function Default: TPdfFutureOptions; static;
end;
TPdfFuture<T> = class
class procedure Run(const AWorker: TPdfFutureWorker<T>; const AReply: TPdfFutureReply<T>; const AToken: IPdfCancellationToken = nil); static;
class procedure RunEx(const AWorker: TPdfFutureWorker<T>; const AReply: TPdfFutureReply<T>; const AToken: IPdfCancellationToken; const AOptions: TPdfFutureOptions); static;
end;
TPdfAsyncExecutor = class
constructor Create(AWorkerCount: Integer; AQueueCapacity: Integer = 0);
destructor Destroy; override;
function Submit(const AWorker: TPdfAsyncTaskWorker; const AReply: TPdfAsyncTaskReply = nil; APriority: TPdfAsyncPriority = papNormal; ADispatchMode: TPdfAsyncDispatchMode = padWorkerThread): IPdfAsyncTask;
function TrySubmit(const AWorker: TPdfAsyncTaskWorker; const AReply: TPdfAsyncTaskReply; out ATask: IPdfAsyncTask; APriority: TPdfAsyncPriority = papNormal; ADispatchMode: TPdfAsyncDispatchMode = padWorkerThread): Boolean;
function WaitForIdle(ATimeoutMs: Cardinal): Boolean;
function GetStats: TPdfAsyncExecutorStats;
procedure Shutdown(ACancelPending: Boolean = True);
property WorkerCount: Integer read FWorkerCount;
property QueueCapacity: Integer read FQueueCapacity;
property IsShutdown: Boolean read GetIsShutdown;
end;
TPdfAsyncExecutorStats = record
WorkerCount: Integer;
QueueCapacity: Integer;
QueuedCount: Integer;
RunningCount: Integer;
PeakQueuedCount: Integer;
SubmittedCount: Int64;
SucceededCount: Int64;
CancelledCount: Int64;
FailedCount: Int64;
RejectedCount: Int64;
end;
IPdfAsyncTask = interface
function Cancel: Boolean;
function WaitFor(ATimeoutMs: Cardinal): Boolean;
function Snapshot: TPdfAsyncTaskResult;
property Id: Int64 read GetId;
property State: TPdfAsyncTaskState read GetState;
property Priority: TPdfAsyncPriority read GetPriority;
property Token: IPdfCancellationToken read GetToken;
property IsCompleted: Boolean read GetIsCompleted;
end;
| Type | Description |
|---|---|
TPdfCancellationReason | Structured terminal reason covering user requests, timeouts, linked work, shutdown, superseded work and application-defined cancellation |
EPdfOperationCancelled | Cancellation exception with the original Reason and message preserved for diagnostics |
IPdfCancellationToken | Compatibility worker-side token with IsCancelled polling, RegisterCallback, ThrowIfCancelled and exception support |
IPdfCancellationTokenSource | Compatibility owner-side source with Token and idempotent Cancel |
IPdfCancellationRegistration | Stable callback registration with Id, IsRegistered and thread-safe Unregister |
IPdfCancellationTokenEx | Extended token with cancellation metadata, callback counts, removable callback registration and event-backed waiting |
IPdfCancellationTokenSourceEx | Extended owner source with atomic TryCancel, CancelWithReason and strongly typed TokenEx |
TPdfCancellationTokenSource | Thread-safe source implementation created with New, NewEx or NewTimeout |
PdfNoCancellationToken | Shared token that never cancels and avoids allocating a source for non-cancellable work |
| Type | Description |
|---|---|
TPdfFutureStatus | Result state: pfsSuccess, pfsCancelled or pfsFailure |
TPdfFutureDispatchMode | Reply policy: pfdSynchronize posts the reply to the main thread, pfdWorkerThread invokes it on the worker, and pfdNone suppresses the reply |
TPdfFutureOptions | RunEx settings for reply dispatch and worker ThreadPriority |
TPdfFutureResult<T> | Completion envelope with Value and HasValue, Status, the ErrorMessage and ErrorClassName pair, the CancellationReason, OperationId, StartedAtTick and CompletedAtTick, status helpers, and ElapsedMilliseconds |
TPdfFutureWorker<T> | Method-pointer worker that receives a cancellation token and returns the background result |
TPdfFutureReply<T> | Method-pointer callback that receives the completed TPdfFutureResult<T> |
TPdfFutureThread<T> | Compiler-support thread type used by TPdfFuture<T>; constructed through Create or the options-aware CreateEx, and applications normally call Run or RunEx |
TPdfFuture<T> | Fire-and-forget helper that preserves the original synchronised-reply behaviour through Run and adds configurable dispatch through RunEx |
| Type | Description |
|---|---|
TPdfAsyncPriority | Four dequeue lanes from papLow through papCritical, with FIFO order inside each lane |
TPdfAsyncTaskState | Task lifecycle: patsQueued, patsRunning, patsSucceeded, patsCancelled or patsFailed |
TPdfAsyncDispatchMode | Reply dispatch through padWorkerThread, padSynchronize or padNone to suppress the reply |
TPdfAsyncTaskWorker | Method-pointer work item that receives its task cancellation token |
TPdfAsyncTaskReply | Method-pointer completion callback receiving an immutable task result snapshot |
TPdfAsyncTaskResult | TaskId, State, Priority, QueuedAtTick, StartedAtTick and CompletedAtTick timing, the ErrorMessage and ErrorClassName worker error pair, the CancellationReason with its message, and the isolated ReplyErrorMessage |
IPdfAsyncTask | Submitted-task handle with Cancel, timeout-aware WaitFor, atomic Snapshot, and the Id, State, Priority, Token and IsCompleted properties |
TPdfAsyncExecutorStats | Capacity, current load, peak queue depth, and cumulative submitted, succeeded, cancelled, failed and rejected counts |
EPdfAsyncQueueFull | Raised by Submit when a bounded queue cannot accept another task |
EPdfAsyncExecutorShutdown | Raised by Submit after executor shutdown starts |
TPdfAsyncExecutor | Reusable fixed worker pool with a bounded queue, four O(1) priority rings, cooperative task cancellation, statistics and graceful shutdown |
| Function | Description |
|---|---|
PdfAsyncTick | Returns the monotonic millisecond tick used by future and executor metadata |
PdfAsyncTickDelta | Computes a defensive elapsed-millisecond value and returns zero for missing or reversed ticks |
PdfNextAsyncId | Returns a process-local positive ID shared by futures, callback registrations and executor tasks |
var
CancelSource: IPdfCancellationTokenSourceEx;
begin
CancelSource := TPdfCancellationTokenSource.NewTimeout(5000, 'Export deadline exceeded');
TPdfFuture<Integer>.Run(Worker, Completed, CancelSource.TokenEx);
if UserRequestedCancel then
CancelSource.CancelWithReason(pcrUser, 'Export cancelled by user');
end;
var
Executor: TPdfAsyncExecutor;
Task: IPdfAsyncTask;
begin
Executor := TPdfAsyncExecutor.Create(4, 128);
try
if not Executor.TrySubmit(RenderPage, PageCompleted, Task, papHigh, padSynchronize) then
HandleBackpressure;
Executor.WaitForIdle(30000);
finally
Executor.Free;
end;
end;
Delphi POSIX retains the TThreadPriority values tpIdle, tpLowest, tpLower, tpNormal, tpHigher, tpHighest and tpTimeCritical for source compatibility, although the RTL supports only default scheduling. tpNormal selects that route; constructing a future with any other priority raises ENotSupportedException before a worker starts
Use TPdfAsyncExecutor for repeated or bursty work so tasks reuse a fixed set of threads instead of creating one thread per operation
A positive queue capacity provides deterministic backpressure whilst zero keeps the queue unbounded; running tasks do not consume queue slots
Priority scheduling is strict between lanes and FIFO within a lane, so reserve papCritical and papHigh for genuinely latency-sensitive work
WaitForIdle processes synchronised callbacks when called from the main thread, allowing padSynchronize replies to finish without a shutdown deadlock
Each timeout source owns one lightweight waiting thread until cancellation or expiry, so a shared application timer or deadline scheduler is preferable when creating very large numbers of short-lived deadlines
PDFium object ownership rules still apply: create or isolate a TPdf instance per concurrent worker and do not mutate or render the same active document instance concurrently
The original IPdfCancellationToken, IPdfCancellationTokenSource, TPdfCancellationTokenSource.New and TPdfFuture<T>.Run APIs remain source-compatible
Use the extended interfaces only when structured diagnostics, removable callbacks, timeout waiting or atomic cancellation ownership is required
Callbacks remain method pointers rather than anonymous methods so the same API compiles with Delphi, C++Builder and Free Pascal