Backtrace in Tasks

At work I have to program concurrently. I really love programming in Julia but one thing that annoys me are the backtrace in tasks.

 t = @async begin
     for i in 1:10 end
     error()
 end
 
 t |> dump
 julia> 
 Task
   next: Nothing nothing
   queue: Nothing nothing
   storage: Nothing nothing
   state: Symbol failed
   donenotify: Base.GenericCondition{Base.Threads.SpinLock}
     waitq: Base.InvasiveLinkedList{Task}
       head: Nothing nothing
       tail: Nothing nothing
     lock: Base.Threads.SpinLock
       handle: Base.Threads.Atomic{Int64}
         value: Int64 0
   result: ErrorException
     msg: String ""
   exception: ErrorException
     msg: String ""
   backtrace: Array{Union{Ptr{Nothing}, Base.InterpreterIP}}((7,))
     1: Ptr{Nothing} @0x00000000668ae2e5
     2: Ptr{Nothing} @0x0000000066884d57
     3: Ptr{Nothing} @0x00000000320969f7
     4: Ptr{Nothing} @0x00000000320b0e84
     5: Ptr{Nothing} @0x00000000320b0ea4
     6: Ptr{Nothing} @0x00000000668675fd
     7: Ptr{Nothing} @0x0000000066884bd6
   logstate: Base.CoreLogging.LogState
     min_enabled_level: Base.CoreLogging.LogLevel
       level: Int32 -1
     logger: Atom.Progress.JunoProgressLogger Atom.Progress.JunoProgressLogger()
   code: #15 (function of type var"#15#16")
   sticky: Bool true

What does that mean?

backtrace: Array{Union{Ptr{Nothing}, Base.InterpreterIP}}((7,))
     1: Ptr{Nothing} @0x00000000668ae2e5
     2: Ptr{Nothing} @0x0000000066884d57
     3: Ptr{Nothing} @0x00000000320969f7
     4: Ptr{Nothing} @0x00000000320b0e84
     5: Ptr{Nothing} @0x00000000320b0ea4
     6: Ptr{Nothing} @0x00000000668675fd
     7: Ptr{Nothing} @0x0000000066884bd6

I need to now where the error happened (which line and file) and how it propagates.

Is there a better way or something to look forward to?

Cheers

Try

julia> Base.display_error(t.exception, t.backtrace)
ERROR: nope
Stacktrace:
 [1] error(::String) at ./error.jl:33
 [2] macro expansion at ./REPL[2]:3 [inlined]
 [3] (::var"#13#14")() at ./task.jl:358
2 Likes

This is not how you retrieve the backtrace though. The error gets thrown when you fetch from the task:

julia> fetch(t)
ERROR: TaskFailedException:

Stacktrace:
 [1] error() at ./error.jl:42
 [2] macro expansion at ./REPL[1]:3 [inlined]
 [3] (::var"#3#4")() at ./task.jl:358
Stacktrace:
 [1] wait at ./task.jl:267 [inlined]
 [2] fetch(::Task) at ./task.jl:282
 [3] top-level scope at REPL[1]:1

Inspecting the internals of the Task object will be very unreliable.

1 Like

I don’t want to wait for it to fail. Just spin it of and if something goes wrong (mainly during the development face) I want to see the error.
For me it seems like the tasks live in their own universe…

You still need to synchronize the result somehow. Either via the return value (fetch / wait), or using @sync. Throwing tasks out into the ether will not be reliable.

If you want to print from the task itself you could do

julia> t = @async begin
           try
               for i in 1:10 end
               error()
           catch e
               @error "Task failed" exception=(e, catch_backtrace())
               rethrow()
           end
       end;
┌ Error: Task failed
│   exception =
│
│    Stacktrace:
│     [1] error() at ./error.jl:42
│     [2] macro expansion at ./REPL[6]:4 [inlined]
│     [3] (::var"#9#10")() at ./task.jl:358
└ @ Main REPL[6]:6245:
1 Like

What happens if I don’t fetch or sync them?

Then you code is racy since you won’t know about what progress the task has made (and as you’ve already seen, exceptions will just dissappear).

2 Likes

I know it’s not the prettiest but here’s my solution with the help from @kristoffer.carlsson and @pfitzseb. Thank you!

macro my_async(ex)
    quote
        @async try 
            $(esc(ex))
        catch e
            @error "Task failed" exception=(e, catch_backtrace())
            rethrow()
        end
    end
end