[
https://issues.apache.org/jira/browse/GROOVY-12459?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18128424#comment-18128424
]
ASF GitHub Bot commented on GROOVY-12459:
-----------------------------------------
codecov-commenter commented on PR #2987:
URL: https://github.com/apache/groovy/pull/2987#issuecomment-6250476937
##
[Codecov](https://app.codecov.io/gh/apache/groovy/pull/2987?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache)
Report
:x: Patch coverage is `97.00000%` with `3 lines` in your changes missing
coverage. Please review.
:white_check_mark: Project coverage is 72.4314%. Comparing base
([`d77e6df`](https://app.codecov.io/gh/apache/groovy/commit/d77e6df56a8d59ca30afbfa4c66afb9c685a22db?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache))
to head
([`570c912`](https://app.codecov.io/gh/apache/groovy/commit/570c9124bc963cb457c7d22929cbbcf3a73b6f5b?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache)).
| [Files with missing
lines](https://app.codecov.io/gh/apache/groovy/pull/2987?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache)
| Patch % | Lines |
|---|---|---|
|
[.../org/apache/groovy/runtime/async/AsyncSupport.java](https://app.codecov.io/gh/apache/groovy/pull/2987?src=pr&el=tree&filepath=src%2Fmain%2Fjava%2Forg%2Fapache%2Fgroovy%2Fruntime%2Fasync%2FAsyncSupport.java&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache#diff-c3JjL21haW4vamF2YS9vcmcvYXBhY2hlL2dyb292eS9ydW50aW1lL2FzeW5jL0FzeW5jU3VwcG9ydC5qYXZh)
| 0.0000% | [2 Missing :warning:
](https://app.codecov.io/gh/apache/groovy/pull/2987?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache)
|
|
[...ache/groovy/runtime/async/DefaultAsyncChannel.java](https://app.codecov.io/gh/apache/groovy/pull/2987?src=pr&el=tree&filepath=src%2Fmain%2Fjava%2Forg%2Fapache%2Fgroovy%2Fruntime%2Fasync%2FDefaultAsyncChannel.java&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache#diff-c3JjL21haW4vamF2YS9vcmcvYXBhY2hlL2dyb292eS9ydW50aW1lL2FzeW5jL0RlZmF1bHRBc3luY0NoYW5uZWwuamF2YQ==)
| 93.7500% | [1 Missing :warning:
](https://app.codecov.io/gh/apache/groovy/pull/2987?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache)
|
<details><summary>Additional details and impacted files</summary>
[](https://app.codecov.io/gh/apache/groovy/pull/2987?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache)
```diff
@@ Coverage Diff @@
## master #2987 +/- ##
==================================================
+ Coverage 72.4166% 72.4314% +0.0147%
- Complexity 40606 40631 +25
==================================================
Files 1643 1643
Lines 142633 142673 +40
Branches 26788 26796 +8
==================================================
+ Hits 103290 103340 +50
+ Misses 29850 29841 -9
+ Partials 9493 9492 -1
```
| [Files with missing
lines](https://app.codecov.io/gh/apache/groovy/pull/2987?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache)
| Coverage Δ | |
|---|---|---|
|
[src/main/java/groovy/concurrent/AsyncChannel.java](https://app.codecov.io/gh/apache/groovy/pull/2987?src=pr&el=tree&filepath=src%2Fmain%2Fjava%2Fgroovy%2Fconcurrent%2FAsyncChannel.java&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache#diff-c3JjL21haW4vamF2YS9ncm9vdnkvY29uY3VycmVudC9Bc3luY0NoYW5uZWwuamF2YQ==)
| `100.0000% <100.0000%> (+20.3125%)` | :arrow_up: |
|
[.../main/java/groovy/concurrent/BroadcastChannel.java](https://app.codecov.io/gh/apache/groovy/pull/2987?src=pr&el=tree&filepath=src%2Fmain%2Fjava%2Fgroovy%2Fconcurrent%2FBroadcastChannel.java&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache#diff-c3JjL21haW4vamF2YS9ncm9vdnkvY29uY3VycmVudC9Ccm9hZGNhc3RDaGFubmVsLmphdmE=)
| `73.6000% <100.0000%> (+5.6755%)` | :arrow_up: |
|
[...java/groovy/concurrent/ChannelClosedException.java](https://app.codecov.io/gh/apache/groovy/pull/2987?src=pr&el=tree&filepath=src%2Fmain%2Fjava%2Fgroovy%2Fconcurrent%2FChannelClosedException.java&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache#diff-c3JjL21haW4vamF2YS9ncm9vdnkvY29uY3VycmVudC9DaGFubmVsQ2xvc2VkRXhjZXB0aW9uLmphdmE=)
| `100.0000% <ø> (ø)` | |
|
[src/main/java/groovy/concurrent/ChannelSelect.java](https://app.codecov.io/gh/apache/groovy/pull/2987?src=pr&el=tree&filepath=src%2Fmain%2Fjava%2Fgroovy%2Fconcurrent%2FChannelSelect.java&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache#diff-c3JjL21haW4vamF2YS9ncm9vdnkvY29uY3VycmVudC9DaGFubmVsU2VsZWN0LmphdmE=)
| `90.1408% <100.0000%> (+0.5886%)` | :arrow_up: |
|
[...ache/groovy/runtime/async/DefaultAsyncChannel.java](https://app.codecov.io/gh/apache/groovy/pull/2987?src=pr&el=tree&filepath=src%2Fmain%2Fjava%2Forg%2Fapache%2Fgroovy%2Fruntime%2Fasync%2FDefaultAsyncChannel.java&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache#diff-c3JjL21haW4vamF2YS9vcmcvYXBhY2hlL2dyb292eS9ydW50aW1lL2FzeW5jL0RlZmF1bHRBc3luY0NoYW5uZWwuamF2YQ==)
| `88.4422% <93.7500%> (+1.3454%)` | :arrow_up: |
|
[.../org/apache/groovy/runtime/async/AsyncSupport.java](https://app.codecov.io/gh/apache/groovy/pull/2987?src=pr&el=tree&filepath=src%2Fmain%2Fjava%2Forg%2Fapache%2Fgroovy%2Fruntime%2Fasync%2FAsyncSupport.java&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache#diff-c3JjL21haW4vamF2YS9vcmcvYXBhY2hlL2dyb292eS9ydW50aW1lL2FzeW5jL0FzeW5jU3VwcG9ydC5qYXZh)
| `77.6398% <0.0000%> (-0.9766%)` | :arrow_down: |
... and [11 files with indirect coverage
changes](https://app.codecov.io/gh/apache/groovy/pull/2987/indirect-changes?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache)
</details>
<details><summary> :rocket: New features to boost your workflow: </summary>
- :snowflake: [Test
Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests,
report on failures, and find test suite problems.
- :package: [JS Bundle
Analysis](https://docs.codecov.com/docs/javascript-bundle-analysis): Save
yourself from yourself by tracking and limiting bundle sizes in JS merges.
</details>
> Failed AsyncChannel producer never closes the channel so `for await` hangs
> and the exception is dropped
> -------------------------------------------------------------------------------------------------------
>
> Key: GROOVY-12459
> URL: https://issues.apache.org/jira/browse/GROOVY-12459
> Project: Groovy
> Issue Type: Bug
> Reporter: Daniel Sun
> Priority: Major
>
> h2. Context
> {{AsyncChannel}} is the channel used with {{async}} and {{for await}}. A
> producer sends values and then calls {{close()}} so the consumer can finish.
> That shape shipped with the channel API. Native async/await is GROOVY-9381.
> Later channel work includes GROOVY-12320 ({{ChannelSelect}}) and GROOVY-12343
> ({{AsyncChannel.after}}).
> The proposed change keeps that close and lets it carry the producer failure.
> The code that fails, and the code that reads, each write their own {{try}} /
> {{catch}}.
> h2. Problem
> {code:groovy}
> def channel = AsyncChannel.create(2)
> async {
> for (int i = 1; i <= 3; i++) {
> someCall()
> channel.send(i * i)
> }
> channel.close()
> }
> for await (value in channel) {
> println value
> }
> {code}
> If {{someCall()}} throws, both of these happen:
> * {{channel.close()}} never runs, so {{for await}} waits forever.
> * The exception stays on the {{Awaitable}} returned by {{async}}. The example
> discards that handle, so the consuming code never sees the failure.
> A {{try}} / {{finally}} inside the task, followed by {{await}} on the handle,
> fixes both symptoms, but the consumer is waiting on the channel, not on the
> handle. {{defer}} runs on the way out of the task, including when the task
> throws. A {{defer}} block that calls {{channel.close()}} closes the channel
> with no cause, so {{for await}} ends normally and the exception stays on the
> handle.
> h2. Change
> The producer catches the failure and closes with it. Values already sent are
> still delivered. {{receive}} and {{send}} then fail with
> {{ChannelClosedException}}, and that failure is the cause. {{for await}}
> throws the failure itself after those values, including a checked exception
> or an {{Error}}. The reader writes the {{try}} / {{catch}}, as with C#
> {{await foreach}}, JavaScript {{for await}}, and Swift {{for try await}}.
> {code:groovy}
> def channel = AsyncChannel.create(2)
> async {
> try {
> for (int i = 1; i <= 3; i++) {
> someCall()
> await channel.send(i * i)
> }
> channel.close()
> } catch (Exception failure) {
> channel.close(failure)
> }
> }
> def seen = []
> try {
> for await (value in channel) {
> seen << value
> }
> } catch (Exception failure) {
> // failure is the exception thrown by someCall()
> }
> {code}
> {{close(failure)}} returns nothing. {{null}} is a normal close. The first
> close wins.
> ||What happened||What the consumer sees||
> |{{close()}}, or {{close(null)}}|{{ChannelClosedException}} with no cause.
> {{for await}} ends. {{ChannelSelect}} still treats this as a normal close.|
> |{{close(failure)}} on a channel that retains it|{{receive}} and {{send}}
> fail with {{ChannelClosedException}} whose cause is {{failure}}. {{for
> await}} and {{ChannelSelect}} throw {{failure}} after the values already
> sent.|
> |Every selected channel closed normally|{{ChannelSelect}} fails with a
> {{ChannelClosedException}} whose message is "all channels are closed".|
> |A transfer already committed when another channel closes|That transfer stays
> the result. A withdrawn branch, and {{CancellationException}}, are not
> closes.|
> {{filter}}, {{map}}, {{merge}}, {{split}}, and {{tap}} carry a source
> failure, or a failure thrown by the callback, through to their output.
> Closing one side of a {{split}} stops the split and closes the other side
> normally. A failure close of one side is not copied onto the other.
> {{BroadcastChannel.close(failure)}} closes every subscriber the same way.
> {{for await}} on a subscriber throws that failure, and the publisher view
> reports it with {{onError}}.
> {{getCloseCause()}} reports the retained failure, or {{null}} when the
> channel is open, was closed normally, or does not retain a cause.
> h2. Compatibility
> {{close(Throwable)}} and {{getCloseCause()}} are default methods, added after
> the 6.0.0 channel API. A class compiled against that API does not need to be
> recompiled and does not throw {{AbstractMethodError}}.
> * The default {{close(Throwable)}} calls {{close()}} and does not retain the
> cause. {{for await}} on that channel ends. It does not throw the failure.
> * The channel from {{AsyncChannel.create}} does retain the cause. {{close()}}
> and {{close(Throwable)}} share one close, so neither calls the other. The
> cause is stored before the closed flag, under the same lock.
> * An implementation of {{close()}} must also override {{close(Throwable)}} if
> its {{close()}} calls that method. The default calls {{close()}} again.
> * A resource that lives only inside an {{async}} task is unchanged. Release
> it with {{defer}}, and observe the task with {{await}} or with
> {{scope.async}} inside {{AsyncScope.withScope}}.
--
This message was sent by Atlassian Jira
(v8.20.10#820010)