Concurrency support?
Neil Mix
nmix at pandora.com
Tue Jun 27 05:33:46 PDT 2006
On Jun 27, 2006, at 5:10 AM, Brendan Eich wrote:
> If the design should not be compromised by implementation issues,
> then we should agree on that. ECMA TG1 was not of one mind on
> "damn the implementation costs, full speed ahead". We have JITted
> runtimes already tracking ES4 (Adobe), based on Waldemar's old
> drafts (JScript.NET). We have small-ish to tiny interpreters
> (Apple, Opera). We do not have the Lua single-implementation code-
> is-spec open source codebase.
I'm very sympathetic to this concern. Fortunately, I had a bit of an
epiphany this morning and realized that it may be possible to
implement a "poor man's CPS" using generators. It's fairly ugly and
cumbersome, and I'm not yet sure it makes things any less mind-
bending, but I thought I'd throw this out in the hopes that it might
inspire modifications to the generator proposal that strike a solid
middle ground.
It works like this: if we assume that
a) arguments can be passed into a generator function and referenced
as normal in the generator body (I assume that's true, but the spec
doesn't explicitly say so), and
b) a generator can obtain a reference to itself (is that what
arguments.callee would do at present?)
then you can chain generators together using something that looks
similar to CPS. In essence, we're now pushing the burden of stack
management onto the coder, so that the runtime only ever yields
across one stack frame at any given time. Here's a silly example:
function doHttpReq(url, callingGen) {
// XHR is a hypothetical class that abstracts
// XmlHttp functionality
var xhr = new XHR();
// grab a reference to this generator instance?
var ourGen = arguments.callee;
// set a callback handler to "resume" us
xhr.onLoad = function() { ourGen.next() }
xhr.send(url);
// wait for the xhr response
yield;
// return the fetched data to the generator that called us
callingGen.send(xhr.data);
// do a final yield to prevent a stopIteration
// exception from being thrown into the xhr callback
yield;
}
// load a bunch of URLs sequentially
function loadItems(urls, callingGen) {
var items = [];
for (var i = 0; i < urls.length; i++) {
// request an individual url,
// passing in a reference to this generator instance.
// Note the call to next()
doHttpReq(urls[i], arguments.callee).next();
// receive the send from doHttpReq
var data = yield;
items.push(data);
}
callingGen.send(items);
// do a final to prevent an exception from being thrown
// into doHttpReq
yield;
}
function init() {
loadItems([url1, url2], arguments.callee).next();
var items = yield;
// do stuff with the items array.
}
addEventHandler("load", function() { init().next(); });
Now, I'll plainly state that this is ugly I'd much prefer more
natural coroutines than this messy CPS-like goo, but I'll take what I
can get. ;) Here's a few things that would clean this up quite a bit:
1) a more intuitive reference to the generator instance
(arguments.callee is just wrong)
2) direct access to an implicit callback for the generator's send
method so that we don't have to create it manually in each frame
3) elimination of the final 'yield' which exists solely to prevent
exception propagation
One way to solve these problems would be to invent an implicit
'generator' object, which then becomes the keyword that identifies a
generator function. Turn yield into a method on the generator
object. Finally, create a property generator.callback, a method that
calls send() and discards any stopIteration exceptions.
Thus the above example would become:
function doHttpReq(url, callingGen) {
var xhr = new XHR();
xhr.onLoad = generator.callback;
xhr.send(url);
generator.yield();
callingGen.send(xhr.data);
}
function loadItems(urls, callingGen) {
var items = [];
for (var i = 0; i < urls.length; i++) {
doHttpReq(urls[i], generator).next();
var data = generator.yield();
items.push(data);
}
callingGen.send(items);
}
function init() {
loadItems([url1, url2], generator).next();
var items = generator.yield();
// do stuff with the items array.
}
addEventHandler("load", function() { init().next(); });
More information about the Es4-discuss
mailing list