Skip to content

Commit 7f6d503

Browse files
authored
Merge branch 'master' into kf/globalminworld
2 parents dfa20f4 + c19a6bb commit 7f6d503

File tree

15 files changed

+191
-114
lines changed

15 files changed

+191
-114
lines changed

NEWS.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ New language features
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`).
2525
- Support for Unicode 16 ([#56925]).
26+
- `Threads.@spawn` now takes a `:samepool` argument to specify the same threadpool as the caller.
27+
`Threads.@spawn :samepool foo()` which is shorthand for `Threads.@spawn Threads.threadpool() foo()` ([#57109])
2628

2729
Language changes
2830
----------------

base/condition.jl

Lines changed: 3 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -125,104 +125,20 @@ proceeding.
125125
"""
126126
function wait end
127127

128-
# wait with timeout
129-
#
130-
# The behavior of wait changes if a timeout is specified. There are
131-
# three concurrent entities that can interact:
132-
# 1. Task W: the task that calls wait w/timeout.
133-
# 2. Task T: the task created to handle a timeout.
134-
# 3. Task N: the task that notifies the Condition being waited on.
135-
#
136-
# Typical flow:
137-
# - W enters the Condition's wait queue.
138-
# - W creates T and stops running (calls wait()).
139-
# - T, when scheduled, waits on a Timer.
140-
# - Two common outcomes:
141-
# - N notifies the Condition.
142-
# - W starts running, closes the Timer, sets waiter_left and returns
143-
# the notify'ed value.
144-
# - The closed Timer throws an EOFError to T which simply ends.
145-
# - The Timer expires.
146-
# - T starts running and locks the Condition.
147-
# - T confirms that waiter_left is unset and that W is still in the
148-
# Condition's wait queue; it then removes W from the wait queue,
149-
# sets dosched to true and unlocks the Condition.
150-
# - If dosched is true, T schedules W with the special :timed_out
151-
# value.
152-
# - T ends.
153-
# - W runs and returns :timed_out.
154-
#
155-
# Some possible interleavings:
156-
# - N notifies the Condition but the Timer expires and T starts running
157-
# before W:
158-
# - W closing the expired Timer is benign.
159-
# - T will find that W is no longer in the Condition's wait queue
160-
# (which is protected by a lock) and will not schedule W.
161-
# - N notifies the Condition; W runs and calls wait on the Condition
162-
# again before the Timer expires:
163-
# - W sets waiter_left before leaving. When T runs, it will find that
164-
# waiter_left is set and will not schedule W.
165-
#
166-
# The lock on the Condition's wait queue and waiter_left together
167-
# ensure proper synchronization and behavior of the tasks involved.
168-
169128
"""
170-
wait(c::GenericCondition; first::Bool=false, timeout::Real=0.0)
129+
wait(c::GenericCondition; first::Bool=false)
171130
172131
Wait for [`notify`](@ref) on `c` and return the `val` parameter passed to `notify`.
173132
174133
If the keyword `first` is set to `true`, the waiter will be put _first_
175134
in line to wake up on `notify`. Otherwise, `wait` has first-in-first-out (FIFO) behavior.
176-
177-
If `timeout` is specified, cancel the `wait` when it expires and return
178-
`:timed_out`. The minimum value for `timeout` is 0.001 seconds, i.e. 1
179-
millisecond.
180135
"""
181-
function wait(c::GenericCondition; first::Bool=false, timeout::Real=0.0)
182-
timeout == 0.0 || timeout 1e-3 || throw(ArgumentError("timeout must be ≥ 1 millisecond"))
183-
136+
function wait(c::GenericCondition; first::Bool=false)
184137
ct = current_task()
185138
_wait2(c, ct, first)
186139
token = unlockall(c.lock)
187-
188-
timer::Union{Timer, Nothing} = nothing
189-
waiter_left::Union{Threads.Atomic{Bool}, Nothing} = nothing
190-
if timeout > 0.0
191-
timer = Timer(timeout)
192-
waiter_left = Threads.Atomic{Bool}(false)
193-
# start a task to wait on the timer
194-
t = Task() do
195-
try
196-
wait(timer)
197-
catch e
198-
# if the timer was closed, the waiting task has been scheduled; do nothing
199-
e isa EOFError && return
200-
end
201-
dosched = false
202-
lock(c.lock)
203-
# Confirm that the waiting task is still in the wait queue and remove it. If
204-
# the task is not in the wait queue, it must have been notified already so we
205-
# don't do anything here.
206-
if !waiter_left[] && ct.queue == c.waitq
207-
dosched = true
208-
Base.list_deletefirst!(c.waitq, ct)
209-
end
210-
unlock(c.lock)
211-
# send the waiting task a timeout
212-
dosched && schedule(ct, :timed_out)
213-
end
214-
t.sticky = false
215-
Threads._spawn_set_thrpool(t, :interactive)
216-
schedule(t)
217-
end
218-
219140
try
220-
res = wait()
221-
if timer !== nothing
222-
close(timer)
223-
waiter_left[] = true
224-
end
225-
return res
141+
return wait()
226142
catch
227143
q = ct.queue; q === nothing || Base.list_deletefirst!(q::IntrusiveLinkedList{Task}, ct)
228144
rethrow()

base/experimental.jl

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
module Experimental
1111

1212
using Base: Threads, sync_varname, is_function_def, @propagate_inbounds
13+
using Base: GenericCondition
1314
using Base.Meta
1415

1516
"""
@@ -577,4 +578,112 @@ function task_wall_time_ns(t::Task=current_task())
577578
return end_at - start_at
578579
end
579580

581+
# wait_with_timeout
582+
#
583+
# A version of `wait(c::Condition)` that additionally allows the
584+
# specification of a timeout. This is experimental as it will likely
585+
# be dropped when a cancellation framework is added.
586+
#
587+
# The parallel behavior of wait_with_timeout is specified here. There
588+
# are three concurrent entities that can interact:
589+
# 1. Task W: the task that calls wait_with_timeout.
590+
# 2. Task T: the task created to handle a timeout.
591+
# 3. Task N: the task that notifies the Condition being waited on.
592+
#
593+
# Typical flow:
594+
# - W enters the Condition's wait queue.
595+
# - W creates T and stops running (calls wait()).
596+
# - T, when scheduled, waits on a Timer.
597+
# - Two common outcomes:
598+
# - N notifies the Condition.
599+
# - W starts running, closes the Timer, sets waiter_left and returns
600+
# the notify'ed value.
601+
# - The closed Timer throws an EOFError to T which simply ends.
602+
# - The Timer expires.
603+
# - T starts running and locks the Condition.
604+
# - T confirms that waiter_left is unset and that W is still in the
605+
# Condition's wait queue; it then removes W from the wait queue,
606+
# sets dosched to true and unlocks the Condition.
607+
# - If dosched is true, T schedules W with the special :timed_out
608+
# value.
609+
# - T ends.
610+
# - W runs and returns :timed_out.
611+
#
612+
# Some possible interleavings:
613+
# - N notifies the Condition but the Timer expires and T starts running
614+
# before W:
615+
# - W closing the expired Timer is benign.
616+
# - T will find that W is no longer in the Condition's wait queue
617+
# (which is protected by a lock) and will not schedule W.
618+
# - N notifies the Condition; W runs and calls wait on the Condition
619+
# again before the Timer expires:
620+
# - W sets waiter_left before leaving. When T runs, it will find that
621+
# waiter_left is set and will not schedule W.
622+
#
623+
# The lock on the Condition's wait queue and waiter_left together
624+
# ensure proper synchronization and behavior of the tasks involved.
625+
626+
"""
627+
wait_with_timeout(c::GenericCondition; first::Bool=false, timeout::Real=0.0)
628+
629+
Wait for [`notify`](@ref) on `c` and return the `val` parameter passed to `notify`.
630+
631+
If the keyword `first` is set to `true`, the waiter will be put _first_
632+
in line to wake up on `notify`. Otherwise, `wait` has first-in-first-out (FIFO) behavior.
633+
634+
If `timeout` is specified, cancel the `wait` when it expires and return
635+
`:timed_out`. The minimum value for `timeout` is 0.001 seconds, i.e. 1
636+
millisecond.
637+
"""
638+
function wait_with_timeout(c::GenericCondition; first::Bool=false, timeout::Real=0.0)
639+
ct = current_task()
640+
Base._wait2(c, ct, first)
641+
token = Base.unlockall(c.lock)
642+
643+
timer::Union{Timer, Nothing} = nothing
644+
waiter_left::Union{Threads.Atomic{Bool}, Nothing} = nothing
645+
if timeout > 0.0
646+
timer = Timer(timeout)
647+
waiter_left = Threads.Atomic{Bool}(false)
648+
# start a task to wait on the timer
649+
t = Task() do
650+
try
651+
wait(timer)
652+
catch e
653+
# if the timer was closed, the waiting task has been scheduled; do nothing
654+
e isa EOFError && return
655+
end
656+
dosched = false
657+
lock(c.lock)
658+
# Confirm that the waiting task is still in the wait queue and remove it. If
659+
# the task is not in the wait queue, it must have been notified already so we
660+
# don't do anything here.
661+
if !waiter_left[] && ct.queue == c.waitq
662+
dosched = true
663+
Base.list_deletefirst!(c.waitq, ct)
664+
end
665+
unlock(c.lock)
666+
# send the waiting task a timeout
667+
dosched && schedule(ct, :timed_out)
668+
end
669+
t.sticky = false
670+
Threads._spawn_set_thrpool(t, :interactive)
671+
schedule(t)
672+
end
673+
674+
try
675+
res = wait()
676+
if timer !== nothing
677+
close(timer)
678+
waiter_left[] = true
679+
end
680+
return res
681+
catch
682+
q = ct.queue; q === nothing || Base.list_deletefirst!(q::IntrusiveLinkedList{Task}, ct)
683+
rethrow()
684+
finally
685+
Base.relockall(c.lock, token)
686+
end
687+
end
688+
580689
end # module

base/threadingconstructs.jl

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -440,10 +440,11 @@ function _spawn_set_thrpool(t::Task, tp::Symbol)
440440
end
441441

442442
"""
443-
Threads.@spawn [:default|:interactive] expr
443+
Threads.@spawn [:default|:interactive|:samepool] expr
444444
445445
Create a [`Task`](@ref) and [`schedule`](@ref) it to run on any available
446-
thread in the specified threadpool (`:default` if unspecified). The task is
446+
thread in the specified threadpool: `:default`, `:interactive`, or `:samepool`
447+
to use the same as the caller. `:default` is used if unspecified. The task is
447448
allocated to a thread once one becomes available. To wait for the task to
448449
finish, call [`wait`](@ref) on the result of this macro, or call
449450
[`fetch`](@ref) to wait and then obtain its return value.
@@ -468,6 +469,9 @@ the variable's value in the current task.
468469
!!! compat "Julia 1.9"
469470
A threadpool may be specified as of Julia 1.9.
470471
472+
!!! compat "Julia 1.12"
473+
The same threadpool may be specified as of Julia 1.12.
474+
471475
# Examples
472476
```julia-repl
473477
julia> t() = println("Hello from ", Threads.threadid());
@@ -486,7 +490,7 @@ macro spawn(args...)
486490
ttype, ex = args
487491
if ttype isa QuoteNode
488492
ttype = ttype.value
489-
if ttype !== :interactive && ttype !== :default
493+
if !in(ttype, (:interactive, :default, :samepool))
490494
throw(ArgumentError(LazyString("unsupported threadpool in @spawn: ", ttype)))
491495
end
492496
tp = QuoteNode(ttype)
@@ -507,7 +511,11 @@ macro spawn(args...)
507511
let $(letargs...)
508512
local task = Task($thunk)
509513
task.sticky = false
510-
_spawn_set_thrpool(task, $(esc(tp)))
514+
local tp = $(esc(tp))
515+
if tp == :samepool
516+
tp = Threads.threadpool()
517+
end
518+
_spawn_set_thrpool(task, tp)
511519
if $(Expr(:islocal, var))
512520
put!($var, task)
513521
end

