Threads in JavaScript?

Threads in JavaScript? "Over your dead body," says Brendan.

But Neil Mix begs to differ -- they're already there!

Neil's latest blog post presents a cool hack combining JavaScript 1.7's generators with trampolined style to implement very lightweight cooperative threads.

The implementation weighs in at a breathtakingly small 4k.

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.

Similar story with Lua

In The Evolution of Lua paper, the Lua authors expressed a similar opinion on threads:

...we still think that no one can write correct programs in a language where 'a=a+1' is not deterministic.
Lua uses coroutines to good effect.

Non sequitur

+ scarecrow.

Prima facie

+ tinman

[Edit: Ok, I'll try to explain the connection ....]

(1) Roberto and Brendan both have a passionate dislike of threads as used in a shared-state, non-atomic sense.

(2) The Lua team chose coroutines as a solution - similar to what Dave is presenting in the generators example.

So, ipso facto... anyone interested in which path the JavaScript team should take, would do well to read the Evolution of Lua paper.

[Edit - Edit Note: That's not to say that coroutines are necessarily the right answer for the Evolution of JavaScript.]

[Edit III: s/continuations/generators/]

Ad hominem

+lyin' ;)

Taps shoes together....

+ There's no place like LtU.
+ There's no place like LtU.

Sic sequitur

In fact, Lua-style coroutines were proposed and rejected for JS, as Neil Mix's blog entry states:

All in all, I wish JavaScript had a more directly supported concurrency mechanism. I asked the ES4 (aka JavaScript 2) design team to consider something like Lua-style coroutines, which led to a spirited discussion, but in the end fears of complexity and the lateness in the design process left the feature on the “wishful thinking” list.

to which Brendan Eich responded (in part):

...we are forced to a lower common denominator than Lua-style coroutines. Python-style generators are about as low as one can go, and still be useful.

I hope I'm not quoting out of context.

I understand Brendan's argument, but the pragmatic nature of the argument is interesting: Lua coroutines (as implemented by default) cannot yield through a C call-frame, although mechanisms are available to overcome that, such as Mike Pall's Coco library. A Javascript implementation would have the same limitation, but some implementations might choose to use a mechanism like coco to lift the limitation; that would create an interoperability problem, since some programmers would undoubtedly use the more general coroutines on implementations where they were available.

Neil Mix's library is interesting in that it demonstrates that generators can be pushed to come closer to a coroutine implementation, but I also think that the Javascript choice was unfortunate, and that Lua does demonstrate that coroutines are both a useful concurrency mechanism, and one which is relatively easy to use (unlike, say, continuations which are clearly more powerful but even harder to implement.)

Having said that, I don't think Lua quite gets it right either; Lua coroutines are unlabeled, and therefore not fully composable; independent uses can interfere with each other. Although it's possible to implement labeled coroutines without much difficulty, as demonstrated by Ana Lúcia de Moura et al. in Coroutines in Lua (and at greater length in her doctoral thesis Revisitando Co-rotinas), the technique must be used by all libraries which use coroutines, and consequently cannot be relied upon unless it is built into the language's core coroutine library.

Ad Infinitum

Both Lua and JavaScript are languages that are commonly embedded in other environments, and heavily interact with other PLs. IIUC, the argument against coroutines in JavaScript boils down to the practical considerations of interaction with the host, based on where the bulk of their users currently live (JavaScript commonly being a scripting engine for browser based applications).

From a non-provincial perspective, I think the most interesting thing about the history of Lua, and the ongoing evolution of JavaScript, is the interaction with the lambda calculus based languages like Scheme and ML. JavaScript seems to be mining Scheme for ideas for improvement, and using ML for the reference implementation. The same could be said of Lua, though the interaction with ML is more in terms of an implementation of ML in Lua.

Lua-ML

The same could be said of Lua, though the interaction with ML is more in terms of an implementation of ML in Lua.

It's the other way round: Lua-ML is an implementation of Lua written in OCaml, which emerged as a by-product of the C-- compiler (also written in OCaml, and configurable via Lua). It's not intended to be a reference implementation, though.

So...

Threads suck, but Erlang is not threads, ahum.

Well, here's a different proposal: just use threads, don't whine about them, and use messaging instead of writing through the whole computer memory. You likely won't have any problems with threads then.

Plus, then we wouldn't need blog posts like these (I feel like I'm reading reddit). ;-)

