# Interrupts in @async code

**URL:** <https://discourse.julialang.org/t/interrupts-in-async-code/95837>\
**Category:** General Usage\
**Tags:** task, async, interruptexception\
**Created:** [March 10, 2023, 3:47am UTC](https://discourse.julialang.org/t/interrupts-in-async-code/95837 "2023-03-10T03:47:06Z")\
**Posts on this page:** 1\
**Page:** 2

<div class="post-metadata">

**Author:** ![ffevotte](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ffevotte/32/6587_2.png) [@ffevotte](https://discourse.julialang.org/u/ffevotte)\
**Post date:** [July 19, 2023, 4:16pm UTC](https://discourse.julialang.org/t/interrupts-in-async-code/95837/21 "2023-07-19T16:16:08Z")

</div>

In the following piece of code, a lot of bookkeeping has to be done (the same kind of bookkeeping that `Base.@sync` does internally), but it allows providing the feature you want without the tasks being aware of each other (i.e. there is no need for the tasks to have a loop that explicitly checks for some `ison[]` variable)

```julia
module Interruptible
const EVENTS_VARNAME = gensym(:events)
const TASKS_VARNAME = gensym(:tasks)

macro sync(body)
    events = esc(EVENTS_VARNAME)
    tasks = esc(TASKS_VARNAME)
    quote
        let
            $events = Channel{Symbol}(2)
            $tasks = Task[]

            $(esc(body))

            for evt in $events
                if evt === :interrupted
                    for t in $tasks
                        istaskdone(t) || schedule(t, :stop, error=true)
                    end
                end

                all(istaskdone, $tasks) && break
            end

            e = CompositeException()
            for t in $tasks
                @assert istaskdone(t)
                istaskfailed(t) && push!(e, TaskFailedException(t))
            end

            isempty(e) || throw(e)
            nothing
        end
    end
end

macro async(name, body)
    name = esc(name)
    body = esc(body)
    events = esc(EVENTS_VARNAME)
    tasks = esc(TASKS_VARNAME)
    quote
        t = $Base.@async try
            $body
        catch e
            if e === :stop
                println($name * " was asked to stop")
            elseif e isa InterruptException
                println($name * " was interrupted")
                put!($events, :interrupted)
            end
            rethrow()
        finally
            println($name * " terminates")
            put!($events, :done)
        end
        push!($tasks, t)
    end
end

end

```

Example:

```julia
julia> Interruptible.@sync begin
           Interruptible.@async "A" begin
               for _ in 1:10
                   println("A is alive")
                   sleep(1.0)
               end
           end
       
           Interruptible.@async "B" begin
               for _ in 1:10
                   println("B is alive")
                   sleep(1.003)
               end
           end
       
           Interruptible.@async "C" begin
               # not a loop, but blocks
               readline()
           end
       end
A is alive
B is alive
^CB was interrupted
B terminates
C was asked to stop
C terminates
A was asked to stop
A terminates
ERROR: TaskFailedException
    nested task error: :stop
    [...]
...and 2 more exceptions.

```

[Previous page](https://discourse.julialang.org/t/interrupts-in-async-code/95837.md?page=1)
