Shouldn’t I get notified it a inner task fails?
Yes you should:
julia> t = @async begin
sleep(1)
error("FAIL")
end
Task (runnable) @0x00007f0fd9203820
julia> fetch(t)
ERROR: TaskFailedException:
FAIL
Stacktrace:
[1] error(::String) at ./error.jl:33
[2] macro expansion at ./REPL[1]:3 [inlined]
[3] (::var"#5#6")() at ./task.jl:333
Stacktrace:
[1] wait at ./task.jl:251 [inlined]
[2] fetch(::Task) at ./task.jl:266
[3] top-level scope at REPL[2]:1
julia> t = Threads.@spawn begin
sleep(1)
error("FAIL")
end
Task (runnable) @0x00007f0fd92004f0
julia> fetch(t)
ERROR: TaskFailedException:
FAIL
Stacktrace:
[1] error(::String) at ./error.jl:33
[2] macro expansion at ./REPL[3]:3 [inlined]
[3] (::var"#7#8")() at ./threadingconstructs.jl:113
Stacktrace:
[1] wait at ./task.jl:251 [inlined]
[2] fetch(::Task) at ./task.jl:266
[3] top-level scope at REPL[4]:1
Or are we not speaking of the same thing?
Thank you for the quick anwser!
julia> Threads.@spawn @async error("Fail")
Task (done) @0x0000000010821fb0
I did this.
You need to fetch the result.
Shouldn’t this fire anyway?
I think it should. We need structured concurrency for this:
https://github.com/JuliaLang/julia/issues/33248
Until we get structured concurrency, making sure to wrap @async and @spawn always with @sync is the closest thing you can do.
How would I use @sync here?
function t()
@async error("")
return 1
end
t()
Either return the task and wait for it or put @sync in t. @sync syncs the tasks that are lexically enclosed.
Treating something like this safely requires so-called nursery (also known as task group and a few other names). ATM, if you want a task to live longer than the function creating it, you need to manually pass it back to the caller. It’s not safe (you may forget doing so). That’s why I think we need structured concurrency.
I think I don’t mind the unstructured concurrency. But it’s very important to have a stacktrace if something fails! Where is the right place to say that?
In what task should the error be thrown?
I think the whole program should stop running unless you started the task in a try-catch-block.
I think the error should be thrown in the most inner task and propagate out until it reaches a try-catch-block or crashes the program.
The stack trace should go from the occurred error through the started task and so on.
These are my first thoughts ![]()
That is structured concurrency.
Would this
function f()
@async sleep(10)
return 1
end
try f() |> println catch end
dont_have_to_wait_and_do_the_next_thing()
still work with structured concurrency?
You’d write something like
function f(nursery)
@async nursery sleep(10)
return 1
end
try
withnursery() do nursery
f(nursery) |> println
dont_have_to_wait_and_do_the_next_thing()
end
catch
end
I don’t understand the syntax. What is withnursery() and where is it defined?
The syntax gets more complicated. What’s the point against:
It’s bad because the caller can’t tell if your function or any function that might exist in the call stack starting from your function may leak un-synchronized tasks; i.e., it violates so-called “black box rule”.
For more information, please have a look at Julep Juleps/StructuredConcurrency.md at master · JuliaLang/Juleps · GitHub and the discussion in the PR. Your point is related to this discussion on effect system which is effectively a dynamically-scoped nursery: Taking Structured Concurrency Seriously · Issue #33248 · JuliaLang/julia · GitHub
I’m not sure, what I want. Both sites have their benefits…
This is my solution as far:
macro my_async(ex)
quote
@async try $(esc(ex))
catch e
println("IN @my_async")
# Here I would like to get the definition line number of `ex`
@show e
end
end
end
So now I get feedback if a task fails.
FWIW, you can improve the error printing with
macro my_async(ex)
quote
@async try
$(esc(ex))
catch e
println("In task:")
@error(exception = (e, catch_backtrace()))
end
end
end