Skip to main content

Lib/concurrent/futures/process.py

Source:

cpython 3.14 @ ab2d84fe1023/Lib/concurrent/futures/process.py

process.py implements ProcessPoolExecutor. It is the most complex file in concurrent.futures because it must deal with process crashes, a multiprocessing call queue, a result queue, and a management thread that coordinates between the calling process and the worker pool.

Map

LinesSymbolRole
1-100_WorkItem, _ResultItemPicklable call/result containers
101-250_process_worker, _process_chunkEntry point run in each worker process
251-480_ExecutorManagerThreadBackground thread in main process; drains result queue
481-740ProcessPoolExecutorPublic executor; submit, map, shutdown

Reading

Worker entry point

Each worker process runs _process_worker, which loops over a SimpleQueue, calling each _WorkItem and putting a _ResultItem back on the result queue.

# CPython: Lib/concurrent/futures/process.py:101 _process_worker
def _process_worker(call_queue, result_queue, initializer, initargs, max_tasks=None):
if initializer is not None:
try:
initializer(*initargs)
except BaseException:
_base.LOGGER.critical('Exception in initializer:', exc_info=True)
return
while True:
call_item = call_queue.get(block=True)
if call_item is None:
result_queue.put(os.getpid())
return
...

A None sentinel signals the worker to shut down gracefully. Workers send their PID back before exiting so the manager thread can join them.

_ExecutorManagerThread

The manager thread is started by the first submit() call. It runs a select-based loop over three file descriptors: the result queue reader, a wakeup pipe that fires when a worker dies unexpectedly, and a shutdown event.

# CPython: Lib/concurrent/futures/process.py:330 _ExecutorManagerThread.run
def run(self):
while True:
self.add_call_item_to_queue()
result_item, is_broken, cause = self.wait_result_broken_or_wakeup()
if is_broken:
self.terminate_broken(cause)
return
if result_item is not None:
self.process_result_item(result_item)
del result_item
if self.is_shutting_down():
self.flag_executor_dead_and_cleanup()
return

When a worker crashes (is_broken=True), all pending futures receive BrokenProcessPool as their exception.

Pickling constraint

All arguments and return values must be picklable because they cross process boundaries through multiprocessing.SimpleQueue. ThreadPoolExecutor has no such constraint.

gopy notes

Not yet ported. A Go port would use os/exec for worker processes and encoding/gob or JSON for call/result serialization. The wakeup-pipe pattern maps naturally to a chan struct{} in Go.