Skip to content

Commit

Permalink
Make isempty(c::Channel) a non-mutating operation (JuliaLang#36641)
Browse files Browse the repository at this point in the history
Previously, `isempty(c::Channel)` would fall back to `iterate(c) ===
nothing`, which actually consumed a value from the channel.  Instead,
let's just define it in terms of its internal datastructures.
  • Loading branch information
staticfloat committed Jul 15, 2020
1 parent 6f62363 commit 4481500
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 0 deletions.
1 change: 1 addition & 0 deletions base/channels.jl
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,7 @@ on a [`put!`](@ref).
"""
isready(c::Channel) = n_avail(c) > 0
n_avail(c::Channel) = isbuffered(c) ? length(c.data) : length(c.cond_put.waitq)
isempty(c::Channel) = isbuffered(c) ? isempty(c.data) : isempty(c.cond_put.waitq)

lock(c::Channel) = lock(c.cond_take)
unlock(c::Channel) = unlock(c.cond_take)
Expand Down
39 changes: 39 additions & 0 deletions test/channels.jl
Original file line number Diff line number Diff line change
Expand Up @@ -470,6 +470,45 @@ end
@test_throws InvalidStateException Base.check_channel_state(c)
end

# PR #36641
# Ensure that `isempty()` does not mutate a Channel's state:
@testset "isempty(::Channel) mutation" begin
function isempty_timeout(c::Channel)
inner_c = Channel{Union{Bool,Nothing}}()
@async put!(inner_c, isempty(c))
@async begin
sleep(0.01)
if isopen(inner_c)
put!(inner_c, nothing)
end
end
result = take!(inner_c)
if result === nothing
error("isempty() timed out!")
end
return result
end
# First, with a non-buffered channel
c = Channel()
@test isempty_timeout(c)
t_put = @async put!(c, 1)
@test !isempty_timeout(c)
# check a second time to ensure `isempty(c)` didn't just consume the element.
@test !isempty_timeout(c)
@test take!(c) == 1
@test isempty_timeout(c)
wait(t_put)

# Next, with a buffered channel:
c = Channel(2)
@test isempty_timeout(c)
t_put = put!(c, 1)
@test !isempty_timeout(c)
@test !isempty_timeout(c)
@test take!(c) == 1
@test isempty_timeout(c)
end

# issue #12473
# make sure 1-shot timers work
let a = []
Expand Down

0 comments on commit 4481500

Please sign in to comment.