Skip to content

Commit

Permalink
Add do-block syntax for Semaphore acquire. Add Semaphore tests (Julia…
Browse files Browse the repository at this point in the history
…Lang#43730)

Co-authored-by: Takafumi Arakaki <[email protected]>
  • Loading branch information
2 people authored and LilithHafner committed Mar 8, 2022
1 parent 2d41efe commit 1d696ba
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 0 deletions.
33 changes: 33 additions & 0 deletions base/lock.jl
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,39 @@ function acquire(s::Semaphore)
return
end

"""
acquire(f, s::Semaphore)
Execute `f` after acquiring from Semaphore `s`,
and `release` on completion or error.
For example, a do-block form that ensures only 2
calls of `foo` will be active at the same time:
```julia
s = Base.Semaphore(2)
@sync for _ in 1:100
Threads.@spawn begin
Base.acquire(s) do
foo()
end
end
end
```
!!! compat "Julia 1.8"
This method requires at least Julia 1.8.
"""
function acquire(f, s::Semaphore)
acquire(s)
try
return f()
finally
release(s)
end
end

"""
release(s::Semaphore)
Expand Down
41 changes: 41 additions & 0 deletions test/misc.jl
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,47 @@ for l in (Threads.SpinLock(), ReentrantLock())
@test try unlock(l) finally end === nothing
end

@testset "Semaphore" begin
sem_size = 2
n = 100
s = Base.Semaphore(sem_size)

# explicit acquire-release form
clock = Threads.Atomic{Int}(1)
occupied = Threads.Atomic{Int}(0)
history = fill!(Vector{Int}(undef, 2n), -1)
@sync for _ in 1:n
@async begin
Base.acquire(s)
history[Threads.atomic_add!(clock, 1)] = Threads.atomic_add!(occupied, 1) + 1
sleep(rand(0:0.01:0.1))
history[Threads.atomic_add!(clock, 1)] = Threads.atomic_sub!(occupied, 1) - 1
Base.release(s)
end
end
@test all(<=(sem_size), history)
@test all(>=(0), history)
@test history[end] == 0

# do-block syntax
clock = Threads.Atomic{Int}(1)
occupied = Threads.Atomic{Int}(0)
history = fill!(Vector{Int}(undef, 2n), -1)
@sync for _ in 1:n
@async begin
@test Base.acquire(s) do
history[Threads.atomic_add!(clock, 1)] = Threads.atomic_add!(occupied, 1) + 1
sleep(rand(0:0.01:0.1))
history[Threads.atomic_add!(clock, 1)] = Threads.atomic_sub!(occupied, 1) - 1
return :resultvalue
end == :resultvalue
end
end
@test all(<=(sem_size), history)
@test all(>=(0), history)
@test history[end] == 0
end

# task switching

@noinline function f6597(c)
Expand Down

0 comments on commit 1d696ba

Please sign in to comment.