# How to wait on a condition for tasks

**URL:** <https://discourse.julialang.org/t/how-to-wait-on-a-condition-for-tasks/48023>\
**Category:** New to Julia\
**Tags:** task\
**Created:** [October 9, 2020, 9:45am UTC](https://discourse.julialang.org/t/how-to-wait-on-a-condition-for-tasks/48023 "2020-10-09T09:45:17Z")\
**Posts on this page:** 11\
**Page:** 1

<div class="post-metadata">

**Author:** ![prototact](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/prototact/32/13847_2.png) [@prototact](https://discourse.julialang.org/u/prototact)\
**Post date:** [October 9, 2020, 9:45am UTC](https://discourse.julialang.org/t/how-to-wait-on-a-condition-for-tasks/48023/1 "2020-10-09T09:45:17Z")

</div>

I am having some practice with tasks, channels and conditions.

I would like to wait on a specific condition with a timeout. If the timeout ends, just `put!` a default value and continue, otherwise `put!` a specific value in the channel.

Instead of using` timedwait`, I decided to start simple by using `wait`.

The following code blocks, specifically at `wait(cond)` in the `compute` definition. Notify returns that all tasks were notified.

What am I doing wrong here?

```julia
function compute(id, chan, cond)
    # status = timedwait(() -> wait(cond), 5)
    # status == :timed_out ? put!(chan, (id,1)) : put!(chan, (id, 1 + 1))
    wait(cond)
    println("Am I blocked $i")
    put!(chan, (id, 1))
    println("Nope.")
end

function main()
    doit = Condition()
    chan = Channel{Tuple{Int,Int}}(10)

    for i in 1:10
        @async compute(i, chan, doit)
    end
    println("Let's notify the tasks.")
    cnt = notify(doit)
    println("Okay that happened for $cnt tasks.")
    [take!(chan) for _ in 1:10]
end

```

---

<div class="post-metadata">

**Author:** ![pbayer](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/pbayer/32/11675_2.png) [@pbayer](https://discourse.julialang.org/u/pbayer)\
**Post date:** [October 9, 2020, 10:32am UTC](https://discourse.julialang.org/t/how-to-wait-on-a-condition-for-tasks/48023/2 "2020-10-09T10:32:40Z")

</div>

```julia
julia> t
Task (failed) @0x000000013bfe58d0
UndefVarError: i not defined
compute(::Int64, ::Channel{Tuple{Int64,Int64}}, ::Base.GenericCondition{Base.AlwaysLockedST}) at /Users/paul/.julia/dev/tmp/wait.jl:5
(::var"#13#14"{Base.GenericCondition{Base.AlwaysLockedST},Channel{Tuple{Int64,Int64}}})() at ./task.jl:356

```

It should read `println("Am I blocked $id")`

---

<div class="post-metadata">

**Author:** ![prototact](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/prototact/32/13847_2.png) [@prototact](https://discourse.julialang.org/u/prototact)\
**Post date:** [October 9, 2020, 10:40am UTC](https://discourse.julialang.org/t/how-to-wait-on-a-condition-for-tasks/48023/3 "2020-10-09T10:40:53Z")

</div>

Whoops. The error was suppressed when calling it from main. Thanks for the help. Should i add a `try...catch` statement to capture this kind of errors?

---

<div class="post-metadata">

**Author:** ![pbayer](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/pbayer/32/11675_2.png) [@pbayer](https://discourse.julialang.org/u/pbayer)\
**Post date:** [October 9, 2020, 10:52am UTC](https://discourse.julialang.org/t/how-to-wait-on-a-condition-for-tasks/48023/4 "2020-10-09T10:52:17Z")

</div>

Let a task fail like any other program. But then you must capture the stacktrace. This is important when working with tasks.  
Otherwise you are lost.

I did it like that:

```julia
t = Ref{Task}()
...

function main()
    doit = Condition()
    chan = Channel{Tuple{Int,Int}}(10)

    global t = @async compute(1, chan, doit)
    println("Let's notify the tasks.")
    cnt = notify(doit)
    println("Okay that happened for $cnt tasks.")
    take!(chan)
end

```

then `julia> main()`,  
then `^C`,  
then `julia> t`

This is sufficient in that case to get to the error.

---

<div class="post-metadata">

**Author:** ![prototact](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/prototact/32/13847_2.png) [@prototact](https://discourse.julialang.org/u/prototact)\
**Post date:** [October 9, 2020, 11:01am UTC](https://discourse.julialang.org/t/how-to-wait-on-a-condition-for-tasks/48023/5 "2020-10-09T11:01:19Z")

</div>

I see, thank you for the advice. Do you happen to know a way to use `timedwait` together with a `Condition` or `Event`, so that a `Task` will wait for the event unless time runs out, in which case it does some default action?

I tried using

```julia
status = timedwait(() -> wait(event), 1)

```

but it does not seem to work.

---

<div class="post-metadata">

**Author:** ![pbayer](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/pbayer/32/11675_2.png) [@pbayer](https://discourse.julialang.org/u/pbayer)\
**Post date:** [October 9, 2020, 11:09am UTC](https://discourse.julialang.org/t/how-to-wait-on-a-condition-for-tasks/48023/6 "2020-10-09T11:09:52Z")

</div>

For me the following prototype worked:

```julia
function receive(ch::Channel, timeout::Real=5.0)
    ev = Base.Event()
    cb(timer) = notify(ev)
    Timer(cb, timeout)
    @async begin
        if isready(ch)
            notify(ev)
        else
            wait(ch)
            notify(ev)
        end
    end
    wait(ev)
    isready(ch) ? take!(ch) : -9999
end

```

What I do here, is to have a condition or an event, then to start two tasks,

1. a timer and
2. a task doing some work (in this case it receives).

Whichever finishes first notifies the event.

Then you wait on the event. And after that you finish your work.

---

<div class="post-metadata">

**Author:** ![prototact](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/prototact/32/13847_2.png) [@prototact](https://discourse.julialang.org/u/prototact)\
**Post date:** [October 9, 2020, 11:13am UTC](https://discourse.julialang.org/t/how-to-wait-on-a-condition-for-tasks/48023/7 "2020-10-09T11:13:06Z")

</div>

By cb(timer) you mean ch(timer), the channel?

---

<div class="post-metadata">

**Author:** ![pbayer](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/pbayer/32/11675_2.png) [@pbayer](https://discourse.julialang.org/u/pbayer)\
**Post date:** [October 9, 2020, 11:17am UTC](https://discourse.julialang.org/t/how-to-wait-on-a-condition-for-tasks/48023/8 "2020-10-09T11:17:54Z")

</div>

no, `cb(timer) = notify(ev)` is a function definition like [here in the documentation](https://docs.julialang.org/en/v1/base/base/#Base.Timer-Tuple%7BFunction,Real%7D).

You can also skip `cb` and simply write:

`Timer(x->notify(ev), timeout)`

---

<div class="post-metadata">

**Author:** ![pbayer](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/pbayer/32/11675_2.png) [@pbayer](https://discourse.julialang.org/u/pbayer)\
**Post date:** [October 9, 2020, 11:53am UTC](https://discourse.julialang.org/t/how-to-wait-on-a-condition-for-tasks/48023/9 "2020-10-09T11:53:01Z")

</div>

~~sorry, after looking at it again it doesn’t seem to work. If it has something in the channel it does not return immediately. I will look at it later and then post a working version. 🤨~~

~~I shouldn’t post prototypes. I’m sorry.~~

Now I posted a working version.

---

<div class="post-metadata">

**Author:** ![prototact](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/prototact/32/13847_2.png) [@prototact](https://discourse.julialang.org/u/prototact)\
**Post date:** [October 9, 2020, 12:09pm UTC](https://discourse.julialang.org/t/how-to-wait-on-a-condition-for-tasks/48023/10 "2020-10-09T12:09:11Z")

</div>

It took another option that does not use a condition or event.

```julia
function compute(id, chan)
    t = Task(() -> sleep(rand(1:10)))
    schedule(t)
    status = timedwait(() -> istaskdone(t), 5)
    (status == :timed_out) ? put!(chan, (id,1)) : put!(chan, (id, 1 + 1))
end

function main()
    chan = Channel{Tuple{Int, Int}}(10)

    for i in 1:10
        @async compute(i, chan)
    end
    [take!(chan) for _ in 1:10]
end

```

---

<div class="post-metadata">

**Author:** ![pbayer](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/pbayer/32/11675_2.png) [@pbayer](https://discourse.julialang.org/u/pbayer)\
**Post date:** [October 9, 2020, 12:33pm UTC](https://discourse.julialang.org/t/how-to-wait-on-a-condition-for-tasks/48023/11 "2020-10-09T12:33:02Z")

</div>

ok, it works and it is the conventional solution.

I remember that i did not go for [`timedwait()`](https://docs.julialang.org/en/v1/base/parallel/#Base.timedwait) because it polls and it has the **delay** of the polling. Since I wanted to be more responsive, I figured out something else.
