Skip to content

Commit

Permalink
Set OutputStream.delegate to nil in HTTPBodyOutputStreamBridge.deinit (
Browse files Browse the repository at this point in the history
…#46)

### Motivation

When running the cancellation tests in a loop, very occasionally there
would be a crash with the following backtrace:

```
#0	0x000000018aa008d4 in objc_opt_respondsToSelector ()
#1	0x000000018aea3410 in _outputStreamCallbackFunc ()
#2	0x000000018aea3310 in _signalEventSync ()
#3	0x000000018aeecdb0 in ___signalEventQueue_block_invoke ()
#4	0x000000018abe6cb8 in _dispatch_call_block_and_release ()
#5	0x000000018abe8910 in _dispatch_client_callout ()
#6	0x000000018abefea4 in _dispatch_lane_serial_drain ()
#7	0x000000018abf0a08 in _dispatch_lane_invoke ()
#8	0x000000018abfb61c in _dispatch_root_queue_drain_deferred_wlh ()
#9	0x000000018abfae90 in _dispatch_workloop_worker_thread ()
#10	0x000000018ad96114 in _pthread_wqthread ()
```

This seems to indicate that the output stream is trying to access its
delegate. However, when running with debug logging enabled I can see
that the delegate has already been deinitialized.

This is likely a result of the delegate itself owning the stream and
setting the stream delegate to `self`, which IIUC is an established
pattern. This presents a race in teardown.

### Modifications

This patch sets the output stream delegate to `nil` in the delegate
`deinit`.

### Result

No attempts to call the delegate will happen after it is has been
deinitailzed.

### Test Plan

With this patch, the failing test passes when run an order of magnitude
more times than were required to reliably reproduce the crash without
the patch.
  • Loading branch information
simonjbeaumont committed Jan 16, 2024
1 parent 3d1f6e7 commit 6efbfda
Showing 1 changed file with 4 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,10 @@ final class HTTPBodyOutputStreamBridge: NSObject, StreamDelegate {
self.outputStream.open()
}

deinit { debug("Output stream delegate deinit") }
deinit {
debug("Output stream delegate deinit")
self.outputStream.delegate = nil
}

func performAction(_ action: State.Action) {
debug("Output stream delegate performing action from state machine: \(action)")
Expand Down

0 comments on commit 6efbfda

Please sign in to comment.