-
-
Couldn't load subscription status.
- Fork 5.7k
Description
When an exception happens in a task, a task TaskFailedException is thrown. But packages that accept a user-provided function and call it in a task (like DataFrames) generally don't want to throw this exception type, but rethrow the original exception that happened in user code instead. Otherwise, changing the implementation (e.g. adding multithreading support) would change the user-visible behavior and break the API.
Currently there doesn't seem to be a good way to do this. The package ExceptionUnwrapping.jl has even been created to work around that.
The best mitigation I could find is to use try... catch to catch the TaskFailedException, and then call throw on the task's exception. This throws the original exception type, which allows preserving the API. But the backtrace refers to the place where the exception was rethrown rather than to the original place where the error happened, and users have to look at the third nested exception to see the most useful backtrace.
Would there be a way to preserve the original backtrace? If not, wouldn't it be useful to allow this kind of pattern?
# User passes custom function f to the package
julia> f(x) = nonexistent(x)
f (generic function with 1 method)
# Default: TaskFailedException
julia> fetch(Threads.@spawn f(1))
ERROR:
TaskFailedException
Stacktrace:
[1] wait
@ ./task.jl:317 [inlined]
[2] fetch(t::Task)
@ Base ./task.jl:332
[3] top-level scope
@ threadingconstructs.jl:179
nested task error: UndefVarError: nonexistent not defined
Stacktrace:
[1] f(x::Int64)
@ Main ./REPL[1]:1
[2] (::var"#1#2")()
@ Main ./threadingconstructs.jl:169
# Best workaround I could find
# Note that user function f only appears in the third backtrace
julia> try
t = Threads.@spawn f(1)
fetch(t)
catch
throw(t.exception)
end
ERROR: UndefVarError: t not defined
Stacktrace:
[1] top-level scope
@ REPL[3]:5
caused by: TaskFailedException
Stacktrace:
[1] wait
@ ./task.jl:317 [inlined]
[2] fetch(t::Task)
@ Base ./task.jl:332
[3] top-level scope
@ REPL[3]:3
nested task error: UndefVarError: nonexistent not defined
Stacktrace:
[1] f(x::Int64)
@ Main ./REPL[1]:1
[2] (::var"#3#4")()
@ Main ./threadingconstructs.jl:169