Add a timeout parameter to Executor.shutdown() — issue #72165

I’d like to discuss the proposed feature in CPython issue #72165: adding an optional timeout parameter to concurrent.futures.Executor.shutdown().

Currently, when Executor.shutdown() is called with wait=True (the default), it waits for all currently running futures to complete. There is no way for the caller to put an upper bound on how long it waits.

The proposed API is:

executor.shutdown(wait=True, *, cancel_futures=False, timeout=None)

When timeout is None, the existing behavior would remain unchanged.

When a timeout is specified, shutdown() would wait for at most that amount of time for the executor to shut down. If the timeout expires before shutdown completes, TimeoutError would be raised.

Running futures would not be cancelled or forcibly terminated when the timeout expires. They would be allowed to continue executing, while the caller regains control after the specified timeout.

For example:

executor = ThreadPoolExecutor()
future = executor.submit(long_running_task)

try:
    executor.shutdown(timeout=5)
except TimeoutError:
    # Shutdown did not complete within 5 seconds.
    ...

The motivation is to provide applications with a bounded shutdown operation. There are situations where waiting indefinitely for running work is undesirable, particularly when shutdown is part of a larger timeout, cleanup, or application-lifecycle mechanism.

I have prototyped this change for both ThreadPoolExecutor and ProcessPoolExecutor, along with tests and documentation. I opened CPython PR #157215 with the implementation, but the PR was closed pending discussion and core developer support for the feature.

I’d particularly appreciate feedback on:

  1. Whether a timeout on Executor.shutdown() is a useful addition to the API.

  2. Whether raising TimeoutError when the timeout expires is the appropriate behavior.

  3. Whether the proposed semantics for running futures are appropriate.

  4. Whether there are existing patterns or alternatives that make this functionality unnecessary.

  5. Whether the API should behave differently for ThreadPoolExecutor and ProcessPoolExecutor.

The goal of this discussion is to determine whether this feature is desirable and, if so, what the appropriate API and semantics should be before pursuing the implementation further.

1 Like