# Channel doesn't close when bound task exits

**URL:** https://discourse.julialang.org/t/channel-doesnt-close-when-bound-task-exits/111349
**Category:** General Usage
**Created:** [March 8, 2024, 11:42am UTC](https://discourse.julialang.org/t/channel-doesnt-close-when-bound-task-exits/111349 "2024-03-08T11:42:38Z")
**Posts on this page:** 6
**Page:** 1

<div class="post-metadata">

### Author: ![green.nsk](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/green.nsk/32/24014_2.png) [@green.nsk](https://discourse.julialang.org/u/green.nsk)
#### Post date: [March 8, 2024, 11:42am UTC](https://discourse.julialang.org/t/channel-doesnt-close-when-bound-task-exits/111349/1 "2024-03-08T11:42:38Z")

</div>

We have some code that was working fine until we migrated to julia-1.10 and now with Julia 1.10.2 it’s quite flaky. I’ve reduced the code to the example below, which I think shouldn’t violate the assertion, but it does.

The absurd thing is it still kinda works in VScode REPL, but it’s flaky. When I run it in terminal REPL it breaks nearly 100% of the time.

Am I wrong in my expectation this should work? Is this a bug?

```Julia
function repro()
    taskref = Ref{Task}()
    feedback = Channel(_ -> nothing; taskref)
    wait(taskref[])

    # isopen(feedback) && sleep(0.1)
    @assert !isopen(feedback) # this fails unless the line above is uncommented
end

repro()

```

I’m trying this on Linux with julia 1.9.3 and 1.10.2

---

<div class="post-metadata">

### Author: ![Mason](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mason/32/2423_2.png) [@Mason](https://discourse.julialang.org/u/Mason)
#### Post date: [March 8, 2024, 12:15pm UTC](https://discourse.julialang.org/t/channel-doesnt-close-when-bound-task-exits/111349/2 "2024-03-08T12:15:22Z")

</div>

I think this might be a bug. I was going to recommend that you instead `wait` on the `Channel`, but then I ran into this surprise:

```julia-repl
julia> function repro2(cond)
           taskref = Ref{Task}()
           feedback = Channel(_ -> nothing; taskref)
           if cond
               wait(feedback)
           end
           isopen(feedback)
       end;

julia> repro2(false)
true

julia> repro2(true)
ERROR: InvalidStateException: Channel is closed.
Stacktrace:
 [1] try_yieldto(undo::typeof(Base.ensure_rescheduled))
   @ Base ./task.jl:931
 [2] wait()
   @ Base ./task.jl:995
 [3] wait(c::Base.GenericCondition{ReentrantLock}; first::Bool)
   @ Base ./condition.jl:130
 [4] wait
   @ ./condition.jl:125 [inlined]
 [5] wait(c::Channel{Any})
   @ Base ./channels.jl:581
 [6] repro2(cond::Bool)
   @ Main ./REPL[39]:5
 [7] top-level scope

```

so `wait`ing on the `Channel` errors claiming the channel is closed, but calling `isopen` on the channel reports that it’s open!

---

<div class="post-metadata">

### Author: ![green.nsk](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/green.nsk/32/24014_2.png) [@green.nsk](https://discourse.julialang.org/u/green.nsk)
#### Post date: [March 8, 2024, 12:30pm UTC](https://discourse.julialang.org/t/channel-doesnt-close-when-bound-task-exits/111349/3 "2024-03-08T12:30:22Z")

</div>

Yeah, with your demonstration it’s quite clear it’s a bug.

Also, to clarify, this is not a regression from 1.9 to 1.10, the reduced example breaks in both. For some reason, our original code works reliably in 1.9, and flaky in 1.10.

---

<div class="post-metadata">

### Author: ![Mason](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mason/32/2423_2.png) [@Mason](https://discourse.julialang.org/u/Mason)
#### Post date: [March 8, 2024, 2:01pm UTC](https://discourse.julialang.org/t/channel-doesnt-close-when-bound-task-exits/111349/4 "2024-03-08T14:01:40Z")

</div>

Would you mind opening an issue on Github?

---

<div class="post-metadata">

### Author: ![green.nsk](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/green.nsk/32/24014_2.png) [@green.nsk](https://discourse.julialang.org/u/green.nsk)
#### Post date: [March 8, 2024, 2:42pm UTC](https://discourse.julialang.org/t/channel-doesnt-close-when-bound-task-exits/111349/5 "2024-03-08T14:42:50Z")

</div>

I came to conclusion that your suggestion to use `wait(feedback)` is the right way to handle the situation. It’s mildly annoying that `wait(::Channel)` throws, but that’s a separate discussion.

In principle you shouldn’t rely on `isopen()` telling you the “true” state, as in multitasking system, the state can change an instant later. So, you should be using `fetch()`/`take!()`/`wait(::Channel)` are more reliable.

TLDR, it’s not a bug, it’s user error.

---

<div class="post-metadata">

### Author: ![green.nsk](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/green.nsk/32/24014_2.png) [@green.nsk](https://discourse.julialang.org/u/green.nsk)
#### Post date: [March 8, 2024, 2:59pm UTC](https://discourse.julialang.org/t/channel-doesnt-close-when-bound-task-exits/111349/6 "2024-03-08T14:59:20Z")

</div>

It looks like there’s no special magic behind `bind(ch::Channel, t::Task)`. Effectively it does `@async begin ; wait(t) ; close(ch); end` + some exception handling. So `@assert !isopen(feedback)` may or may not fail depending on how exactly tasks get scheduled. This probably explains why our original code got flaky on 1.10, it wasn’t robust to start with.
