[dmd-concurrency] Sending mesages to non-listening threads

Michel Fortin michel.fortin at michelf.com
Wed Jan 13 15:30:16 PST 2010


Le 2010-01-13 à 16:30, Robert Jacques a écrit :

> I've been thinking about this. Obviously, a shared delegate is perfectly safe. (pure or immutable delegates are also safe) But, there are some obvious performance/use issues.

With an event handling system in a thread, you could make a special kind of asynchronous delegate that would always execute in its thread of origin. You can do this easily with Cocoa (performSelector:inThread:), but there are obviously no safeties for race conditions.

Here's what I'm thinking it could look in D: have a template struct -- lets call it AsyncCall -- with an opCall operator that simply send back a message to the originator thread with a delegate (valid in that thread) and the arguments (from the calling thread). Could be used like this:

	void main() {
		bool done = false;

		void setDone(bool value) {
			done = value;
		}

		// create worker, give it a callback to call when it has finished
		Tid tid = spawn(&worker, AsyncCall!(bool)(&setDone));

		// this is some sort of event-halding loop that accepts asynchronous calls.
		while (!done) {
			receive((AsyncCall!(bool) callback, bool arg) { callback(arg) });
		}
	}

	void worker(AsyncCall!(bool) setDone) {
		/* do something */
		setDone(true);
	}

The struct enforce thread-safety by making sure a call is always sent to the thread from which the delegate was taken. It simply contains a tid and a delegate.

	immutable struct AsyncCall!(A...) {
		private Tid tid;
		private void delegate(A) callee;

		this(void delegate(A) callee) {
			this.tid = thisTid;
			this.callee = callee;
		}
		void opCall(A args) {
			if (tid == thisTid)
				callee(args);
			else
				tid.send(callee, args);
		}
	}

Obviously, that's only for asynchronous calls, there's no way to return a value from a MessageCall. Also, it will only work for thread-to-thread communications.

Improving my previous example of simple GUI programming, with AsyncCall you could do this (assuming the GUI event loop automatically execute the async calls it receives):

	// Executed in GUI thread
	void onClick() {
		StatusWindow statusWindow = new StatusWindow("Working hard...");

		spawn(&doWork, asyncCall((float fractionDone) {
			statusWindow.completion = fractionDone;
		}), asyncCall({
			statusWindow.close();
		});
	}

	// Executed in worker thread
	void doWork(AsyncCall!(float) updateStatus, AsyncCall!() callWhenDone) {
		sleep(2); // working here.
		updateStatus(0.33);
		sleep(2); // working here.
		updateStatus(0.66);
		sleep(2); // working here.
		updateStatus(1.00);
		callWhenDone();
	}

-- 
Michel Fortin
michel.fortin at michelf.com
http://michelf.com/





More information about the dmd-concurrency mailing list