Async programming mode in Hack(php)

Hack(Php) supports a very interesting programming mode called Async. The idea is to run IO related function asynchronously, in the single same main php thread. It is called cooperative multi-tasking. The sample code is as follows:


namespace Hack\UserDocumentation\Async\Intro\Examples\Curl;

async function curl_A(): Awaitable<string> {
  $x = await \HH\Asio\curl_exec("");
  return $x;

async function curl_B(): Awaitable<string> {
  $y = await \HH\Asio\curl_exec("");
  return $y;

async function async_curl(): Awaitable<void> {
  $start = microtime(true);
  list($a, $b) = await \HH\Asio\v(array(curl_A(), curl_B()));
  $end = microtime(true);
  echo "Total time taken: " . strval($end - $start) . " seconds" . PHP_EOL;


In this example, we defined two async functions, each of which perform a time-consuming IO operation (curl). Through “await \HH\Asio\v(array(curl_A(), curl_B()))”, we basically be able to call the two async function simultaneously, i.e., parallelized two io operations. Note in each function, we directly return $x (or $y), whose type is “string”, even through the return type is specified as “Awaitable<string>”, as Awaitable<T> is something generated by HHVM automatically.

Here is my understanding on how Async is realized in Hack

  1. When a function is defined as “async”, it always return a “Waitable<T>” object. Inside the function, the programmer still return whatever type “T” result it want to return. This Waitable<T> should be automatically added by HHVM or HACK compiler.
  2. Without await function, The execution flow will not be stalled, the program will run synchronously until the async function returned, and the return value is a “Waitable”. If there is a real sync operation inside the called async function. A task will be saved in HHVM’s internal state, the task will be linked with the returned “Waitable<T>”.
  3. When “$x = await Waitable<T>” is called, the thread stop execution for current logic branch, until the underneath sync operation finished and Waitable<T>’s true value can be yielded.
  4. HHVM ueses \HH\Asio\v (for vector) and \HH\Asio\m (for map) to support schedule multiple async function call (i.e., multiple Waitables) logic branch all together, and be able to switch between when one got blocked. This is the key entry point for so called cooperative multi-tasking.  And one that certainly recursively constructive multiple level of sync calls under each execution logic branch. 
  5. There need to be some special handle for the final real “sync” operation, e.g., \HH\Asio\curl_exec as we shown in the example. Normal sync calls (mysql operation, sleep, curl) will not works and will block the entire thread and in-valid Sync purpose. I guess a special version of each regular sync call should be implemented within HHVM, which may be called “Extension” in HHVM document.
  6. There is a different between join and await. await only wait for current Awaitable object to be materialized, and allows HHVM to switch execution branch for other task, while “join” does not allows the switch. Therefore, “join” is usually used in top level un-async function, e.g.,  main().

Async is a very interesting mode I only see in Hack. It solves the problem how to parallelized time-consuming IO operations in an elegant way, at the programing language level. Usually in Java, we have to rely on user-space multi-threading to solve this kind of problem.

Async programming mode in Hack(php)