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