# Multithreading and "silent errors"

**URL:** <https://discourse.julialang.org/t/multithreading-and-silent-errors/78983>\
**Category:** New to Julia\
**Tags:** debug, multithreading\
**Created:** [April 4, 2022, 5:24am UTC](https://discourse.julialang.org/t/multithreading-and-silent-errors/78983 "2022-04-04T05:24:09Z")\
**Posts on this page:** 2\
**Page:** 1

<div class="post-metadata">

**Author:** ![unis](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/unis/32/19458_2.png) [@unis](https://discourse.julialang.org/u/unis)\
**Post date:** [April 4, 2022, 5:24am UTC](https://discourse.julialang.org/t/multithreading-and-silent-errors/78983/1 "2022-04-04T05:24:09Z")

</div>

Consider the following MWE:

```julia
using Base.Threads: @spawn

function f()
    error("Some error")
end

function g()
    while true
        sleep(1)
    end
end

@info "Initialize multi-threaded processing ($(Threads.nthreads()) threads available)."
@sync begin
    @spawn f()
    @spawn g()
end

```

If I run the above code, the error will occur but remain silent. This is inconvenient for debugging.

In this [thread](https://discourse.julialang.org/t/debugging-threads-spawn-applied-to-expr-data/61359.), it has been suggested to work with `@macroexpand`. Interestingly, if I put `@macroexpand` in front of the faulty function `f()`, the error remains silent; if however I put it in front of `g()`, the error is shown on the CLI.

This leads me to two questions:

- If I need to debug such a multithreaded code, what is the best way to make errors occurring within tasks visible in the CLI? Indeed using @macroexpand, or are there cleaner ways?
- If indeed the best way is to use @macroexpand, where exactly should it be placed?

---

<div class="post-metadata">

**Author:** ![Sukera](https://avatars.discourse-cdn.com/v4/letter/s/ce7236/32.png) [@Sukera](https://discourse.julialang.org/u/Sukera)\
**Post date:** [April 4, 2022, 6:39am UTC](https://discourse.julialang.org/t/multithreading-and-silent-errors/78983/2 "2022-04-04T06:39:43Z")

</div>

> [@unis](#):
>
> In this [thread](https://discourse.julialang.org/t/debugging-threads-spawn-applied-to-expr-data/61359.), it has been suggested to work with `@macroexpand` . Interestingly, if I put `@macroexpand` in front of the faulty function `f()` , the error remains silent; if however I put it in front of `g()` , the error is shown on the CLI.
> 
> […]
> 
> - If indeed the best way is to use @macroexpand, where exactly should it be placed?

`@macroexpand` only shows you what an expanded macro (which is an AST transform, it replaces syntax with other, custom syntax) looks like. Placing it in front of `@spawn` only returns what that expanded form looks like, the code isn’t run anymore.

> [@unis](#):
>
> This leads me to two questions:
> 
> - If I need to debug such a multithreaded code, what is the best way to make errors occurring within tasks visible in the CLI? Indeed using @macroexpand, or are there cleaner ways?

First, `@sync` and `Threads.@spawn` are only related insofar they’re both managing `Task`s. The former is about tasks in general (though originally intended for single-threaded, asynchronous code using `@async`, it also works with tasks spawned by `@spawn`), the latter is for spawning a `Task` on different threads other than the original one.

Second, `@sync` captures all exceptions that occur in spawned tasks inside of itself, as documented by its docstring:

```julia
help?> @sync
  @sync

  Wait until all lexically-enclosed uses of @async, @spawn, @spawnat and
  @distributed are complete. All exceptions thrown by enclosed async
  operations are collected and thrown as a CompositeException.

```

So you’d only get the exception once `@sync` determines that all enclosed tasks have finished. In your example, that wouldn’t ever be the case due to the infinite loop. If you want to get those exceptions earlier, you’ll have to `try/catch` or `wait(task)` and handle them appropriately. This may (depending on your usecase) require additional communication, locking and/or synchronization with your long running task, so I’d recommend structuring your long running task with periodic checks for messages in e.g. a `Channel`, to communicate that it should e.g. terminate, change behavior…

In general though, I’d recommend checking if you can avoid only testing your program in the presence of multithreading. If possible, try to test & find edge cases on a single thread, and only use multithreading when you’re reasonably certain that you’ve handled what can go wrong.
