Commit 0654cd1
committed
Encode mutating-safe contract in XPCClient.send
The Codex adversarial review of this branch flagged that
`XPCClient.send(_:responseTimeout:)` can drop a late XPC reply after
its timeout fires. For idempotent reads (`ClientHealthCheck.ping`,
`list` operations) that is a deliberate tradeoff: the connection
remains valid for subsequent sends and the next caller can re-issue
the request. For mutating operations the same behavior is unsafe:
the caller surfaces `.timeout`, the user retries, and the original
operation may still commit on the server — duplicate or out-of-order
container/network state under any slow-but-not-dead daemon.
An independent audit of the call sites contradicted the operator note
written when the freeze fix was first proposed. Four mutating call
sites were already reaching the unsafe path:
- ContainerClient.create (containerCreate, 60s default via xpcSend)
- NetworkClient.create (networkCreate, 60s default via xpcSend)
- NetworkClient.delete (networkDelete, 60s default via xpcSend)
- SandboxClient.create (sandboxCreateEndpoint, 60s timeout: param)
This commit removes that footgun at the API surface so future call
sites cannot reach for it by accident:
send(_:) -- mutating-safe; no timeout
send(_:timeoutForIdempotentRequest:) -- explicit; late-reply drop
acknowledged at call site
The old `responseTimeout:` spelling is retained as
`@available(*, unavailable, ...)` so any reintroduction in a future
patch fails to build with a teaching error pointing at the two
overloads.
Cancellation contract:
- send(_:) checks Task.isCancelled before dispatch via
Task.checkCancellation(); after dispatch, cancellation is ignored
and the call completes only when the daemon replies or the
underlying connection is invalidated. Honoring cancellation after
dispatch would re-introduce the same late-commit ambiguity as a
timeout.
- send(_:timeoutForIdempotentRequest:) keeps the existing
reply/timeout/cancellation race semantics, with late replies
dropped silently so reusable clients keep working.
Call-site migrations:
- ContainerClient gains an `xpcSendIdempotent(message:timeout:)`
helper. `create` uses the no-timeout `xpcSend(message:)`; `list`
uses the idempotent helper with its existing 10s bound.
- NetworkClient (APIService) follows the same split: `create` and
`delete` use the no-timeout helper; `list` keeps its 1s bound via
the idempotent helper.
- SandboxClient.create drops its `timeout:` parameter; the only
caller (ContainersService) was already passing the default.
- ClientHealthCheck.ping calls the idempotent overload with a
non-optional Duration. All seven ping callers in ContainerCommands
are unchanged at the call site.
Tests: a new ContainerXPCTests target uses an in-process
`xpc_endpoint_create`-based listener so the contract can be exercised
without a live mach service. Six tests cover both overloads:
- idempotentTimeoutReturnsWithinBound — verifies the .timeout error
code (not .interrupted) and that elapsed time is within the
expected window
- reusableClientSurvivesIdempotentTimeout — same XPCClient instance
survives a timeout and can complete a follow-up send
- lateReplyAfterIdempotentTimeoutIsIgnoredCleanly — server replies
after the client has timed out; subsequent send still works
- plainSendCompletesWhenServerReplies — happy path
- plainSendIgnoresCancellationAfterDispatch — Task.cancel() after
dispatch must NOT short-circuit; the task waits for the reply
- plainSendHonorsCancellationBeforeDispatch — pre-dispatch
cancellation surfaces CancellationError
What this commit does not address:
- No idempotency token or recovery query (Codex's third suggestion).
This commit prevents the unsafe combination at the API; it does not
give callers a way to safely time out a mutating request and then
ask the daemon "did it actually commit?".
- Reusable ContainerClient/NetworkClient mutating calls now have no
timeout (correctly so, per the new contract). Wedged-daemon
scenarios will hang those callers indefinitely; the user-visible
workaround (`launchctl bootout`) remains the only escape today.
Both are reasonable follow-ups but out of scope for closing the freeze
regression.1 parent 9985b32 commit 0654cd1
7 files changed
Lines changed: 422 additions & 33 deletions
File tree
- Sources
- ContainerXPC
- Services
- ContainerAPIService/Client
- ContainerSandboxService/Client
- Tests/ContainerXPCTests
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
428 | 428 | | |
429 | 429 | | |
430 | 430 | | |
| 431 | + | |
| 432 | + | |
| 433 | + | |
| 434 | + | |
| 435 | + | |
| 436 | + | |
431 | 437 | | |
432 | 438 | | |
433 | 439 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
69 | 69 | | |
70 | 70 | | |
71 | 71 | | |
72 | | - | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
73 | 106 | | |
74 | 107 | | |
75 | 108 | | |
76 | 109 | | |
77 | 110 | | |
78 | 111 | | |
79 | | - | |
80 | | - | |
81 | | - | |
82 | | - | |
83 | | - | |
84 | | - | |
85 | | - | |
86 | | - | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
87 | 127 | | |
88 | | - | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
89 | 132 | | |
90 | 133 | | |
91 | 134 | | |
| |||
100 | 143 | | |
101 | 144 | | |
102 | 145 | | |
103 | | - | |
104 | | - | |
105 | | - | |
106 | | - | |
107 | | - | |
108 | | - | |
109 | | - | |
110 | | - | |
111 | | - | |
112 | | - | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
113 | 154 | | |
114 | | - | |
| 155 | + | |
115 | 156 | | |
116 | 157 | | |
117 | 158 | | |
| |||
126 | 167 | | |
127 | 168 | | |
128 | 169 | | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
129 | 187 | | |
130 | 188 | | |
131 | 189 | | |
| |||
Lines changed: 2 additions & 2 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
28 | 28 | | |
29 | 29 | | |
30 | 30 | | |
31 | | - | |
| 31 | + | |
32 | 32 | | |
33 | 33 | | |
34 | | - | |
| 34 | + | |
35 | 35 | | |
36 | 36 | | |
37 | 37 | | |
| |||
Lines changed: 9 additions & 4 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
37 | 37 | | |
38 | 38 | | |
39 | 39 | | |
40 | | - | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
41 | 46 | | |
42 | | - | |
| 47 | + | |
43 | 48 | | |
44 | | - | |
| 49 | + | |
45 | 50 | | |
46 | 51 | | |
47 | 52 | | |
| |||
82 | 87 | | |
83 | 88 | | |
84 | 89 | | |
85 | | - | |
| 90 | + | |
86 | 91 | | |
87 | 92 | | |
88 | 93 | | |
| |||
Lines changed: 9 additions & 4 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
57 | 57 | | |
58 | 58 | | |
59 | 59 | | |
60 | | - | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
61 | 66 | | |
62 | | - | |
| 67 | + | |
63 | 68 | | |
64 | | - | |
| 69 | + | |
65 | 70 | | |
66 | 71 | | |
67 | 72 | | |
| |||
97 | 102 | | |
98 | 103 | | |
99 | 104 | | |
100 | | - | |
| 105 | + | |
101 | 106 | | |
102 | 107 | | |
103 | 108 | | |
| |||
Lines changed: 7 additions & 2 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
47 | 47 | | |
48 | 48 | | |
49 | 49 | | |
50 | | - | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
51 | 56 | | |
52 | 57 | | |
53 | 58 | | |
54 | 59 | | |
55 | 60 | | |
56 | 61 | | |
57 | | - | |
| 62 | + | |
58 | 63 | | |
59 | 64 | | |
60 | 65 | | |
| |||
0 commit comments