That lightweight yield idea is really cool, though.

Context

Well, here's a different proposal: just use threads, don't whine about them, and use messaging instead of writing through the whole computer memory.

That's probably great advice for a programmer using a language that already has threads as its main concurrency mechanism. However, Brendan's blog post is written in the context of language design, specifically about whether to add threads to Javascript. That's not a decision to be made lightly. Brendan has the right perspective on this, IMO. Read down to the third-last paragraph:

"A requirement for JS3 [...] is to do something along these more implicit lines of concurrency support. In all the fast yet maintainable MT systems I've built or worked on, the key idea (which Will Clinger stated clearly to me over lunch last fall) is to separate the mutable unshared data from the immutable shared data. Do that well, with language and VM support, and threads become what they should be: not an abstraction violator from hell, but a scaling device that can be composed with existing abstractions."

That's a worthy goal, and I'm glad to see it being taken seriously.

Plus, then we wouldn't need blog posts like these (I feel like I'm reading reddit). ;-)

Remember the context, and it'll make more sense.

Context

Thanks for pointing that out.

While probably every language grows until it wants to include threads (i.e. JS *will* probably include threads at some point), I agree that the decision should be made by reflecting what choices and alternatives there are.

I don't get it

Threads suck, but Erlang is not threads, ahum.

Well, here's a different proposal: just use threads, don't whine about them, and use messaging instead of writing through the whole computer memory.

Erlang doesn't give programmer-access to OS threads indeed (technically, Erlang gives access to lightweight processes, definitely not threads), so I don't quite get why you suggest to use threads (therefore shared mutable memory) + message passing when Erlang uses processes (shared-nothing) + message passing...

hygienic macros

On a related note, I was quite surprised to find these points on a page titled JS3 Requirements:

  • AST reflection
  • Quotations
  • Hygienic macros

JS3/ES5

Yes, this is a hope (especially of mine!) for the future of JS, but just to be clear it's not happening in this round. We're working on ES4 aka JS2 right now, and the above is a wish-list for ES5/JS3.

Defining Threads

I mean by "threads" (as in "Threads suck" ;-) exactly what the blog post and the Unix SMP kernel work it mentioned imply: shared address space, preemptively-scheduled execution contexts (stack + registers) that can map onto parallel CPUs, and where programmers are not well-protected from shared memory hazards.

Sorry if this is not the conventional meaning in others' worlds. I think it ought to be; there's no point in redefining the term as it is commonly used in Unix, Java, Windows, etc. at this late date (Neil's fun Thread.js package and synonyms such as Ada "tasks" notwithstanding).

So Erlang does not have threads (and of course I was commending it to others' attention, not condemning it). And (pacé Ulrich) JS will *not* "include threads at some point." Not as defined here.

yeah, big difference

Sorry, I didn't mean to suggest Thread.js refutes your post. (Mea culpa for excessive cuteness.) You won't hear any disagreement from me that shared-memory threads are hazardous and have serious consequences for language design. Neil's post is complementary, demonstrating one way to implement some level of threading entirely in the language using simple control operators.

A nice thing about a library like Neil's is that it's not possible for it to violate any properties of the language (e.g. Chris's example of deterministic "a=a+1"), since it's written entirely within the language.

Erlang-in-JavaScript

You all might want to check out Er.js. This builds off Neil's Thread.js to make an Erlang-style concurrent message-passing system in JavaScript.

Communicating event-loops in JS

Similarly, there's a Javascript ref_send implementation for communicating event loops. It can be used in browsers now.