Repository navigation
GROOVY-12571: end a subscriber that arrives after a BroadcastChannel closed like one attached before - #3105
GROOVY-12571: end a subscriber that arrives after a BroadcastChannel closed like one attached before#3105codeconsole wants to merge 1 commit into
Conversation
…closed like one attached before BroadcastChannel.asPublisher() signalled onError(ChannelClosedException) to a subscriber that arrived after the broadcast had closed. After a normal close, `for await` over the publisher threw ChannelClosedException instead of ending without values. After close(failure) it threw the wrapper instead of the failure. A subscriber attached a moment before the close completes, or receives the failure itself, so the same program either finished or failed depending on whether the consumer subscribed before or after the producer closed. A late subscriber now receives onComplete after a normal close and onError with the retained failure after close(failure), straight after onSubscribe. That matches SubmissionPublisher, Agent.changes() and `for await` over a closed AsyncChannel. BroadcastChannel.subscribe() still throws ChannelClosedException, as before.
🚨 All tests passed but jobs failed 🚨Failed Jobs without Test Failures❌ Build and test / lts (17, macos-latest) 🏷️ Commit: 43ec544 Rerun ControlsClick the checkbox to trigger a rerun:
Learn more about TestLens at testlens.app/docs. |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #3105 +/- ##
==================================================
+ Coverage 73.6711% 73.6788% +0.0077%
- Complexity 42584 42590 +6
==================================================
Files 1678 1678
Lines 146907 146916 +9
Branches 27790 27793 +3
==================================================
+ Hits 108228 108246 +18
+ Misses 28528 28519 -9
Partials 10151 10151
🚀 New features to boost your workflow:
|
daniellansun
left a comment
There was a problem hiding this comment.
GROOVY-12571 behavior change is coherent with Reactive Streams: a late asPublisher() subscriber after close should see the same terminal signal as an in-flight one — onComplete for normal close, onError(cause) for close(failure) — not ChannelClosedException.
Implementation matches closedNow(): normal close has null cause → onComplete; failed close wraps the failure as CCE cause → onError(cause). Docs and tests (subscriberArrivingAfterANormalCloseCompletes / …FailedCloseReceivesTheFailure, including for await / await) are good. Direct subscribe() still throws CCE — correctly left alone.
CI: lts (17, macos-latest) 60m Gradle timeout — please re-run; then APPROVE. Codecov clean.
Compat: callers that caught ChannelClosedException from late publisher subscribe will now see completion/error instead — call that out on the JIRA if not already.
BroadcastChannel.asPublisher()now ends a subscriber that arrives after the broadcast closed the same way as one that was attached before the close. Afterclose()it receivesonComplete, sofor await (x in broadcast.asPublisher())ends without values andawait broadcast.asPublisher()returnsnull. Before, both threwChannelClosedException. Afterclose(failure)it receivesfailureitself, not aChannelClosedExceptionwrapping it. This matchesSubmissionPublisher,Agent.changes()andfor awaitover a closedAsyncChannel, and makes the result independent of whether the consumer subscribed before or after the producer closed.BroadcastChannel.subscribe()still throwsChannelClosedExceptiononce the broadcast is closed. The async channels guide says so.BroadcastChannelshipped in 6.0.0, so this changes behavior there; it is a candidate for master only.Tests
BroadcastChannelAsPublisherTest(marked// GROOVY-12571): a subscriber and afor awaitthat arrive afterclose()and afterclose(failure); they fail without the change.The
groovy.concurrenttests pass (271), as do the Checkstyle and CodeNarc gates.