Thursday, May 9, 2013

Green threads in the browser in 20 lines of Wat

This page shows 5 independent, cooperatively scheduled Wat green threads (view source for full Wat code).

Each thread has an ID and is defined as a function that loops forever, repeatedly printing its ID, and then sleeping for a (randomly long) while.
(define (run-thread (id String))
    (@appendChild (.body $document)
                  (@createTextNode $document (+ "Active thread: " id " ")))
    (sleep (* 1000 (@random $Math)))))
So, how can a Wat thread sleep inside a loop when JavaScript forbids sleeping? Why, with good ole delimited continuations:

To spawn a thread, I just wrap the call to RUN-THREAD in a prompt (which is just a unique object used as an identifier):
(define default-prompt 'default-prompt)

(define (spawn-thread (id String))
  (push-prompt default-prompt
    (run-thread id)))
Where it gets interesting is the SLEEP function which captures the continuation up to the prompt, and sets up a callback with JavaScript's setTimeout that will reinstall the continuation later:
(define (sleep (ms Number))
  (take-subcont default-prompt k
    (define (callback . #ignore)
      (push-prompt-subcont default-prompt k))
    ($setTimeout (js-callback callback) ms)))
So, first, SLEEP aborts up to and including the default prompt using TAKE-SUBCONT. It receives the continuation in the variable K. Once it has K, it defines a CALLBACK function, that will reinstall the default prompt with PUSH-PROMPT, and then continue with K again with PUSH-SUBCONT. All that's left is to give this callback to setTimeout.

Then I can spawn independent threads:
(spawn-thread "thread-1")
(spawn-thread "thread-2")
(spawn-thread "thread-3")
(spawn-thread "thread-4")
(spawn-thread "thread-5")
Wat is very new, but it's already practical for adding concurrency and metaprogramming to JavaScript. Deployment is very easy. Include the single wat.js file, put some Lisp code in a <script> tag, and run it.


Roly Perera said...

I do like this a lot. Are you tempted to add a parser for a more conventional syntax as well?

I'm also wondering about JS source maps. Are these feasible, or tricky given your use of continuations, etc?

Manuel Simoni said...

Hi Roly, thanks.

Of course I'm tempted, but so far I've been able to resist the urge of adding a typewriter syntax to Wat. With the JS syntax, Wat *is* JS on a fundamental level, which is extremely convenient e.g. for deployment.

For source maps to work, Wat would have to be compiled to JS. And yeah, I don't know how/if a standard JS debugger would deal with its fancy control flow. However, due to its "call-by-text"-based nature, Wat can provide quite useful stack traces on its own. I ripped this feature out of the current version for simplicity, but every captured stack frame can remember the original form it represents. This immediately gives great debugging info.