contrib/refresh_checksums.mk

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ CLANG_TRIPLETS=$(filter %-darwin %-freebsd,$(TRIPLETS))
2424
NON_CLANG_TRIPLETS=$(filter-out %-darwin %-freebsd,$(TRIPLETS))
2525

2626
# These are the projects currently using BinaryBuilder; both GCC-expanded and non-GCC-expanded:
27-
BB_PROJECTS=openssl libssh2 nghttp2 mpfr curl libgit2 pcre libuv unwind llvmunwind dsfmt objconv p7zip zlib libsuitesparse openlibm blastrampoline libtracyclient
27+
BB_PROJECTS=openssl libssh2 nghttp2 mpfr curl libgit2 pcre libuv unwind llvmunwind dsfmt objconv p7zip zlib libsuitesparse openlibm blastrampoline libtracyclient mmtk_julia
2828
BB_GCC_EXPANDED_PROJECTS=openblas csl
2929
BB_CXX_EXPANDED_PROJECTS=gmp llvm clang llvm-tools lld
3030
# These are non-BB source-only deps
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
7405afe10033da0431c8fd920a8cbbbf
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
ad3498cfee95bcd088e47c15eb2707f47ced9493881ec356cbeb22f66207406d23a3e3b27e70a00be7c2c755c6651f54f5378ef42bf4d1312c84d589010aab7b

