Skip to content

Commit

Permalink
print error and backtrace when a task would otherwise fail silently.
Browse files Browse the repository at this point in the history
fixes #10405

silence an error from this in UnixDomainCM
  • Loading branch information
JeffBezanson committed Mar 6, 2015
1 parent 05226ca commit 6343d50
Show file tree
Hide file tree
Showing 3 changed files with 30 additions and 12 deletions.
36 changes: 26 additions & 10 deletions base/task.jl
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ macro task(ex)
:(Task(()->$(esc(ex))))
end

# schedule an expression to run asynchronously, with minimal ceremony
macro schedule(expr)
expr = :(()->($expr))
:(enq_work(Task($(esc(expr)))))
end

current_task() = ccall(:jl_get_current_task, Any, ())::Task
istaskdone(t::Task) = ((t.state == :done) | (t.state == :failed))
istaskstarted(t::Task) = isdefined(t, :last)
Expand Down Expand Up @@ -58,6 +64,7 @@ function task_done_hook(t::Task)
err = (t.state == :failed)
result = t.result
nexttask = t.last
handled = true

q = t.consumers

Expand All @@ -68,21 +75,36 @@ function task_done_hook(t::Task)
nexttask.state = :runnable
elseif isa(q,Condition) && !isempty(q.waitq)
notify(q, result, error=err)
else
handled = false
end

t.consumers = nothing

isa(t.donenotify,Condition) && notify(t.donenotify, result, error=err)
if isa(t.donenotify,Condition)
handled |= !isempty(t.donenotify.waitq)
notify(t.donenotify, result, error=err)
end

if nexttask.state == :runnable
if err
nexttask.exception = result
end
yieldto(nexttask, result)
else
if err && isa(result,InterruptException) && isdefined(REPL,:interactive_task) &&
REPL.interactive_task.state == :waiting && isempty(Workqueue)
throwto(REPL.interactive_task, result)
if err && !handled
if isa(result,InterruptException) && isdefined(REPL,:interactive_task) &&
REPL.interactive_task.state == :waiting && isempty(Workqueue)
throwto(REPL.interactive_task, result)
end
let bt = catch_backtrace()
# run a new task to print the error for us
@schedule with_output_color(:red, STDERR) do io
print(io, "ERROR (unhandled task failure): ")
showerror(io, result, bt)
println(io)
end
end
end
wait()
end
Expand Down Expand Up @@ -235,12 +257,6 @@ function enq_work(t::Task)
t
end

# schedule an expression to run asynchronously, with minimal ceremony
macro schedule(expr)
expr = :(()->($expr))
:(enq_work(Task($(esc(expr)))))
end

schedule(t::Task) = enq_work(t)

function schedule(t::Task, arg; error=false)
Expand Down
2 changes: 1 addition & 1 deletion doc/stdlib/parallel.rst
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ General Parallel Computing Support

* ``Process``: Wait for a process or process chain to exit. The ``exitcode`` field of a process can be used to determine success or failure.

* ``Task``: Wait for a ``Task`` to finish, returning its result value.
* ``Task``: Wait for a ``Task`` to finish, returning its result value. If the task fails with an exception, the exception is propagated (re-thrown in the task that called ``wait``).

This comment has been minimized.

Copy link
@bjarthur

bjarthur Mar 6, 2015

Contributor

where would it be appropriate to document, perhaps via example, the `t = @async ...; wait(t); t.exception`` syntax you showed in #10405


* ``RawFD``: Wait for changes on a file descriptor (see `poll_fd` for keyword arguments and return code)

Expand Down
4 changes: 3 additions & 1 deletion examples/clustermanager/simple/UnixDomainCM.jl
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,9 @@ end

function manage(manager::UnixDomainCM, id::Int, config::WorkerConfig, op)
if op == :deregister
rm(get(config.userdata)[:sockname])
try

This comment has been minimized.

Copy link
@JeffBezanson

JeffBezanson Mar 6, 2015

Author Member

@amitmurthy is this ok? I got this error in the tests:

     * examples            ERROR (unhandled task failure): unlink: no such file or directory (ENOENT)
 in unlink at fs.jl:98
 in rm at file.jl:57
 in manage at /home/jeff/src/julia/usr/share/doc/julia/examples/clustermanager/simple/UnixDomainCM.jl:72
 in deregister_worker at multi.jl:362
 in anonymous at task.jl:923
rm(get(config.userdata)[:sockname])
end
end
nothing
end
Expand Down

3 comments on commit 6343d50

@amitmurthy
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it just tries to cleanup a unix domain socket entry in the filesystem if present. I'll take a look if it is required at all.

@bjarthur
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i still get

$ julia -e '@async throw(Exception)'

$ echo $?
0

shouldn't it at least print the exception and backtrace? returning a 1 would be great too, but i think we disagree on that.

@JeffBezanson
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My guess is that -e exits immediately after evaluating the expression, without visiting the event loop, so the printing doesn't happen. With -E you'll see the exception, or something like ./julia -e '@async error(); sleep(0)'.

Please sign in to comment.