September 28, 2021·4 min read·engineeringcppnetworkingexecutionstandards

P2464 challenges networking's executor error model

The networking debate concerns failures between submitting work and running its continuation. P2464 argues that a standard asynchronous execution model needs a generic way to represent those failures.

A network operation can finish while the code meant to process its result never runs. Reporting the network operation's status does not automatically report a failure in the mechanism responsible for invoking its continuation. An asynchronous abstraction must explain that interval if generic code is expected to compose work across different execution environments.

Ville Voutilainen makes that argument in P2464R0, "Ruminations on networking and executors", dated September 28 and written on behalf of Finland's national body. He recommends against standardizing the Networking TS in its current form. The paper is an argument in an ongoing design dispute, not a committee decision rejecting networking.

I consider the failure interval a useful way to understand the disagreement. An abstraction used by a systems library needs to state what happens when work cannot reach its consumer. C++'s low-overhead generic programming is valuable when that contract survives composition, including the paths where execution cannot proceed normally.

Submitting work and observing completion are different operations

P2464 criticizes the P0443 executor model as a work-submission interface without a generic result-observation mechanism. Voutilainen's concern is specifically failure after submission and before continuation invocation. An immediate submission failure and an I/O completion error are not the only possible unsuccessful outcomes in the larger execution chain.

The paper says Networking TS proponents consider the model sufficient within its intended environment. It discusses suggested answers involving refined executors and completion handlers. Voutilainen objects that generic code receives the common executor abstraction, not necessarily the implementation-specific refinement through which additional error information might be available.

That is the central generality question. A concrete executor may provide a perfectly usable private error channel. A generic algorithm cannot rely on it unless the common contract describes how to obtain and interpret it. Adding the information separately in each implementation creates another interface that adapters must know about.

The distinction does not establish that existing networking applications routinely lose results. P2464's recommendation concerns the scope of a standard abstraction and the broader uses programmers may make of it. The author's argument is that a component standardized for generic execution will be used outside the narrow assumptions of one networking environment.

A continuation can move across execution contexts

The paper considers execution strategies more complicated than a simple pool that always runs accepted work. A continuation can be queued through several contexts, with intermediate logic translating lower-level results into application-level results. At each boundary, the execution mechanism has responsibilities separate from the operation whose result it transports.

Voutilainen's illustrative pseudocode separates obtaining a network result from arranging where its continuation runs. The network library knows the particular I/O operation. It need not know the concrete scheduler type or the application logic wrapped around the continuation. A generic contract must connect those responsibilities without requiring every combination to receive a bespoke adapter.

This is also why the paper distinguishes execution-context policy from the network operation itself. The caller may select where higher-level work continues. Preserving that choice and reporting inability to carry it out are part of the composition problem. Returning a socket error alone cannot describe failure in an unrelated execution resource.

The paper argues that sender and receiver designs supply the common completion model needed for this task. That remains a design claim to evaluate against the proposals and their implementations. The useful technical question is which outcomes the common interface represents and which assumptions it leaves to concrete execution resources.

P2300 makes completion part of the operation description

The contemporary P2300R1 execution proposal, published July 6, describes schedulers as handles to execution resources and senders as descriptions of work. A sender reports completion through value, error, or done signals. Its priorities explicitly include error propagation and cancellation, treating cancellation separately from an error.

Sender algorithms compose descriptions of asynchronous work. For example, then attaches a callable to a successful value result and produces another sender. The proposal's example starts work on a scheduler, transforms its value through subsequent operations, and uses sync_wait to start and observe the resulting computation synchronously.

The observation behavior reflects the completion distinction. In that example, sync_wait returns an optional tuple for a value result, an empty optional for a done result, or throws for an error. Those are the R1 proposal's interfaces and names, not a claim that they are available in the current standard library.

P2300 also separates explicit execution-context transitions from transforming values. That separation makes it possible to reason about where an operation completes as well as what data it produces. It addresses the same broader composition problem that P2464 uses to challenge a submission-only executor contract.

I prefer evaluating these models through such observable outcomes. Counting callbacks or templates does not establish whether an error can reach the appropriate consumer. The interface has to identify completion and execution responsibilities clearly enough that a generic algorithm can preserve them across a boundary.

The recommendation has a schedule cost

P2464 offers a minimal direction of replacing the Networking TS's executor dependency with a P2300 scheduler. It acknowledges mismatches and efficiency concerns, and says the design and implementation would take time. Its stronger recommendation is to stop targeting the Networking TS for C++23 and focus on sender and receiver progress.

That recommendation trades nearer-term standardization of an existing networking design for a different common execution model. The paper does not claim that changing the dependency is a mechanical rename. Its own acknowledgement of design work matters because execution policy affects how completion handlers and operations interact, including the conditions under which a caller can observe that work completed or failed to run.

The disagreement is therefore substantive even for programmers who only need networking. A standard networking interface establishes a relationship with the execution facilities around it. P2464 asks the committee to settle whether that relationship can express failures in scheduling and continuation delivery before making the proposed networking model part of the main standard.