Skip to content

Commit 9680219

Browse files
add @acquire
1 parent 13f446e commit 9680219

File tree

4 files changed

+84
-0
lines changed

4 files changed

+84
-0
lines changed

NEWS.md

+1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ New language features
2222
The available metrics are:
2323
- actual running time for the task (`Base.Experimental.task_running_time_ns`), and
2424
- wall-time for the task (`Base.Experimental.task_wall_time_ns`).
25+
- New `Base.@acquire` macro for a non-closure version of `Base.acquire(f, s::Base.Semaphore)`, like `@lock`. ([#xxx])
2526

2627
Language changes
2728
----------------

base/lock.jl

+64
Original file line numberDiff line numberDiff line change
@@ -479,6 +479,70 @@ function acquire(f, s::Semaphore)
479479
end
480480
end
481481

482+
"""
483+
@acquire s::Semaphore expr
484+
485+
Execute `f` after acquiring from Semaphore `s`,
486+
and `release` on completion or error.
487+
488+
For example, a do-block form that ensures only 2
489+
calls of `foo` will be active at the same time:
490+
491+
```julia
492+
s = Base.Semaphore(2)
493+
@sync for _ in 1:100
494+
Threads.@spawn begin
495+
Base.acquire(s) do
496+
foo()
497+
end
498+
end
499+
end
500+
```
501+
502+
!!! compat "Julia 1.8"
503+
This method requires at least Julia 1.8.
504+
505+
"""
506+
function acquire(f, s::Semaphore)
507+
acquire(s)
508+
try
509+
return f()
510+
finally
511+
release(s)
512+
end
513+
end
514+
515+
"""
516+
@acquire s::Semaphore expr
517+
518+
Macro version of `Base.acquire(f, s::Semaphore)` but with `expr` instead of `f` function.
519+
Expands to:
520+
```julia
521+
Base.acquire(s)
522+
try
523+
expr
524+
finally
525+
Base.release(s)
526+
end
527+
```
528+
This is similar to using [`acquire`](@ref) with a `do` block, but avoids creating a closure
529+
and thus can improve the performance.
530+
531+
!!! compat
532+
`@acquire` was added in Julia 1.12
533+
"""
534+
macro acquire(s, expr)
535+
quote
536+
temp = $(esc(s))
537+
Base.acquire(temp)
538+
try
539+
$(esc(expr))
540+
finally
541+
Base.release(temp)
542+
end
543+
end
544+
end
545+
482546
"""
483547
release(s::Semaphore)
484548

base/public.jl

+1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ public
2626
# Semaphores
2727
Semaphore,
2828
acquire,
29+
@acquire,
2930
release,
3031

3132
# arrays

test/misc.jl

+18
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,24 @@ end
237237
@test all(<=(sem_size), history)
238238
@test all(>=(0), history)
239239
@test history[end] == 0
240+
241+
# macro form
242+
clock = Threads.Atomic{Int}(1)
243+
occupied = Threads.Atomic{Int}(0)
244+
history = fill!(Vector{Int}(undef, 2n), -1)
245+
@sync for _ in 1:n
246+
@async begin
247+
@test Base.@acquire s begin
248+
history[Threads.atomic_add!(clock, 1)] = Threads.atomic_add!(occupied, 1) + 1
249+
sleep(rand(0:0.01:0.1))
250+
history[Threads.atomic_add!(clock, 1)] = Threads.atomic_sub!(occupied, 1) - 1
251+
return :resultvalue
252+
end === :resultvalue
253+
end
254+
end
255+
@test all(<=(sem_size), history)
256+
@test all(>=(0), history)
257+
@test history[end] == 0
240258
end
241259

242260
# task switching

0 commit comments

Comments
 (0)