Skip to content
Lu Wang edited this page Jul 23, 2014 · 13 revisions

ASYNCIFY allows you to use some asynchronous function in C, through several transformation of LLVM IR.

Note: this feature has not landed yet

Intro

If you call sleep() in C/C++, what kind of JavaScript code would you expect emscripten to produce?

// test.c
#include <stdio.h>
#include <unistd.h>
int main() {
  int i = 100;
  printf("Hello\n");
  sleep(1);
  printf("World %d!\n");
  return 0;
}

Note that we cannot implement sleep like this:

function sleep(ms) {
  var t = Date.now() + ms;
  while(Date.now() < t) ;
}

because this would block the JavaScript engine, such that pending events cannot be processed.

A hand written counterpart in JavaScript would be

function main() {
  var i = 100;
  console.log('Hello');
  setTimeout(function() {
    console.log('World ' + i + '!');
    async_return_value = 0;
  }, 1000);
}

Specifically, a number of aspects should be taken into consideration:

  • Split the function when an async function is called, and the second function should be registered as the callback for the async function
  • Any function that calls an async function also becomes an async function.
  • Keep all local variables available to the callback
  • Closure cannot be used in order to make asm.js validated code.
  • Take care of loops and branches
  • Make the return value available to the callee
  • Some functions could be both sync or async, depending on the input.

And the ASYNCIFY option does all above automatically, through a number of transformations on LLVM IR.

Usage

Call emscripten_sleep() whenever you need to pause the program, and add -s ASYNCIFY=1 to emscripten.

Sometimes it's a good replacement of emscripten_set_main_loop, you may replace all sleep-alike functions with emscripten_sleep, instead of refactoring the whole main loop.

Extensions

Limitations

Code size increase should be expected, depending on the specific input. -Os (or -Oz for linking) is recommended when ASYNCIFY is turned on. E.g. usually the following loop is expanded to speed up:

for(int i = 0; i < 3; ++i) {
  // do something
  emscripten_sleep(1000);
  // do something else
}

However by expanding the loop, two more async calls are introduced, such that more callback functions will be produced during the asyncify transformation.

setjmp/longjmp and C++ exception are not working well when there are async function calls in the scope, but they still work when there's no async calls. E.g.

try {
  // do something
  if(error) throw 0; // works
  emscripten_sleep(1000);
  // do something else
  if(error) throw 0; // does not work
 }

Other possible implementations

  • Closures (breaking asm.js)
  • Generators (too slow currently)
  • Blocking message (in workers)