Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Non-blocking IO using ES6 generators #4557

Closed
mazko opened this issue Sep 12, 2016 · 6 comments
Closed

Non-blocking IO using ES6 generators #4557

mazko opened this issue Sep 12, 2016 · 6 comments
Labels

Comments

@mazko
Copy link

mazko commented Sep 12, 2016

Modern browsers already support ES6 generators natively without polyfills. Example (tested on node, firefox, opera, chrome):

function getch() {
  return new Promise(function(resolve, reject) {
    // emulate user input each 1..3 second
    var monkeySleep = 1000 * (1 + Math.floor(Math.random() * 10))
    setTimeout(() => {
        var ch = 33 + Math.floor(Math.random() * (127 - 33));
        resolve(ch)
    }, monkeySleep);
  });
}

function log(what){
  console.log(what)
}

function* repl() {
  // C: int ch = getch();
  var ch = yield getch();
  log(`Monkey input: < ${String.fromCharCode(ch)} >`);
  // C: int ch = getch();
  yield getch();
  // ensure local context is saved between yelds
  log(`Monkey input2: < ${String.fromCharCode(ch)} >`);

  return Math.random();
}

function* main() {
  log('New monkey: ' + new Date())
  for (;;) {
    // C: int i = return repl();
    var i = yield* repl();
    console.log(i);
    if (i < 0.333) break;
  }
  return 42;
}

function execute(generator, yieldValue) {

  var next = generator.next(yieldValue);

  if (!next.done) {
    next.value.then(
      result => execute(generator, result),
      err => generator.throw(err)
    );
  } else {
    // exit code
    console.log(next.value);
  }

}

execute( main() );
console.log('execute!') 

Lets try rewrite this to EMCC:

test.c

#include <stdio.h>
#include <emscripten.h>


// EMCC ASM HOWTO?: function * _main_impl()
int main_impl()
{
  printf("New monkey\n");
  fflush(stdout);
  int ch;
  for (;;) {
#ifdef __EMSCRIPTEN__
    // EMCC ASM HOWTO?: ch = yield Module.getch()
    ch = -1;
#else
    ch = getc(stdin);
#endif
    if (ch != EOF) {
      printf("Monkey input: < %c >\n", ch);
      fflush(stdout);
    } else {
      break;
    }
  }
  return 42;
}

int main()
{
#ifdef __EMSCRIPTEN__
  EM_ASM(
    (function(){
      function execute(generator, yieldValue) {
        var next = generator.next(yieldValue);
        if (!next.done) {
          next.value.then(
            result => execute(generator, result),
            err => generator.throw(err)
          );
        } else {
          // real exit status code here
          console.log(next.value);
        }
      }
      var main = Module.cwrap(
        'main_impl', // name of C function
        'number', // return type
        []); // argument types
      execute( main() );
    })();
  );
  // Generators limitation: 
  // can't get exit status code right now, 
  // but we can still return promise
  // return -1;
#else
  return main_impl();
#endif
}

pre.js

var Module = {};

Module.noExitRuntime = true;

Module.getch = function(x) {
  return new Promise(function(resolve, reject) {
    // emulate user input each 1..3 second
    var monkeySleep = 1000 * (1 + Math.floor(Math.random() * 10))
    setTimeout(() => {
        var ch = 33 + Math.floor(Math.random() * (127 - 33));
        resolve(ch)
    }, monkeySleep);
  });
}

After compilation emcc --pre-js pre.js test.c -s EXPORTED_FUNCTIONS='["_main", "_main_impl"]' -o test.html i have to do edit manually test.js to declare and use generator specific syntax function * _main_impl() and yield Module.getch(). Any ideas / emcc macros ?

@juj
Copy link
Collaborator

juj commented Sep 20, 2016

Unfortunately even though ES6 JavaScript would support generators, asm.js doesn't, and we are unable to use the yield keyword like it does to simulate synchronous pauses. The upcoming wasm spec will neither have this style of construct, I'm afraid.

Perhaps if this is interesting, it might be possible to add a codegen mode that does those types of manual mods that you need, although I wonder if it will need to do some kind of dependency propagation for those *s? Also, that would drop support for asm.js.

Are you looking to do synchronous stdin keyboard input with C getch() style? That is not yet possible, but with the upcoming synchronous multithreaded filesystem, we will be able to do that.

@saschanaz
Copy link
Collaborator

Maybe this WebAssembly/design#720 issue is related.

@mazko
Copy link
Author

mazko commented Sep 28, 2016

Just wrote a little script (which is far from ideal yet, just works) for this and here are few examples bc | dc

https://en.wikipedia.org/wiki/Bc_(programming_language)

@saschanaz
Copy link
Collaborator

@mazko Not sure what it does, what should be an input and what should be an output?

@mazko
Copy link
Author

mazko commented Sep 29, 2016

It makes each function between stdin [syscall145, syscall3] ... _main generator function () --> function* () + yield *.

emcc main.c -o main.js
node script main.js main.y.js

@stale
Copy link

stale bot commented Aug 30, 2019

This issue has been automatically marked as stale because there has been no activity in the past 2 years. It will be closed automatically if no further activity occurs in the next 7 days. Feel free to re-open at any time if this issue is still relevant.

@stale stale bot added the wontfix label Aug 30, 2019
@stale stale bot closed this as completed Sep 6, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants