List of possible race conditions - acrylic-origami/HHReactor Wiki

What Hack doesn't guarantee

The Hack langspec on async functions unfortunately doesn't make many guarantees about async behavior, which opens up the possibility of race conditions that may be virtually impossible in practice but are possible by the spec. The most significant fallout is on ordering between events in parallel coroutines, which is understandable from a specification standpoint, but in many cases produces very unintuitive and surprising behavior. Below are the most relevant specifications (as of HHVM 3.19):

  1. returning from an async function or block could throw control to any pending Awaitable (although it "typically" returns control to the caller). Spec
  2. When control hits an await statement, control could be given to resume the async function that produced the Awaitable if it is resumable. Because of 1., this means that if that function happens to then returns synchronously, control could go anywhere. Spec

Generally, when multiple resumable await statements are queued in parallel in the scheduler, there are no guarantees which will get control next; there is no dependence on which resolved first or last. This is "deliberately not specified".

Whether or not ConditionWaitHandle::{succeed, fail} will give control back to the calling scope immediately isn't specified in the langspec either. Anecdotally, it has never happened in testing so far, but without specification this could be subject to change unpredictably in the future [right?].


To actually do anything interesting with reasonable simplicity, the HHReactor implementation has had to rely on the following assumptions in limited places:

  1. If control does go to the async function after an await statement and the async function returns, control always returns to the calling scope.
  2. ConditionWaitHandle::{succeed, fail} gives control back to the calling scope immediately, as though they are synchronous function calls.

List of race conditions