# Timeout Policy API **Unit**: `Murphy.Policy.Timeout` The Timeout policy aborts an operation that exceeds a configured duration, so a hung dependency cannot hang the caller. ## Overview The Timeout pattern is useful for bounding how long an operation is allowed to run, such as: - Network calls without a reliable timeout of their own - Enforcing an SLA on a critical code path - Preventing a slow dependency from blocking a calling thread indefinitely ## Table of Contents - [TTimeoutContext Class](#ttimeoutcontext-class) - [ITimeoutPolicy Interface](#itimeoutpolicy-interface) - [TTimeoutPolicy Class](#ttimeoutpolicy-class) - [TTimeoutBuilder Class](#ttimeoutbuilder-class) - [Usage Examples](#usage-examples) --- ## TTimeoutContext Class Contains information about a timeout that occurred, passed to the `OnTimeout` callback. ### Declaration ```pascal type TTimeoutContext = class(TObject) private FElapsedTime: TTimeSpan; FTimeoutDuration: TTimeSpan; public property ElapsedTime: TTimeSpan read FElapsedTime; property TimeoutDuration: TTimeSpan read FTimeoutDuration; end; ``` ### Properties #### ElapsedTime ```pascal property ElapsedTime: TTimeSpan read FElapsedTime; ``` **Description**: How much time actually elapsed before the timeout fired. **Type**: `TTimeSpan` (read-only) #### TimeoutDuration ```pascal property TimeoutDuration: TTimeSpan read FTimeoutDuration; ``` **Description**: The configured timeout duration that was exceeded. **Type**: `TTimeSpan` (read-only) --- ## ITimeoutPolicy Interface The interface for the Timeout policy pattern. Extends `IExecutablePolicy`, so a Timeout policy can participate in a [Policy Wrap](PolicyWrap-Policy.md). ### Declaration ```pascal type ITimeoutPolicy = interface(IExecutablePolicy) ['{8B3E9F12-4A7C-4D1E-9B5A-2E8C7F6D4A91}'] function After(ADuration: TTimeSpan): ITimeoutPolicy; function OnTimeout(ACallback: TProc): ITimeoutPolicy; end; ``` ### Methods #### Execute ```pascal procedure Execute(AProc: TProc); ``` **Description**: Executes `AProc` on a background task and waits up to the configured duration. **Behavior**: 1. Runs `AProc` on a `TTask` 2. If it completes within the timeout, any exception it raised is re-raised as-is on the calling thread 3. If it does not complete in time: - The `OnTimeout` callback (if configured) is invoked with a `TTimeoutContext` - `ETimeoutRejectedException` is raised - The background task is abandoned and keeps running; it cleans up its own exception if it eventually raises one **Note**: Exception filtering configured via `Handle()` is **not** applied by this policy - it is accepted only for API consistency with other builders. Any exception raised by `AProc` within the timeout is re-raised untouched, and the timeout itself fires regardless of exception type. #### After ```pascal function After(ADuration: TTimeSpan): ITimeoutPolicy; ``` **Description**: Configures the maximum duration allowed for the operation. **Parameters**: - `ADuration: TTimeSpan` - Maximum time to wait before aborting **Returns**: `ITimeoutPolicy` - Self for method chaining **Default**: `30 seconds` if not specified **Example**: ```pascal Policy := TTimeoutBuilder .Handle([]) .After(TTimeSpan.FromSeconds(5)) .Build; ``` #### OnTimeout ```pascal function OnTimeout(ACallback: TProc): ITimeoutPolicy; ``` **Description**: Configures a callback invoked when the operation times out, before `ETimeoutRejectedException` is raised. **Parameters**: - `ACallback: TProc` - Procedure receiving the timeout context **Returns**: `ITimeoutPolicy` - Self for method chaining **Use Cases**: Logging elapsed time, recording metrics, alerting on repeated timeouts **Example**: ```pascal Policy := TTimeoutBuilder .Handle([]) .After(TTimeSpan.FromSeconds(5)) .OnTimeout(procedure(Context: TTimeoutContext) begin Log(Format('Timed out after %s', [Context.ElapsedTime.ToString])); end) .Build; ``` --- ## TTimeoutPolicy Class Concrete implementation of the Timeout policy pattern. ### Declaration ```pascal type TTimeoutPolicy = class(TPolicy, ITimeoutPolicy) private FTimeoutDuration: TTimeSpan; FOnTimeoutCallback: TProc; public constructor Create(AExceptionTypes: TArray); override; procedure Execute(AProc: TProc); function After(ADuration: TTimeSpan): ITimeoutPolicy; function OnTimeout(ACallback: TProc): ITimeoutPolicy; end; ``` ### Constructor ```pascal constructor Create(AExceptionTypes: TArray); override; ``` **Defaults**: - `FTimeoutDuration`: `30 seconds` - `FOnTimeoutCallback`: `nil` **Note**: Typically called by `TTimeoutBuilder`, not directly by users. ### Implementation Details `Execute` runs `AProc` on a `TTask` and waits with `TTask.Wait(Timeout)`. If the wait times out, the task is abandoned rather than cancelled: it keeps executing, and if it later raises an exception or completes, that outcome is discarded because nobody is waiting for it anymore. This means the underlying operation must be safe to abandon. --- ## TTimeoutBuilder Class Builder class for creating Timeout policies with fluent configuration. ### Declaration ```pascal type TTimeoutBuilder = class sealed(TPolicyBuilder) public class function Handle(AExceptionTypes: TArray): ITimeoutPolicy; override; end; ``` #### Handle ```pascal class function Handle(AExceptionTypes: TArray): ITimeoutPolicy; override; ``` **Description**: Creates a new Timeout policy instance. The exception types passed here are **not** used by this policy: any exception raised by the operation is re-raised as-is, and the timeout applies regardless of any exception filter. Pass `[]` for clarity. **Returns**: `ITimeoutPolicy` - Policy instance ready for further configuration --- ## Usage Examples ### Example 1: Basic Timeout ```pascal uses Murphy.Policy.Timeout; var Policy: ITimeoutPolicy; begin Policy := TTimeoutBuilder .Handle([]) .After(TTimeSpan.FromSeconds(5)) .Build; Policy.Execute( procedure begin CallSlowService; end); end; ``` ### Example 2: Handling the Timeout Exception ```pascal var Policy: ITimeoutPolicy; begin Policy := TTimeoutBuilder.Handle([]).After(TTimeSpan.FromSeconds(2)).Build; try Policy.Execute(procedure begin CallSlowService; end); except on E: ETimeoutRejectedException do WriteLn('Operation aborted: took too long'); end; end; ``` ### Example 3: Timeout Inside a Policy Wrap ```pascal uses Murphy.Policy.Retry, Murphy.Policy.Timeout, Murphy.Policy.Wrap; var Policy: IPolicyWrap; begin Policy := TPolicyWrapBuilder.Wrap([ TRetryBuilder.Handle(ETimeoutRejectedException).Retry(3), TTimeoutBuilder.Handle([]).After(TTimeSpan.FromSeconds(1)) ]); Policy.Execute(procedure begin CallExternalService; end); end; ``` --- ## See Also - [Timeout Pattern Guide](../Patterns/Timeout.md) - Comprehensive usage guide with more examples - [Base Policy API](Base-Policy.md) - Inherited functionality, including `IExecutablePolicy` - [Policy Wrap API](PolicyWrap-Policy.md) - Composing Timeout with other policies - [Combining Patterns](../Patterns/Combining-Patterns.md) - Using Timeout with other policies --- [← Rate Limit Policy](RateLimit-Policy.md) | [API Reference Index](README.md) | [Bulkhead Policy →](Bulkhead-Policy.md)