Commit 50e16382 authored by Romain Courteaud's avatar Romain Courteaud

Fix cancellation handling

Ensure to cancel the ongoing promise if queue is cancelled.
parent 9ed53129
...@@ -45,18 +45,12 @@ ...@@ -45,18 +45,12 @@
try { try {
result = callback.apply(context, argument_list); result = callback.apply(context, argument_list);
} catch (e) { } catch (e) {
return new RSVP.Queue() return new RSVP.Queue(RSVP.reject(e));
.push(function returnPushableError() {
return RSVP.reject(e);
});
} }
if (result instanceof RSVP.Queue) { if (result instanceof RSVP.Queue) {
return result; return result;
} }
return new RSVP.Queue() return new RSVP.Queue(result);
.push(function returnPushableResult() {
return result;
});
} }
function readBlobAsDataURL(blob) { function readBlobAsDataURL(blob) {
...@@ -111,20 +105,10 @@ ...@@ -111,20 +105,10 @@
try { try {
result = callback(evt); result = callback(evt);
} catch (e) { } catch (e) {
result = RSVP.reject(e); return reject(e);
} }
callback_promise = result; callback_promise = new RSVP.Queue(result).push(undefined, reject);
new RSVP.Queue()
.push(function waitForEventCallbackResult() {
return result;
})
.push(undefined, function handleEventCallbackError(error) {
if (!(error instanceof RSVP.CancellationError)) {
canceller();
reject(error);
}
});
}; };
target.addEventListener(type, handle_event_callback, useCapture); target.addEventListener(type, handle_event_callback, useCapture);
...@@ -423,10 +407,7 @@ ...@@ -423,10 +407,7 @@
if (resolved) { if (resolved) {
throw new ResolvedMonitorError(); throw new ResolvedMonitorError();
} }
var queue = new RSVP.Queue() var queue = new RSVP.Queue(promise_to_monitor)
.push(function waitForPromiseToMonitor() {
return promise_to_monitor;
})
.push(function handlePromiseToMonitorSuccess(fulfillmentValue) { .push(function handlePromiseToMonitorSuccess(fulfillmentValue) {
// Promise to monitor is fullfilled, remove it from the list // Promise to monitor is fullfilled, remove it from the list
var len = promise_list.length, var len = promise_list.length,
...@@ -442,13 +423,6 @@ ...@@ -442,13 +423,6 @@
} }
promise_list = new_promise_list; promise_list = new_promise_list;
}, function handlePromiseToMonitorError(rejectedReason) { }, function handlePromiseToMonitorError(rejectedReason) {
if (rejectedReason instanceof RSVP.CancellationError) {
if (!(promise_to_monitor.isFulfilled &&
promise_to_monitor.isRejected)) {
// The queue could be cancelled before the first push is run
promise_to_monitor.cancel();
}
}
reject(rejectedReason); reject(rejectedReason);
throw rejectedReason; throw rejectedReason;
}); });
...@@ -603,16 +577,16 @@ ...@@ -603,16 +577,16 @@
}; };
function runJob(gadget, name, callback, argument_list) { function runJob(gadget, name, callback, argument_list) {
var job_promise = ensurePushableQueue(callback, argument_list, gadget);
if (gadget.__job_dict.hasOwnProperty(name)) { if (gadget.__job_dict.hasOwnProperty(name)) {
gadget.__job_dict[name].cancel(); gadget.__job_dict[name].cancel();
} }
var job_promise = ensurePushableQueue(callback, argument_list, gadget);
gadget.__job_dict[name] = job_promise; gadget.__job_dict[name] = job_promise;
gadget.__monitor.monitor(new RSVP.Queue() // gadget.__monitor.monitor(job_promise
.push(function waitForJobPromise() { gadget.__monitor.monitor(new RSVP.Queue(job_promise)
return job_promise;
})
.push(undefined, function handleJobError(error) { .push(undefined, function handleJobError(error) {
// Do not crash monitor if the job has been cancelled
// by a new execution
if (!(error instanceof RSVP.CancellationError)) { if (!(error instanceof RSVP.CancellationError)) {
throw error; throw error;
} }
...@@ -1200,10 +1174,7 @@ ...@@ -1200,10 +1174,7 @@
result = method(url, options, parent_gadget, old_element); result = method(url, options, parent_gadget, old_element);
// Set the HTML context // Set the HTML context
if (typeof result.then === 'function') { if (typeof result.then === 'function') {
return new RSVP.Queue() return new RSVP.Queue(result)
.push(function () {
return result;
})
.push(function setAsyncGadgetInstanceHTMLContext(gadget_instance) { .push(function setAsyncGadgetInstanceHTMLContext(gadget_instance) {
return setGadgetInstanceHTMLContext(gadget_instance, options, return setGadgetInstanceHTMLContext(gadget_instance, options,
parent_gadget, url, parent_gadget, url,
...@@ -1923,11 +1894,8 @@ ...@@ -1923,11 +1894,8 @@
embedded_channel, embedded_channel,
declare_method_list_waiting; declare_method_list_waiting;
return new RSVP.Queue() // Wait for the loading gadget to be created
.push(function waitForLoadingGadget() { return new RSVP.Queue(wait_for_gadget_loaded)
// Wait for the loading gadget to be created
return wait_for_gadget_loaded;
})
.push(function handleLoadingGadget(result_list) { .push(function handleLoadingGadget(result_list) {
TmpConstructor = result_list[0]; TmpConstructor = result_list[0];
root_gadget = result_list[1]; root_gadget = result_list[1];
......
...@@ -171,7 +171,7 @@ ...@@ -171,7 +171,7 @@
var mutex = new Mutex(), var mutex = new Mutex(),
counter = 0; counter = 0;
stop(); stop();
expect(5); expect(4);
function assertCounter(value) { function assertCounter(value) {
equal(counter, value); equal(counter, value);
counter += 1; counter += 1;
...@@ -201,7 +201,9 @@ ...@@ -201,7 +201,9 @@
}) })
.push(undefined, function (error) { .push(undefined, function (error) {
equal(error.message, 'error in callback1'); equal(error.message, 'error in callback1');
assertCounter(3); // Callback 2 is called before RSVP.all is rejected
// Callback 3 is cancelled by RSVP.all
assertCounter(2);
}) })
.always(function () { .always(function () {
start(); start();
...@@ -253,6 +255,86 @@ ...@@ -253,6 +255,86 @@
}); });
}); });
test('lockAndRun cancel stop first execution', function () {
var mutex = new Mutex(),
counter = 0;
stop();
expect(2);
function assertCounter(value) {
equal(counter, value);
counter += 1;
}
function callback1() {
assertCounter(0);
return new RSVP.Queue()
.push(function () {
return RSVP.delay(50);
})
.push(function () {
assertCounter(-999);
ok(false, 'Should not reach that code');
});
}
return new RSVP.Queue()
.push(function () {
var promise = mutex.lockAndRun(callback1);
promise.cancel('cancel callback1');
return RSVP.delay(200);
})
.push(function () {
assertCounter(1);
})
.always(function () {
start();
});
});
test('lockAndRun cancel stop second execution', function () {
var mutex = new Mutex(),
counter = 0;
stop();
expect(3);
function assertCounter(value) {
equal(counter, value);
counter += 1;
}
function callback1() {
assertCounter(0);
return new RSVP.Queue()
.push(function () {
return RSVP.delay(50);
})
.push(function () {
assertCounter(1);
});
}
function callback2() {
assertCounter(2);
return new RSVP.Queue()
.push(function () {
return RSVP.delay(50);
})
.push(function () {
assertCounter(-999);
ok(false, 'Should not reach that code');
});
}
return new RSVP.Queue()
.push(function () {
return mutex.lockAndRun(callback1);
})
.push(function () {
var promise = mutex.lockAndRun(callback2);
promise.cancel('cancel callback2');
return RSVP.delay(200);
})
.push(function () {
assertCounter(2);
})
.always(function () {
start();
});
});
test('lockAndRun cancel does not cancel previous execution', function () { test('lockAndRun cancel does not cancel previous execution', function () {
var mutex = new Mutex(), var mutex = new Mutex(),
......
...@@ -2229,6 +2229,151 @@ ...@@ -2229,6 +2229,151 @@
}); });
}); });
test('mutex prevent concurrent execution', function () {
// Subclass RenderJSGadget to not pollute its namespace
var Klass = function () {
RenderJSGadget.call(this);
}, gadget,
counter = 0;
Klass.prototype = new RenderJSGadget();
Klass.prototype.constructor = Klass;
Klass.declareMethod = RenderJSGadget.declareMethod;
gadget = new Klass();
function assertCounter(value) {
equal(counter, value);
counter += 1;
}
Klass.declareMethod('testFoo', function (expected_counter) {
assertCounter(expected_counter);
return new RSVP.Queue()
.push(function () {
return RSVP.delay(50);
})
.push(function () {
assertCounter(expected_counter + 1);
return counter;
});
}, {mutex: 'foo'});
// method can be called
stop();
expect(10);
return new RSVP.Queue()
.push(function () {
return RSVP.all([
gadget.testFoo(0),
gadget.testFoo(2),
gadget.testFoo(4)
]);
})
.push(function (result_list) {
equal(result_list[0], 2);
equal(result_list[1], 4);
equal(result_list[2], 6);
assertCounter(6);
})
.always(function () {
start();
});
});
test('mutex first cancellation stop execution', function () {
// Subclass RenderJSGadget to not pollute its namespace
var Klass = function () {
RenderJSGadget.call(this);
}, gadget,
counter = 0;
Klass.prototype = new RenderJSGadget();
Klass.prototype.constructor = Klass;
Klass.declareMethod = RenderJSGadget.declareMethod;
gadget = new Klass();
function assertCounter(value) {
equal(counter, value);
counter += 1;
}
Klass.declareMethod('testFoo', function (expected_counter) {
assertCounter(expected_counter);
return new RSVP.Queue()
.push(function () {
return RSVP.delay(50);
})
.push(function () {
assertCounter(expected_counter + 1);
ok(false, 'Should not reach that code');
});
}, {mutex: 'foo'});
// method can be called
stop();
expect(2);
return new RSVP.Queue()
.push(function () {
// Immediately cancel the first call
gadget.testFoo(0).cancel();
return RSVP.delay(200);
})
.push(function () {
assertCounter(1);
})
.always(function () {
start();
});
});
test('not mutex first cancellation stop execution', function () {
// Subclass RenderJSGadget to not pollute its namespace
var Klass = function () {
RenderJSGadget.call(this);
}, gadget,
counter = 0;
Klass.prototype = new RenderJSGadget();
Klass.prototype.constructor = Klass;
Klass.declareMethod = RenderJSGadget.declareMethod;
gadget = new Klass();
function assertCounter(value) {
equal(counter, value);
counter += 1;
}
Klass.declareMethod('testFoo', function (expected_counter) {
assertCounter(expected_counter);
return new RSVP.Queue()
.push(function () {
return RSVP.delay(50);
})
.push(function () {
assertCounter(expected_counter + 1);
ok(false, 'Should not reach that code');
});
});
// method can be called
stop();
expect(2);
return new RSVP.Queue()
.push(function () {
// Immediately cancel the first call
gadget.testFoo(0).cancel();
return RSVP.delay(200);
})
.push(function () {
assertCounter(1);
})
.always(function () {
start();
});
});
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// RenderJSGadgetKlass.ready // RenderJSGadgetKlass.ready
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
...@@ -2431,21 +2576,13 @@ ...@@ -2431,21 +2576,13 @@
service_status.status = undefined; service_status.status = undefined;
klass.declareService(function () { klass.declareService(function () {
service_status.start_count += 1; return RSVP.Promise(function () {
return new RSVP.Queue() service_status.start_count += 1;
.push(function () { service_status.status = "started";
service_status.status = "started"; }, function () {
return RSVP.defer().promise; service_status.stop_count += 1;
}) service_status.status = "stopped";
.push(undefined, function (error) { });
service_status.stop_count += 1;
if (error instanceof RSVP.CancellationError) {
service_status.status = "stopped";
} else {
service_status.status = "error";
}
throw error;
});
}); });
} }
...@@ -3011,22 +3148,14 @@ ...@@ -3011,22 +3148,14 @@
service_status.stop_count = 0; service_status.stop_count = 0;
service_status.status = undefined; service_status.status = undefined;
klass.onEvent('bar', function (evt) { klass.onEvent('bar', function () {
service_status.start_count += 1; return new RSVP.Promise(function () {
return new RSVP.Queue() service_status.start_count += 1;
.push(function () { service_status.status = "started";
service_status.status = "started"; }, function () {
return RSVP.defer().promise; service_status.stop_count += 1;
}) service_status.status = "stopped";
.push(undefined, function (error) { });
service_status.stop_count += 1;
if (error instanceof RSVP.CancellationError) {
service_status.status = "stopped";
} else {
service_status.status = "error";
}
throw error;
});
}); });
} }
...@@ -3283,22 +3412,14 @@ ...@@ -3283,22 +3412,14 @@
service_status.status = undefined; service_status.status = undefined;
klass.declareJob(name, function (parameter) { klass.declareJob(name, function (parameter) {
service_status.start_count += 1; return new RSVP.Promise(function () {
service_status.parameter = parameter; service_status.start_count += 1;
return new RSVP.Queue() service_status.parameter = parameter;
.push(function () { service_status.status = "started";
service_status.status = "started"; }, function () {
return RSVP.defer().promise; service_status.stop_count += 1;
}) service_status.status = "stopped";
.push(undefined, function (error) { });
service_status.stop_count += 1;
if (error instanceof RSVP.CancellationError) {
service_status.status = "stopped";
} else {
service_status.status = "error";
}
throw error;
});
}); });
} }
......
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