Repository navigation
test, docs: teardown destroys every frame before any service - #401
Conversation
|
An automated preview of the documentation is available at https://401.corosio.prtest3.cppalliance.org/index.html If more commits are pushed to the pull request, the docs will rebuild at the same URL. 2026-10-20 22:29:40 UTC |
|
GCOVR code coverage report https://401.corosio.prtest3.cppalliance.org/gcovr/index.html Build time: 2026-10-20 22:42:56 UTC |
Benchmark report
linux — full results (226 benchmarks, 5 iterations, 1.5s each)accept_churn
fan_out
http_server
local_socket_latency
local_socket_throughput
socket_latency
socket_throughput
windows — full results (77 benchmarks, 5 iterations, 1.5s each)accept_churn
fan_out
http_server
socket_latency
socket_throughput
macos — full results (113 benchmarks, 5 iterations, 1.5s each)accept_churn
fan_out
http_server
local_socket_latency
local_socket_throughput
socket_latency
socket_throughput
Run & raw JSON artifacts · flag rule: |median Δ| > max(2%, 3×CV of the noisier side) · advisory only |
5535449 to
76f437b
Compare
Destroying a context is correct only if every suspended frame is destroyed while the services still exist: work left for the scheduler's last drain can reach an I/O object whose service is already gone. Each service gives up its work in shutdown(), so nothing should be left by then. A new test parks chains on a timer, a socket read, an accept and a signal wait, in frames that hold their I/O objects or own them, on a strand, and on POSIX behind a blocking-pool worker. A service created last, and so destroyed first, records that every frame was already destroyed when the services began to go. The scheduler destructors' last drain stays as a backstop for work posted after the shutdown drain, and their comments now say so.
The io_context destructor note, the I/O context guide and the lifetime design note described work posted after the services shut down as an ordinary part of teardown. Every service gives up its work in shutdown() and posts nothing from its destructor, so the scheduler's last drain is only a backstop; the three now say so, and the design note says why work reaching it must not use the destroyed services.
The comment said it catches a pool created after the context's shutdown walk, whose shutdown() is never called. The context now shuts such a pool down as it is created, and a post after shutdown() starts no worker, so the destructor's join is only a guard.
76f437b to
3b6e734
Compare
Problem
Destroying an
io_contextwas correct only because the timer service is created before the scheduler. Work left for the scheduler's last drain can reach an I/O object whose service is already gone. Nothing stated or tested which work may reach that drain.Change
shutdown()and posts nothing from its destructor. An audit of every service, on every backend, found none that posts from its destructor, so no service code changes.io_contextdestructor note, the I/O context guide and the lifetime design note state the rule.Creating the timer service with the first timer, which removes its ordering special case, follows in a separate PR.
Testing
testShutdownDestroysFramesBeforeServicesparks chains on a timer, a socket read, an accept and a signal wait, in frames that hold or own their I/O objects, on a strand, and on POSIX behind a blocking-pool worker (not on io_uring, which reads files through the ring). A service created last, and so destroyed first, checks that every frame is already gone when the services begin to go.Paired with cppalliance/capy#463.
Resolves #399