Skip to content

Commit 0787a62

Browse files
Make Timer(f, ...) tasks match the stickiness of the parent task. Add spawn kwarg. (#56745)
1 parent ad5533c commit 0787a62

File tree

3 files changed

+18
-4
lines changed

3 files changed

+18
-4
lines changed

NEWS.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,8 @@ New library features
112112
* New `ltruncate`, `rtruncate` and `ctruncate` functions for truncating strings to text width, accounting for char widths ([#55351])
113113
* `isless` (and thus `cmp`, sorting, etc.) is now supported for zero-dimensional `AbstractArray`s ([#55772])
114114
* `invoke` now supports passing a Method instead of a type signature making this interface somewhat more flexible for certain uncommon use cases ([#56692]).
115+
* `Timer(f, ...)` will now match the stickiness of the parent task when creating timer tasks, which can be overridden
116+
by the new `spawn` kwarg. This avoids the issue where sticky tasks i.e. `@async` make their parent sticky ([#56745])
115117
* `invoke` now supports passing a CodeInstance instead of a type, which can enable
116118
certain compiler plugin workflows ([#56660]).
117119
* `sort` now supports `NTuple`s ([#54494])

base/asyncevent.jl

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,7 @@ end
275275

276276
# timer with repeated callback
277277
"""
278-
Timer(callback::Function, delay; interval = 0)
278+
Timer(callback::Function, delay; interval = 0, spawn::Union{Nothing,Bool}=nothing)
279279
280280
Create a timer that runs the function `callback` at each timer expiration.
281281
@@ -285,6 +285,13 @@ callback is only run once. The function `callback` is called with a single argum
285285
itself. Stop a timer by calling `close`. The `callback` may still be run one final time, if the timer
286286
has already expired.
287287
288+
If `spawn` is `true`, the created task will be spawned, meaning that it will be allowed
289+
to move thread, which avoids the side-effect of forcing the parent task to get stuck to the thread
290+
it is on. If `spawn` is `nothing` (default), the task will be spawned if the parent task isn't sticky.
291+
292+
!!! compat "Julia 1.12"
293+
The `spawn` argument was introduced in Julia 1.12.
294+
288295
# Examples
289296
290297
Here the first number is printed after a delay of two seconds, then the following numbers are
@@ -304,7 +311,8 @@ julia> begin
304311
3
305312
```
306313
"""
307-
function Timer(cb::Function, timeout; kwargs...)
314+
function Timer(cb::Function, timeout; spawn::Union{Nothing,Bool}=nothing, kwargs...)
315+
sticky = spawn === nothing ? current_task().sticky : !spawn
308316
timer = Timer(timeout; kwargs...)
309317
t = @task begin
310318
unpreserve_handle(timer)
@@ -319,6 +327,7 @@ function Timer(cb::Function, timeout; kwargs...)
319327
isopen(timer) || return
320328
end
321329
end
330+
t.sticky = sticky
322331
# here we are mimicking parts of _trywait, in coordination with task `t`
323332
preserve_handle(timer)
324333
@lock timer.cond begin

test/channels.jl

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -550,8 +550,11 @@ end
550550
# make sure 1-shot timers work
551551
let a = []
552552
Timer(t -> push!(a, 1), 0.01, interval = 0)
553-
sleep(0.2)
554-
@test a == [1]
553+
@test timedwait(() -> a == [1], 10) === :ok
554+
end
555+
let a = []
556+
Timer(t -> push!(a, 1), 0.01, interval = 0, spawn = true)
557+
@test timedwait(() -> a == [1], 10) === :ok
555558
end
556559

557560
# make sure that we don't accidentally create a one-shot timer

0 commit comments

Comments
 (0)