deps/checksums/Distributed-c6136853451677f1957bec20ecce13419cde3a12.tar.gz/md5

Lines changed: 0 additions & 1 deletion
This file was deleted.

deps/checksums/Distributed-c6136853451677f1957bec20ecce13419cde3a12.tar.gz/sha512

Lines changed: 0 additions & 1 deletion
This file was deleted.

deps/checksums/mmtk_julia

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,7 @@ mmtk_julia-f07d66aafc86af84ea988b35335acc9bbc770fa1.tar.gz/md5/38afb5db6d8c55413
44
mmtk_julia-f07d66aafc86af84ea988b35335acc9bbc770fa1.tar.gz/sha512/78525582a46a6baf8d33df7b622e55cf244439afcd7192ba55489c1bc18393d1237d2903d517c610484bf9e2a7338ad31435a9cbf70889d6bcf87c40cec829e5
55
mmtk_julia.v0.30.3+1.x86_64-linux-gnu.tar.gz/md5/631b204574da7062802dac501a4b711f
66
mmtk_julia.v0.30.3+1.x86_64-linux-gnu.tar.gz/sha512/daaed59d08fc49621479ed638dea0aac0cba123986e486571447e8e21e9a098776ce2e87fbd92ddea276782fc44621f23d40fa213296b28e1d4480553c7de4f7
7+
mmtk_julia-c9e046baf3a0d52fe75d6c8b28f6afd69b045d95.tar.gz/md5/73a8fbea71edce30a39a30f31969dd8e
8+
mmtk_julia-c9e046baf3a0d52fe75d6c8b28f6afd69b045d95.tar.gz/sha512/374848b7696b565dea66daa208830581f92c1fcb0138e7a7ab88564402e94bc79c54b6ed370ec68473e31e2bd411bf82c97793796c31d39aafbbfffea9c05588
9+
mmtk_julia.v0.30.4+0.x86_64-linux-gnu.tar.gz/md5/8cdeb14fd69945f64308be49f6912f9c
10+
mmtk_julia.v0.30.4+0.x86_64-linux-gnu.tar.gz/sha512/3692502f65dec8c0971b56b9bf8178641892b390d520cbcd69880d75b7500e6341534d87882246e68998f590f824ec54c18f4b8fb4aa09b8f313de065c48450e

0 commit comments

Comments
 (0)