# @sync block drops asynchronous errors in favor of synchronous errors

**URL:** https://discourse.julialang.org/t/sync-block-drops-asynchronous-errors-in-favor-of-synchronous-errors/26440
**Category:** Internals & Design
**Created:** [July 16, 2019, 8:53pm UTC](https://discourse.julialang.org/t/sync-block-drops-asynchronous-errors-in-favor-of-synchronous-errors/26440 "2019-07-16T20:53:49Z")
**Posts on this page:** 3
**Page:** 1

<div class="post-metadata">

### Author: ![Jasha10](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jasha10/32/7508_2.png) [@Jasha10](https://discourse.julialang.org/u/Jasha10)
#### Post date: [July 16, 2019, 8:53pm UTC](https://discourse.julialang.org/t/sync-block-drops-asynchronous-errors-in-favor-of-synchronous-errors/26440/1 "2019-07-16T20:53:49Z")

</div>

The `@sync` macro creates an array `Any[]` that is used to keep track of any `@async` tasks that are created within the (lexically-scoped) `@sync` block. After the code in the `@sync` block runs, the `Base.sync_end` function is called. This function waits on each task in the aforementioned array so as to make sure that control does not flow forward until after all of the `@async` tasks have completed.

You can check out the source code for the `@sync` and `@async` macros in the the [task.jl](https://github.com/JuliaLang/julia/blob/e6379daa19c7da91f1cd6d3bcf5424f495d67d02/base/task.jl#L273) source file.

The when the `sync_end` function iterates over this array of `@async` tasks to wait for each one of them, it catches any exceptions raised by the `@async` tasks and wraps them in a `CompositeException`, which is then thrown to `@sync`’s caller. Here is an example of a CompositeException being thrown into outer scope after an asynchronous error within a `@sync` block:

```julia
julia> @sync begin
           t = @async begin
               error("asynchronous error")
           end
       end
ERROR: asynchronous error
error(::String) at ./error.jl:33
macro expansion at ./REPL[1]:3 [inlined]
(::getfield(Main, Symbol("##3#4")))() at ./task.jl:259
Stacktrace:
 [1] sync_end(::Array{Any,1}) at ./task.jl:226
 [2] top-level scope at task.jl:245

```

You can see in the stacktrace that `sync_end` was involved in catching the error raised by the async task. If multiple asynchronous errors arise, they are all included in the exception that is raised:

```julia
julia> @sync begin
           t1 = @async begin
               error("asynchronous error 1")
           end
           t2 = @async begin
               error("asynchronous error 2")
           end
       end
ERROR: asynchronous error 1
error(::String) at ./error.jl:33
macro expansion at ./REPL[3]:3 [inlined]
(::getfield(Main, Symbol("##5#7")))() at ./task.jl:259

...and 1 more exception(s).

Stacktrace:
 [1] sync_end(::Array{Any,1}) at ./task.jl:226
 [2] top-level scope at task.jl:245

```

Note the `... and 1 more exceptions(s)` message, indicating that the second asynchronous error has not disappeared into the void (rather, it is accessible via the fields of the `CompositeException` that was raised).

Of course, synchronous errors are propagated as well:

```julia
julia> @sync begin
           error("synchronous error")
       end
ERROR: synchronous error
Stacktrace:
 [1] error(::String) at ./error.jl:33
 [2] top-level scope at task.jl:244

```

In this case `sync_end` does not appear in the stacktrace, as control never reached the call to `sync_end` at the end of the `@sync` call. Rather, control was interrupted by the `error` in the middle of executing the sync block.

However, I’ve noticed that, when a synchronous error occurs within a `@sync` block, any asynchronous errors that occur are not caught by the call to `sync_end`:

```julia
julia> @sync begin
           t = @async begin
               error("asynchronous error")
           end
           sleep(1)
           error("synchronous error")
       end
ERROR: synchronous error
Stacktrace:
 [1] error(::String) at ./error.jl:33
 [2] top-level scope at task.jl:244

```

The asynchronous error is not caught, and no `CompositeException` is raised, as control never reaches the call to `sync_end`. The call to `sleep(1)` gives the `@async` task `t` time to execute, so the asynchronous error probably occurs before the synchronous error. But the asynchronous error never propagates to the user.

I feel that something is left to be desiered, because the `@sync` block can handle either 1) catching and propagating multiple `@async` errors, or 2) catching and propagating a synchronous error, but it is not able to simultaneously handle asynchronous and synchronous errors.

Should this be considered a bug?

---

<div class="post-metadata">

### Author: ![Jasha10](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jasha10/32/7508_2.png) [@Jasha10](https://discourse.julialang.org/u/Jasha10)
#### Post date: [July 17, 2019, 6:00am UTC](https://discourse.julialang.org/t/sync-block-drops-asynchronous-errors-in-favor-of-synchronous-errors/26440/2 "2019-07-17T06:00:34Z")

</div>

On second thought, a `@sync` macro that collects and rethrows simultaneous synchronous and asynchronous errors would probably be undesirable, as the macro would not be able to immediately propagate the synchronous exceptions that occur; rather it would have to wait to collect any asynchronous exceptions.

---

<div class="post-metadata">

### Author: ![Jasha10](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jasha10/32/7508_2.png) [@Jasha10](https://discourse.julialang.org/u/Jasha10)
#### Post date: [July 17, 2019, 7:25am UTC](https://discourse.julialang.org/t/sync-block-drops-asynchronous-errors-in-favor-of-synchronous-errors/26440/3 "2019-07-17T07:25:04Z")

</div>

I’ve realized that this topic is actually related to the discussions about structured concurrency a la nurseries.  
E.g [@schedule considered harmful](https://discourse.julialang.org/t/schedule-considered-harmful/10540)
