-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
add TaskFailedException
to propagate backtrace of failed task in wait
#32814
Conversation
Interesting. Doing it this way with Thinking out loud, one option would be to import the exception stack from the failed task into the current task. But that seems quite confusing because there's no way to tell which exceptions arose on which task. Another option is to have a Going further, it would be nice if |
+1 This sounds good. |
wait
FailedTaskException
to propagate backtrace of failed task in wait
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks good to me. Needs a couple of tests for show
of FailedTaskException
.
while isa(ex.task.exception, FailedTaskException) | ||
pushfirst!(stacks, ex.task.backtrace) | ||
ex = ex.task.exception | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, this surfaces the underlying error through multiple levels of wait()
. That's nice:
julia> t1 = @async sum()
t2 = @async wait(t1)
wait(t2)
ERROR: FailedTaskException:
MethodError: no method matching sum()
Closest candidates are:
sum(::Tuple{Any,Vararg{Any,N} where N}) at tuple.jl:379
sum(::StepRangeLen) at twiceprecision.jl:536
sum(::AbstractRange{#s66} where #s66<:Real) at range.jl:978
...
Stacktrace:
[1] (::getfield(Main, Symbol("##23#24")))() at ./task.jl:332
Stacktrace:
[1] wait(::Task) at ./task.jl:251
[2] (::getfield(Main, Symbol("##25#26")))() at ./task.jl:332
Stacktrace:
[1] wait(::Task) at ./task.jl:251
[2] top-level scope at REPL[5]:3
rethrow() | ||
if isa(r, Task) | ||
_wait(r) | ||
if istaskfailed(r) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice improvement not having the extra catch and rethrow.
NEWS.md
Outdated
@@ -37,6 +37,8 @@ New library functions | |||
Standard library changes | |||
------------------------ | |||
|
|||
* When `wait` (or `@sync` etc.) is called on a failing `Task`, the exception is propagated as a | |||
`FailedTaskException` wrapping the task ([#32814]). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You might want a few words about why this is an improvement (i.e. you no longer lose the backtrace).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(should also mention fetch
in the docs)
base/task.jl
Outdated
var = esc(sync_varname) | ||
quote | ||
local ref = $(esc(expr)) | ||
if $(Expr(:isdefined, var)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this should perhaps not include the isdefined check and just assert that there is a sync pool?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, right.
Hm, now I notice we added |
Hmm |
I'm not saying to combine them, just use the same word order: ProcessFailedException and TaskFailedException, instead of ProcessFailedException and FailedTaskException. |
+1 to having the word order match: |
FailedTaskException
to propagate backtrace of failed task in wait
TaskFailedException
to propagate backtrace of failed task in wait
if !isa(r, Task) || (isa(r, Task) && !istaskfailed(r)) | ||
rethrow() | ||
if isa(r, Task) | ||
_wait(r) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
x-ref: #32677
If the first task is blocking on a second task and the second task is throwing an exception, we will never see the exception and @sync
will block forever.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 but out of scope for this PR.
@@ -323,12 +323,12 @@ end | |||
ct = current_task() | |||
testerr = ErrorException("expected") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably out of scope for this PR,
but do you think
we should have a custom error type defined in Test stdlib for testing these things,
so it is harder to get false positives from other errors being thrown?
(If so i can open an issue)
TaskFailedException
to propagate backtrace of failed task in wait
TaskFailedException
to propagate backtrace of failed task in wait
79bf746
to
5e6a91a
Compare
Before:
After:
So the location of the failure inside the waited-for task is now visible.
I'm not sure if aCapturedException
is the best way to do this, but it was within easy reach. Maybe it would make sense to use the exception stack somehow? @c42f