Commit b72c73a8 authored by Romain Courteaud's avatar Romain Courteaud

Speed up mutex.

parent 4499150f
...@@ -185,51 +185,22 @@ ...@@ -185,51 +185,22 @@
if (!(this instanceof Mutex)) { if (!(this instanceof Mutex)) {
return new Mutex(); return new Mutex();
} }
this._latest_defer = null; this._latest_promise = null;
}; };
Mutex.prototype = { Mutex.prototype = {
constructor: Mutex, constructor: Mutex,
lockAndRun: function lockMutexAndRun(callback) {
lock: function lockMutex() { var previous_promise = this._latest_promise;
var previous_defer = this._latest_defer, if (previous_promise === null) {
current_defer = RSVP.defer(), this._latest_promise = RSVP.resolve(callback());
queue = new RSVP.Queue(); } else {
this._latest_promise = this._latest_promise
this._latest_defer = current_defer; .always(function () {
return callback();
if (previous_defer !== null) { });
queue.push(function acquireMutex() {
return previous_defer.promise;
});
} }
return this._latest_promise;
// Create a new promise (.then) not cancellable
// to allow external cancellation of the callback
// without breaking the mutex implementation
queue
.fail(current_defer.resolve.bind(current_defer));
return queue
.push(function generateMutexUnlock() {
return function runAndUnlock(callback) {
return ensurePushableQueue(callback)
.push(function releaseMutexAfterSuccess(result) {
current_defer.resolve(result);
return result;
}, function releaseMutexAfterError(error) {
current_defer.resolve(error);
throw error;
});
};
});
},
lockAndRun: function (callback) {
return this.lock()
.push(function executeLockAndRunCallback(runAndUnlock) {
return runAndUnlock(callback);
});
} }
}; };
...@@ -671,7 +642,9 @@ ...@@ -671,7 +642,9 @@
if (!context.hasOwnProperty(mutex_name)) { if (!context.hasOwnProperty(mutex_name)) {
context[mutex_name] = new Mutex(); context[mutex_name] = new Mutex();
} }
return context[mutex_name].lockAndRun(waitForMethodCallback); return ensurePushableQueue(context[mutex_name].lockAndRun,
[waitForMethodCallback],
context[mutex_name]);
} }
return ensurePushableQueue(callback, argument_list, context); return ensurePushableQueue(callback, argument_list, context);
}; };
......
...@@ -152,14 +152,17 @@ ...@@ -152,14 +152,17 @@
var mutex = new Mutex(), var mutex = new Mutex(),
counter = 0; counter = 0;
stop(); stop();
expect(4); expect(5);
function assertCounter(value) { function assertCounter(value) {
equal(counter, value); equal(counter, value);
counter += 1; counter += 1;
} }
function callback1() { function callback1() {
assertCounter(0); return new RSVP.Queue()
throw new Error('error in callback1'); .push(function () {
assertCounter(0);
throw new Error('error in callback1');
});
} }
function callback2() { function callback2() {
assertCounter(1); assertCounter(1);
...@@ -179,7 +182,7 @@ ...@@ -179,7 +182,7 @@
}) })
.push(undefined, function (error) { .push(undefined, function (error) {
equal(error.message, 'error in callback1'); equal(error.message, 'error in callback1');
assertCounter(2); assertCounter(3);
}) })
.always(function () { .always(function () {
start(); start();
...@@ -196,7 +199,10 @@ ...@@ -196,7 +199,10 @@
counter += 1; counter += 1;
} }
function callback1() { function callback1() {
ok(false, 'Should not reach that code'); return new RSVP.Queue()
.push(function () {
ok(false, 'Should not reach that code');
});
} }
function callback2() { function callback2() {
assertCounter(1); assertCounter(1);
...@@ -208,7 +214,7 @@ ...@@ -208,7 +214,7 @@
return RSVP.all([ return RSVP.all([
promise1 promise1
.then(function () { .then(function () {
ok(false, 'Should not reach that code'); ok(false, 'Should not reach that code 2');
}, function (error) { }, function (error) {
assertCounter(0); assertCounter(0);
equal(error.message, 'Default Message'); equal(error.message, 'Default Message');
...@@ -288,76 +294,4 @@ ...@@ -288,76 +294,4 @@
}); });
}); });
test('lockAndRun only wait for the callback', function () {
var mutex = new Mutex(),
counter = 0;
stop();
expect(4);
function assertCounter(value) {
equal(counter, value);
counter += 1;
}
function callback1() {
assertCounter(0);
return 'callback1 result';
}
function callback2() {
assertCounter(1);
return 'callback2 result';
}
return new RSVP.Queue()
.push(function () {
return RSVP.any([
mutex.lockAndRun(callback1)
.push(function () {
return RSVP.delay(10000);
}),
mutex.lockAndRun(callback2)
]);
})
.push(function (result) {
equal(result, 'callback2 result');
assertCounter(2);
})
.always(function () {
start();
});
});
test('lockAndRun only wait for the error callback', function () {
var mutex = new Mutex(),
counter = 0;
stop();
expect(4);
function assertCounter(value) {
equal(counter, value);
counter += 1;
}
function callback1() {
assertCounter(0);
throw new Error('callback1 error');
}
function callback2() {
assertCounter(1);
return 'callback2 result';
}
return new RSVP.Queue()
.push(function () {
return RSVP.any([
mutex.lockAndRun(callback1)
.push(undefined, function () {
return RSVP.delay(10000);
}),
mutex.lockAndRun(callback2)
]);
})
.push(function (result) {
equal(result, 'callback2 result');
assertCounter(2);
})
.always(function () {
start();
});
});
}(renderJS.Mutex, QUnit)); }(renderJS.Mutex, QUnit));
\ No newline at end of file
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment