OK, I think I’ve figured it out. Sorry for the noise. Feel free to ignore the 
rest unless you’re interested in what went wrong.

When `uv_async_send` gets called the first time it queues up the notifying of 
the `AsyncCondition`, then we queue up the async task. So when we hit the 
`yield()` we have 2 events queued and the behavior will depend on what order 
they’re popped off. I’m guessing that what’s happening is that the async task 
is running first, so that when the queued AsyncCondition notification runs, it 
unblocks the async task.

One fix would be to `yield()` after the `uv_async_send`, and assume that the 
scheduler will handle the async stuff before rescheduling the main task. 
Better, we can `wait(asynccond)` to be sure that it has fired before we move on.

This also highlights an important difference to keep in mind between a normal 
`Condition` and an `AsyncCondition` -

This will block indefinitely, because `notify` only affects tasks that are 
already waiting:
c = Condition()
notify(c)
wait(c)

but this won’t, because the underlying `notify` action doesn’t actually happen 
until the next yield point:
a = Base.AsyncCondition()
ccall(:uv_async_send, Cint, (Ptr{Void}, ), a.handle)
wait(a)


Below is the least race-condition-prone version I can think of. It doesn’t make 
any assumptions about what gets scheduled when we run `yield()`:

begin # wrapped for easy REPL-pasting

asynccond = Base.AsyncCondition()
startcond = Condition()
endcond = Condition()
n = 0
ccall(:uv_async_send, Cint, (Ptr{Void}, ), asynccond.handle)
wait(asynccond)
@schedule begin
    global n
    notify(startcond)
    n += 1
    wait(asynccond)
    n += 1
    notify(endcond)
end
wait(startcond) # make sure the async task has started
println("waiting, n: $n")
ccall(:uv_async_send, Cint, (Ptr{Void}, ), asynccond.handle)
wait(endcond) # make sure the async task woke up
# if we get here then the waiter was successfully woken
println("woke, n: $n")

end



maybe this will be helpful to someone (or to future me).

-s

> On Jul 26, 2016, at 3:54 PM, Spencer Russell <[email protected]> wrote:
> 
> Another clue - if I add another `yield()` after the first, I get "waiting, n: 
> 2”, which implies that the async task is waking up even though I haven’t 
> called `uv_async_send`.
> 
> Probably what’s going on below is that the `println` is switching to the 
> async task, so it finishes before we’ve run the 2nd `uv_async_send`, so then 
> when we wait on `waitcond` we wait forever.
> 
> So perhaps the question is - why is the async task being woken up without 
> calling `uv_async_send`?
> 
> -s
> 
>> On Jul 26, 2016, at 3:44 PM, Spencer Russell <[email protected] 
>> <mailto:[email protected]>> wrote:
>> 
>> I'm seeing some behavior with `AsyncCondition`s (on 0.5) that I don't 
>> understand. I'm not sure if it's a bug or expected behavior.
>> 
>> Here’s a minimal example that doesn’t work the way I expect:
>> 
>> begin # wrapped for easy REPL-pasting
>> 
>> asynccond = Base.AsyncCondition()
>> waitcond = Condition()
>> n = 0
>> ccall(:uv_async_send, Cint, (Ptr{Void}, ), asynccond.handle) # this seems 
>> like it shouldn't have any effect
>> @async begin
>>     global n
>>     n += 1
>>     wait(asynccond)
>>     n += 1
>>     notify(waitcond)
>> end
>> yield() # give the async task a chance to run
>> println("waiting, n: $n")
>> ccall(:uv_async_send, Cint, (Ptr{Void}, ), asynccond.handle)
>> wait(waitcond)
>> # if we get here then the waiter was successfully woken
>> println("woke, n: $n")
>> 
>> end
>> 
>> Here’s what I’d expect to happen:
>> 
>> The first uv_async_send wouldn’t do anything because there are no tasks 
>> waiting on the AsyncCondition.
>> The async task is added to the scheduler
>> the main task hits the first yield(), which switches to the async task
>> the async task blocks waiting on asynccond, which hands control back to the 
>> main task
>> we print out n from the main task
>> this call to uv_async_send should put the async task back in the schedule 
>> queue
>> the main task blocks waiting on waitcond, which hands control back to the 
>> async task
>> the async task notifies waitcond, which should add the main task back to the 
>> schedule queue, then that task is complete
>> the main task prints the “woke” message
>> 
>> Instead it prints "waiting, n: 1”, then hangs.
>> It seems like the async task is never waking up from waiting on asynccond. 
>> If I remove the first `uv_async_send` or add a `yield()` immediately after 
>> it, then it works as expected.
>> 
>> Any ideas what’s going on?
>> 
>> -s
> 

Reply via email to