PDFium Delphi Component Docs

Модуль FPdfAsync

Модуль: FPdfAsync
Инфраструктура отмены, future и ограниченного пула рабочих потоков для отзывчивой обработки PDF в приложениях VCL, LCL, FMX и headless

Syntax

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;

Cancellation types

TypeDescription
TPdfCancellationReasonСтруктурированная причина завершения, охватывающая запросы пользователя, таймауты, связанную работу, остановку приложения, устаревшую работу и определяемую приложением отмену
EPdfOperationCancelledИсключение отмены с сохранёнными для диагностики исходными Reason и сообщением
IPdfCancellationTokenСовместимый токен рабочей стороны с опросом IsCancelled, RegisterCallback, ThrowIfCancelled и поддержкой исключений
IPdfCancellationTokenSourceСовместимый источник стороны владельца со свойством Token и идемпотентным Cancel
IPdfCancellationRegistrationСтабильная регистрация обратного вызова с Id, IsRegistered и потокобезопасным Unregister
IPdfCancellationTokenExРасширенный токен с метаданными отмены, счётчиками обратных вызовов, съёмной регистрацией обратных вызовов и ожиданием на событиях
IPdfCancellationTokenSourceExРасширенный источник владельца с атомарными TryCancel и CancelWithReason и строго типизированным TokenEx
TPdfCancellationTokenSourceПотокобезопасная реализация источника, создаваемая через New, NewEx или NewTimeout
PdfNoCancellationTokenОбщий токен, который никогда не отменяется и избавляет от выделения источника для неотменяемой работы

Future types

TypeDescription
TPdfFutureStatusСостояние результата: pfsSuccess, pfsCancelled или pfsFailure
TPdfFutureDispatchModeПолитика ответа: pfdSynchronize отправляет ответ в главный поток, pfdWorkerThread вызывает его в рабочем потоке, а pfdNone подавляет ответ
TPdfFutureOptionsНастройки RunEx для диспетчеризации ответа и ThreadPriority рабочего потока
TPdfFutureResult<T>Конверт завершения с Value и HasValue, Status, парой ErrorMessage и ErrorClassName, CancellationReason, OperationId, StartedAtTick и CompletedAtTick, помощниками статуса и ElapsedMilliseconds
TPdfFutureWorker<T>Рабочий элемент — указатель на метод, который получает токен отмены и возвращает фоновый результат
TPdfFutureReply<T>Обратный вызов — указатель на метод, принимающий завершённый TPdfFutureResult<T>
TPdfFutureThread<T>Тип потока с поддержкой компилятора, используемый TPdfFuture<T>; создаётся через Create или учитывающий опции CreateEx, а приложения обычно вызывают Run или RunEx
TPdfFuture<T>Помощник fire-and-forget, сохраняющий исходное поведение синхронизированного ответа через Run и добавляющий настраиваемую диспетчеризацию через RunEx

Executor types

TypeDescription
TPdfAsyncPriorityЧетыре очереди извлечения от papLow до papCritical с порядком FIFO внутри каждой
TPdfAsyncTaskStateЖизненный цикл задачи: patsQueued, patsRunning, patsSucceeded, patsCancelled или patsFailed
TPdfAsyncDispatchModeДиспетчеризация ответа через padWorkerThread, padSynchronize или подавляющий ответ padNone
TPdfAsyncTaskWorkerЭлемент работы — указатель на метод, получающий токен отмены своей задачи
TPdfAsyncTaskReplyЗавершающий обратный вызов — указатель на метод, принимающий неизменяемый снимок результата задачи
TPdfAsyncTaskResultTaskId, State, Priority, моменты QueuedAtTick, StartedAtTick и CompletedAtTick, пара ошибки worker ErrorMessage и ErrorClassName, CancellationReason с сообщением и изолированный ReplyErrorMessage
IPdfAsyncTaskДескриптор отправленной задачи с Cancel, учитывающим таймаут WaitFor, атомарным Snapshot и свойствами Id, State, Priority, Token и IsCompleted
TPdfAsyncExecutorStatsЁмкость, текущая нагрузка, пиковая глубина очереди и накопительные счётчики отправленных, успешных, отменённых, неудачных и отклонённых задач
EPdfAsyncQueueFullВозбуждается методом Submit, когда ограниченная очередь не может принять ещё одну задачу
EPdfAsyncExecutorShutdownВозбуждается методом Submit после начала остановки исполнителя
TPdfAsyncExecutorМногоразовый фиксированный пул рабочих потоков с ограниченной очередью, четырьмя приоритетными кольцами O(1), кооперативной отменой задач, статистикой и корректной остановкой

Timing and identity helpers

FunctionDescription
PdfAsyncTickВозвращает монотонную миллисекундную метку, используемую в метаданных future и исполнителя
PdfAsyncTickDeltaВычисляет защитное значение прошедших миллисекунд и возвращает ноль для отсутствующих или идущих в обратном порядке меток
PdfNextAsyncIdВозвращает локальный для процесса положительный идентификатор, общий для future, регистраций обратных вызовов и задач исполнителя

Cancellation example

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;

Bounded executor example

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;

Performance and threading

Сборки Delphi для POSIX сохраняют значения параметра TThreadPriority tpIdle, tpLowest, tpLower, tpNormal, tpHigher, tpHighest и tpTimeCritical ради совместимости на уровне исходного кода, но RTL платформы поддерживает только путь планирования по умолчанию. tpNormal использует этот путь; создание future с другим приоритетом потока выбрасывает ENotSupportedException ещё до запуска worker

Используйте TPdfAsyncExecutor для повторяющейся или всплесковой работы, чтобы задачи переиспользовали фиксированный набор потоков вместо создания отдельного потока на каждую операцию

Положительная ёмкость очереди даёт детерминированное обратное давление, а ноль оставляет очередь неограниченной; выполняющиеся задачи не занимают слоты очереди

Приоритетное планирование строго между очередями и FIFO внутри каждой, поэтому приберегайте papCritical и papHigh для действительно чувствительной к задержкам работы

WaitForIdle обрабатывает синхронизированные обратные вызовы при вызове из главного потока, позволяя ответам padSynchronize завершиться без дедлока при остановке

Каждый источник таймаута держит один лёгкий поток ожидания до отмены или истечения, поэтому при создании очень большого числа короткоживущих дедлайнов предпочтительнее общий таймер приложения или планировщик дедлайнов

Правила владения объектами PDFium остаются в силе: создавайте или изолируйте экземпляр TPdf для каждого параллельного worker и не изменяйте и не рендерьте один и тот же активный экземпляр документа параллельно

Compatibility

Исходные API IPdfCancellationToken, IPdfCancellationTokenSource, TPdfCancellationTokenSource.New и TPdfFuture<T>.Run остаются совместимыми на уровне исходного кода

Расширенные интерфейсы используйте только тогда, когда требуются структурированная диагностика, съёмные обратные вызовы, ожидание по таймауту или атомарное владение отменой

Обратные вызовы остаются указателями на методы, а не анонимными методами, поэтому тот же API компилируется в Delphi, C++Builder и Free Pascal

See Also

TPdf.RenderPageProgressive, TPdfView.AsyncRendering