advanced24 min studyLesson 162 in Engineering

Reactor and proactor ownership

Compare where an operation executes, which state a handler receives, and how both patterns preserve request identity.

On this page

What you’ll understand

  • Trace reactor and proactor request lifecycles.
  • Separate operation execution from completion dispatch.
  • Preserve identity and ownership across out-of-order results.

The words reactor and proactor describe a division of responsibilities, not a ranking of fashionable libraries. A reactor tells application code when it should attempt work. A proactor dispatches application code after an asynchronous operation produces a result. Both can hide waiting, and both can fail badly when state, ownership, and backpressure are left implicit. Compare their traces before comparing their throughput.

Place the operation on the trace

In a reactor design, a dispatcher waits for resource conditions and invokes the associated handler. That handler attempts I/O, interprets the result, and updates application state. A connection can therefore have readable input without containing a complete protocol frame. Separate the generic dispatcher from connection-specific parsing and request state. The dispatcher should not need to understand every message format, but its registration and lifetime rules must make it impossible to dispatch into an already-destroyed handler.

In a proactor design, an initiator requests an asynchronous operation and supplies enough state to associate a later result with its continuation. A completion dispatcher delivers the resulting status and progress. The operation might be carried out by an operating-system facility or by workers beneath an asynchronous facade. Application code must understand the actual contract rather than infer hardware behavior from the pattern name. Completion ordering and buffer lifetime remain explicit obligations even when initiating code looks sequential.

Lreactor=dready+qh+shL_{\mathrm{reactor}}=d_{\mathrm{ready}}+q_h+s_h
For this bookkeeping decomposition, latency after the external readiness condition consists of readiness-delivery delay d_ready, handler queue delay q_h, and handler execution s_h, all in seconds. It is not a universal performance law.↔ Scroll wide equations horizontally.

A result belongs to an operation, not just a connection

Several operations may reference the same connection while completing in a different order from submission. Store a stable operation identity and the expected transition with each request. Do not confuse a reused descriptor number with the original connection object. A generation number or another lifetime-safe handle can reject stale callbacks. The application must also decide whether two writes are allowed concurrently and how their ordering relates to its protocol; the dispatch pattern alone does not establish message order.

Draw ownership changes alongside control flow. Before submission, the application prepares a request and its buffer. While an operation is outstanding, permitted readers and writers follow the API contract. On a terminal result, the owner interprets progress and releases or transfers resources exactly once. If a task times out, some underlying work may still exist. A timeout continuation cannot free memory merely because the caller has stopped waiting. Error paths must respect the same terminal boundary as successful paths.

Nlive=Nsubmitted−NterminalN_{\mathrm{live}}=N_{\mathrm{submitted}}-N_{\mathrm{terminal}}
These cumulative counts refer to distinct logical operations over a period starting with no live operations. A multishot notification that leaves an operation active is not terminal. The count is dimensionless and must remain nonnegative.↔ Scroll wide equations horizontally.

Choose interfaces from constraints

A readiness-based socket service may already have a good protocol state machine and modest copying costs. A completion-based storage pipeline may benefit from explicitly managing a bounded set of outstanding reads. Neither observation proves a universal winner. Compare supported operations, runtime portability, cancellation semantics, submission costs, memory pressure, and the complexity of shutdown. A hybrid often exists: one layer exposes futures while another layer uses readiness or a blocking worker pool. Review the boundary that your team actually owns.

The queue lab isolates only offered work and draining capacity. Press Reset, set arrival rate to 4 and service rate to 3, predict growth from the initial four-job backlog, then press Play. For a separate draining experiment, press Reset again, set arrival rate to 2 and service rate to 4, predict the slope, then press Play. Reset both rates for each new run rather than treating a slider edit as a mid-run capacity change. This can motivate bounding outstanding operations, but it cannot choose reactor versus proactor or count per-request notifications. For that choice, write two traces of the same request and mark the instruction that obtains its result, the component allowed to release its buffer, and the error path that removes its registration.

Audit outstanding operation accounting

A completion-oriented toy service begins with no live operations. It submits 120 distinct one-shot reads. During the observed interval, 90 finish successfully, 10 finish with errors, and cancellation is requested for 8 others without their terminal results yet arriving.

  1. Count terminal operations: 90 successful plus 10 failed equals 100. An error is still terminal when the API says the operation has finished.

  2. Subtract terminal operations from submissions: 120 − 100 = 20 operations remain live. The eight cancellation requests do not yet subtract from this number.

  3. Partition the 20 as eight cancellation-requested and twelve other outstanding operations. Continue retaining their required resources until their own terminal contracts are satisfied.

The result

Twenty operations remain live. Counting cancellation requests as completed cleanup would undercount by eight and could cause premature release.

Explore the model

Interactive lab

Interactive lab · queue

Patterns still need bounded outstanding work

Press Reset, explicitly set arrival rate to 4 and service rate to 3, predict growth, then press Play. Start the draining comparison with another Reset and both rates set to 2 and 4. This is a capacity illustration from four initial jobs, not a reactor/proactor execution trace or API throughput comparison.

Patterns still need bounded outstanding workFluid backlog starts at 4 and follows max(0, 4 + (λ − μ)t). The separate M/M/1 residence-time formula assumes Poisson arrivals, exponential service and λ < μ. Current readings: Fluid backlog: 4; Net rate · λ − μ: −1 /s; M/M/1 mean residence: 1 s. All parameters are adjustable with the labeled controls below.02.557.51001.534.56Elapsed time · sFluid backlogλ < μ
Fluid backlogCurrent time
Fluid backlog
4
Net rate · λ − μ
−1 /s
M/M/1 mean residence
1 s
t = 0 s

Fluid backlog starts at 4 and follows max(0, 4 + (λ − μ)t). The separate M/M/1 residence-time formula assumes Poisson arrivals, exponential service and λ < μ. Parameter changes pause playback and recompute the model at the current elapsed time. Reset restores both parameters and returns time to zero. Playback starts paused. Adjust either parameter, then choose Play or Step.

Look forChanging notification style does not remove the need to bound and account for live operations.

Check your understanding

Which observation most directly distinguishes a proactor-style completion from a reactor-style readiness event?

Try it yourself

A service submits 70 one-shot requests, observes 45 successful terminals and 5 error terminals, then requests cancellation of 12 remaining requests. How many are still live before further results arrive?

Take a moment to make a prediction or work it out on paper. Open a hint when you need one.

Give me a hint

Classify terminal outcomes before considering cancellation intent.

Compare with the solution

There are 50 terminal requests, so 70 − 50 = 20 remain live. Twelve of those have cancellation requested. Their request objects and API-required buffers remain owned until a terminal result or another documented quiescence guarantee permits release. Do not subtract a cancellation-control completion if it does not itself establish the target operation’s termination.

Take it with you

  • Reactor handlers attempt operations; proactor handlers receive prior operation results.
  • Stable identities and explicit lifetimes matter under either pattern.
  • A cancellation request is not interchangeable with a terminal operation result.

Read further

Original sources and deeper explanations to follow at your own pace.

Optional progress, saved in this browser when available. Revisit any lesson at any time.

Lesson not marked complete.