On Fri, 04 Apr 2014 09:26:26 GMT, Jack Applegame wrote:

Look at this code

import vibe.d;

__gshared TaskMutex mutex;

void foo(string name) {
	synchronized(mutex) sleep(2.seconds);
	logInfo(name);
}

shared static this() {
	mutex = new TaskMutex;
	runTask({
		while(true) foo("task 1");
	});
	runTask({
		while(true) foo("task 2");
	});
	runTask({
		while(true) foo("task 3");
	});
}

In this case task 2 and task 3 never gets control. Output is

task 1
task 1
task 1
...

Workaround is calling yield() after unlocking mutex:

void foo(string name) {
	synchronized(mutex) sleep(2.seconds);
	yield();
	logInfo(name);
}

Is this a bug or a feature?

The mutex here is a wall that forces other tasks to yield when trying to lock it (whatever the thread they're in). Once the lock is freed, the waiters are notified and resumed at the next run of the event loop.

However, this seems to happen within a single thread and your first task blocks this thread because it's in an infinite loop. Leaving a synchronization scope shouldn't force a task to yield because even though a context switch is very light, it shouldn't be abused. You may see a difference if you actually include some multi-threading into the equation using runWorkerTask.