mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-10 03:45:46 +00:00
Bug 1372670 - part 3 - add spinEventLoopUntil to nsIThreadManager; r=erahm,florian
This commit is contained in:
parent
2c70f522cc
commit
27c58cf89f
@ -28,8 +28,7 @@ function awaitPromise(promise) {
|
||||
value = val;
|
||||
});
|
||||
|
||||
while (resolved === null)
|
||||
Services.tm.mainThread.processNextEvent(true);
|
||||
Services.tm.spinEventLoopUntil(() => resolved !== null);
|
||||
|
||||
if (resolved === true)
|
||||
return value;
|
||||
|
@ -5175,9 +5175,9 @@ function server(port, basePath)
|
||||
srv.registerContentType("sjs", SJS_TYPE);
|
||||
srv.start(port);
|
||||
|
||||
gThreadManager.spinEventLoopUntil(() => srv.isStopped());
|
||||
|
||||
var thread = gThreadManager.currentThread;
|
||||
while (!srv.isStopped())
|
||||
thread.processNextEvent(true);
|
||||
|
||||
// get rid of any pending requests
|
||||
while (thread.hasPendingEvents())
|
||||
|
@ -5175,9 +5175,9 @@ function server(port, basePath)
|
||||
srv.registerContentType("sjs", SJS_TYPE);
|
||||
srv.start(port);
|
||||
|
||||
gThreadManager.spinEventLoopUntil(() => srv.isStopped());
|
||||
|
||||
var thread = gThreadManager.currentThread;
|
||||
while (!srv.isStopped())
|
||||
thread.processNextEvent(true);
|
||||
|
||||
// get rid of any pending requests
|
||||
while (thread.hasPendingEvents())
|
||||
|
@ -5176,9 +5176,9 @@ function server(port, basePath)
|
||||
srv.registerContentType("sjs", SJS_TYPE);
|
||||
srv.start(port);
|
||||
|
||||
gThreadManager.spinEventLoopUntil(() => srv.isStopped());
|
||||
|
||||
var thread = gThreadManager.currentThread;
|
||||
while (!srv.isStopped())
|
||||
thread.processNextEvent(true);
|
||||
|
||||
// get rid of any pending requests
|
||||
while (thread.hasPendingEvents())
|
||||
|
@ -5175,9 +5175,9 @@ function server(port, basePath)
|
||||
srv.registerContentType("sjs", SJS_TYPE);
|
||||
srv.start(port);
|
||||
|
||||
gThreadManager.spinEventLoopUntil(() => srv.isStopped());
|
||||
|
||||
var thread = gThreadManager.currentThread;
|
||||
while (!srv.isStopped())
|
||||
thread.processNextEvent(true);
|
||||
|
||||
// get rid of any pending requests
|
||||
while (thread.hasPendingEvents())
|
||||
|
@ -5176,9 +5176,9 @@ function server(port, basePath)
|
||||
srv.registerContentType("sjs", SJS_TYPE);
|
||||
srv.start(port);
|
||||
|
||||
gThreadManager.spinEventLoopUntil(() => srv.isStopped());
|
||||
|
||||
var thread = gThreadManager.currentThread;
|
||||
while (!srv.isStopped())
|
||||
thread.processNextEvent(true);
|
||||
|
||||
// get rid of any pending requests
|
||||
while (thread.hasPendingEvents())
|
||||
|
@ -915,10 +915,10 @@ class Buffer extends PP_Resource {
|
||||
class Flash_MessageLoop extends PP_Resource {
|
||||
run() {
|
||||
this._running = true;
|
||||
let thread = Cc["@mozilla.org/thread-manager;1"].getService().currentThread;
|
||||
while (this._running) {
|
||||
thread.processNextEvent(true);
|
||||
}
|
||||
let tm = Cc["@mozilla.org/thread-manager;1"].getService();
|
||||
tm.spinEventLoopUntil(() => {
|
||||
return !this._running;
|
||||
});
|
||||
}
|
||||
quit() {
|
||||
this._running = false;
|
||||
|
@ -139,10 +139,10 @@ DevToolsStartup.prototype = {
|
||||
|
||||
if (pauseOnStartup) {
|
||||
// Spin the event loop until the debugger connects.
|
||||
let thread = Cc["@mozilla.org/thread-manager;1"].getService().currentThread;
|
||||
while (!devtoolsThreadResumed) {
|
||||
thread.processNextEvent(true);
|
||||
}
|
||||
let tm = Cc["@mozilla.org/thread-manager;1"].getService();
|
||||
tm.spinEventLoopUntil(() => {
|
||||
return devtoolsThreadResumed;
|
||||
});
|
||||
}
|
||||
|
||||
if (cmdLine.state == Ci.nsICommandLine.STATE_REMOTE_AUTO) {
|
||||
|
@ -627,9 +627,9 @@ var gDevToolsBrowser = exports.gDevToolsBrowser = {
|
||||
let utils = window.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils);
|
||||
utils.enterModalState();
|
||||
while (!setupFinished) {
|
||||
tm.currentThread.processNextEvent(true);
|
||||
}
|
||||
tm.spinEventLoopUntil(() => {
|
||||
return setupFinished;
|
||||
});
|
||||
utils.leaveModalState();
|
||||
};
|
||||
|
||||
|
@ -163,9 +163,9 @@ function tunnelToInnerBrowser(outer, inner) {
|
||||
// The constructor of the new XBL binding is run asynchronously and there is no
|
||||
// event to signal its completion. Spin an event loop to watch for properties that
|
||||
// are set by the contructor.
|
||||
while (!outer._remoteWebNavigation) {
|
||||
Services.tm.currentThread.processNextEvent(true);
|
||||
}
|
||||
Services.tm.spinEventLoopUntil(() => {
|
||||
return outer._remoteWebNavigation;
|
||||
});
|
||||
|
||||
// Replace the `webNavigation` object with our own version which tries to use
|
||||
// mozbrowser APIs where possible. This replaces the webNavigation object that the
|
||||
|
@ -20,11 +20,11 @@ function setTestPluginEnabledState(newEnabledState, pluginName) {
|
||||
return;
|
||||
}
|
||||
var plugin = getTestPlugin(pluginName);
|
||||
while (plugin.enabledState != newEnabledState) {
|
||||
// Run a nested event loop to wait for the preference change to
|
||||
// propagate to the child. Yuck!
|
||||
SpecialPowers.Services.tm.currentThread.processNextEvent(true);
|
||||
}
|
||||
// Run a nested event loop to wait for the preference change to
|
||||
// propagate to the child. Yuck!
|
||||
SpecialPowers.Services.tm.spinEventLoopUntil(() => {
|
||||
return plugin.enabledState == newEnabledState;
|
||||
});
|
||||
SimpleTest.registerCleanupFunction(function() {
|
||||
SpecialPowers.setTestPluginEnabledState(oldEnabledState, pluginName);
|
||||
});
|
||||
|
@ -425,20 +425,19 @@ BrowserElementChild.prototype = {
|
||||
win.modalDepth++;
|
||||
let origModalDepth = win.modalDepth;
|
||||
|
||||
let thread = Services.tm.currentThread;
|
||||
debug("Nested event loop - begin");
|
||||
while (win.modalDepth == origModalDepth && !this._shuttingDown) {
|
||||
Services.tm.spinEventLoopUntil(() => {
|
||||
// Bail out of the loop if the inner window changed; that means the
|
||||
// window navigated. Bail out when we're shutting down because otherwise
|
||||
// we'll leak our window.
|
||||
if (this._tryGetInnerWindowID(win) !== innerWindowID) {
|
||||
debug("_waitForResult: Inner window ID changed " +
|
||||
"while in nested event loop.");
|
||||
break;
|
||||
return true;
|
||||
}
|
||||
|
||||
thread.processNextEvent(/* mayWait = */ true);
|
||||
}
|
||||
return win.modalDepth !== origModalDepth || this._shuttingDown;
|
||||
});
|
||||
debug("Nested event loop - finish");
|
||||
|
||||
if (win.modalDepth == 0) {
|
||||
|
@ -26,9 +26,9 @@ function* testSteps()
|
||||
let transaction2;
|
||||
|
||||
let comp = this.window ? SpecialPowers.wrap(Components) : Components;
|
||||
let thread = comp.classes["@mozilla.org/thread-manager;1"]
|
||||
.getService(comp.interfaces.nsIThreadManager)
|
||||
.currentThread;
|
||||
let tm = comp.classes["@mozilla.org/thread-manager;1"]
|
||||
.getService(comp.interfaces.nsIThreadManager);
|
||||
let thread = tm.currentThread;
|
||||
|
||||
let eventHasRun;
|
||||
|
||||
@ -38,9 +38,7 @@ function* testSteps()
|
||||
transaction2 = db.transaction("foo");
|
||||
}, Components.interfaces.nsIThread.DISPATCH_NORMAL);
|
||||
|
||||
while (!eventHasRun) {
|
||||
thread.processNextEvent(false);
|
||||
}
|
||||
tm.spinEventLoopUntil(() => eventHasRun);
|
||||
|
||||
ok(transaction2, "Non-null transaction2");
|
||||
|
||||
|
@ -28,11 +28,11 @@ function getTestPlugin(pluginName) {
|
||||
function setTestPluginEnabledState(newEnabledState, pluginName) {
|
||||
var oldEnabledState = SpecialPowers.setTestPluginEnabledState(newEnabledState, pluginName);
|
||||
var plugin = getTestPlugin(pluginName);
|
||||
while (plugin.enabledState != newEnabledState) {
|
||||
// Run a nested event loop to wait for the preference change to
|
||||
// propagate to the child. Yuck!
|
||||
SpecialPowers.Services.tm.currentThread.processNextEvent(true);
|
||||
}
|
||||
// Run a nested event loop to wait for the preference change to
|
||||
// propagate to the child. Yuck!
|
||||
SpecialPowers.Services.tm.spinEventLoopUntil(() => {
|
||||
return plugin.enabledState == newEnabledState;
|
||||
});
|
||||
SimpleTest.registerCleanupFunction(function() {
|
||||
SpecialPowers.setTestPluginEnabledState(oldEnabledState, pluginName);
|
||||
});
|
||||
|
@ -19,11 +19,9 @@ function setTestPluginEnabledState(newEnabledState, pluginName) {
|
||||
return;
|
||||
}
|
||||
var plugin = getTestPlugin(pluginName);
|
||||
while (plugin.enabledState != newEnabledState) {
|
||||
// Run a nested event loop to wait for the preference change to
|
||||
// propagate to the child. Yuck!
|
||||
SpecialPowers.Services.tm.currentThread.processNextEvent(true);
|
||||
}
|
||||
// Run a nested event loop to wait for the preference change to
|
||||
// propagate to the child. Yuck!
|
||||
SpecialPowers.Services.tm.spinEventLoopUntil(() => plugin.enabledState == newEnabledState);
|
||||
SimpleTest.registerCleanupFunction(function() {
|
||||
SpecialPowers.setTestPluginEnabledState(oldEnabledState, pluginName);
|
||||
});
|
||||
|
@ -179,9 +179,7 @@ FilePicker.prototype = {
|
||||
this._promptActive = true;
|
||||
this._sendMessage();
|
||||
|
||||
let thread = Services.tm.currentThread;
|
||||
while (this._promptActive)
|
||||
thread.processNextEvent(true);
|
||||
Services.tm.spinEventLoopUntil(() => !this._promptActive);
|
||||
delete this._promptActive;
|
||||
|
||||
if (this._domWin) {
|
||||
|
@ -71,9 +71,7 @@ NSSDialogs.prototype = {
|
||||
});
|
||||
|
||||
// Spin this thread while we wait for a result
|
||||
let thread = Services.tm.currentThread;
|
||||
while (response === null)
|
||||
thread.processNextEvent(true);
|
||||
Services.tm.spinEventLoopUntil(() => response != null);
|
||||
|
||||
return response;
|
||||
},
|
||||
|
@ -200,9 +200,7 @@ InternalPrompt.prototype = {
|
||||
});
|
||||
|
||||
// Spin this thread while we wait for a result
|
||||
let thread = Services.tm.currentThread;
|
||||
while (retval == null)
|
||||
thread.processNextEvent(true);
|
||||
Services.tm.spinEventLoopUntil(() => retval != null);
|
||||
|
||||
if (this._domWin) {
|
||||
let winUtils = this._domWin.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
|
||||
|
@ -56,10 +56,7 @@ TabSource.prototype = {
|
||||
});
|
||||
|
||||
// Spin this thread while we wait for a result.
|
||||
let thread = Services.tm.currentThread;
|
||||
while (result == null) {
|
||||
thread.processNextEvent(true);
|
||||
}
|
||||
Services.tm.spinEventLoopUntil(() => result != null);
|
||||
|
||||
if (result == -1) {
|
||||
return null;
|
||||
|
@ -460,10 +460,7 @@ PromptDelegate.prototype = {
|
||||
this.asyncShowPrompt(aMsg, res => result = res);
|
||||
|
||||
// Spin this thread while we wait for a result
|
||||
let thread = Services.tm.currentThread;
|
||||
while (result === undefined) {
|
||||
thread.processNextEvent(true);
|
||||
}
|
||||
Services.tm.spinEventLoopUntil(() => result !== undefined);
|
||||
} finally {
|
||||
this._changeModalState(/* aEntering */ false);
|
||||
}
|
||||
|
@ -838,9 +838,7 @@ JavaBridge.prototype = {
|
||||
// spin the event loop, but here we're in a test and our API
|
||||
// specifies a synchronous call, so we spin the loop to wait for
|
||||
// the call to finish.
|
||||
while (this._repliesNeeded > initialReplies) {
|
||||
thread.processNextEvent(true);
|
||||
}
|
||||
this._Services.tm.spinEventLoopUntil(() => this._repliesNeeded <= initialReplies);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -387,10 +387,7 @@ function waitForAResponse(outputList) {
|
||||
// From <https://developer.mozilla.org/en/XPConnect/xpcshell/HOWTO>
|
||||
var threadManager = Cc["@mozilla.org/thread-manager;1"]
|
||||
.getService(Ci.nsIThreadManager);
|
||||
var mainThread = threadManager.currentThread;
|
||||
while (outputList.length == 0) {
|
||||
mainThread.processNextEvent(true);
|
||||
}
|
||||
threadManager.spinEventLoopUntil(() => outputList.length != 0);
|
||||
}
|
||||
|
||||
function readCurrentList(filename) {
|
||||
|
@ -91,12 +91,10 @@ this.Async = {
|
||||
*/
|
||||
waitForSyncCallback: function waitForSyncCallback(callback) {
|
||||
// Grab the current thread so we can make it give up priority.
|
||||
let thread = Cc["@mozilla.org/thread-manager;1"].getService().currentThread;
|
||||
let tm = Cc["@mozilla.org/thread-manager;1"].getService();
|
||||
|
||||
// Keep waiting until our callback is triggered (unless the app is quitting).
|
||||
while (Async.checkAppReady() && callback.state == CB_READY) {
|
||||
thread.processNextEvent(true);
|
||||
}
|
||||
tm.spinEventLoopUntil(() => !Async.checkAppReady || callback.state != CB_READY);
|
||||
|
||||
// Reset the state of the callback to prepare for another call.
|
||||
let state = callback.state;
|
||||
|
@ -591,14 +591,15 @@ Assert.prototype = {
|
||||
var timeoutInterval = hwindow.setInterval(wait, interval);
|
||||
var thread = Services.tm.currentThread;
|
||||
|
||||
while (self.result !== true && !self.timeIsUp) {
|
||||
thread.processNextEvent(true);
|
||||
|
||||
Services.tm.spinEventLoopUntil(() => {
|
||||
let type = typeof(self.result);
|
||||
if (type !== 'boolean')
|
||||
if (type !== 'boolean') {
|
||||
throw TypeError("waitFor() callback has to return a boolean" +
|
||||
" instead of '" + type + "'");
|
||||
}
|
||||
}
|
||||
|
||||
return self.result === true || self.timeIsUp;
|
||||
});
|
||||
|
||||
hwindow.clearInterval(timeoutInterval);
|
||||
|
||||
|
@ -5341,9 +5341,9 @@ function server(port, basePath)
|
||||
srv.identity.setPrimary("http", "localhost", port);
|
||||
srv.start(port);
|
||||
|
||||
gThreadManager.spinEventLoopUntil(() => srv.isStopped());
|
||||
|
||||
var thread = gThreadManager.currentThread;
|
||||
while (!srv.isStopped())
|
||||
thread.processNextEvent(true);
|
||||
|
||||
// get rid of any pending requests
|
||||
while (thread.hasPendingEvents())
|
||||
|
@ -210,11 +210,7 @@ function sleep(milliseconds) {
|
||||
var timeup = false;
|
||||
|
||||
hwindow.setTimeout(function () { timeup = true; }, milliseconds);
|
||||
var thread = Services.tm.currentThread;
|
||||
|
||||
while (!timeup) {
|
||||
thread.processNextEvent(true);
|
||||
}
|
||||
Services.tm.spinEventLoopUntil(() => timeup);
|
||||
|
||||
broker.pass({'function':'utils.sleep()'});
|
||||
}
|
||||
|
@ -73,10 +73,8 @@ function asyncCleanup() {
|
||||
print("*** Storage Tests: Trying to asyncClose!");
|
||||
getOpenedDatabase().asyncClose(function() { closed = true; });
|
||||
|
||||
let curThread = Components.classes["@mozilla.org/thread-manager;1"]
|
||||
.getService().currentThread;
|
||||
while (!closed)
|
||||
curThread.processNextEvent(true);
|
||||
let tm = Cc["@mozilla.org/thread-manager;1"].getService();
|
||||
tm.spinEventLoopUntil(() => closed);
|
||||
|
||||
// we need to null out the database variable to get a new connection the next
|
||||
// time getOpenedDatabase is called
|
||||
|
@ -135,10 +135,8 @@ function execAsync(aStmt, aOptions, aResults) {
|
||||
if ("cancel" in aOptions && aOptions.cancel)
|
||||
pending.cancel();
|
||||
|
||||
let curThread = Components.classes["@mozilla.org/thread-manager;1"]
|
||||
.getService().currentThread;
|
||||
while (!completed && !_quit)
|
||||
curThread.processNextEvent(true);
|
||||
let tm = Cc["@mozilla.org/thread-manager;1"].getService(Ci.nsIThreadManager);
|
||||
tm.spinEventLoopUntil(() => completed || _quit);
|
||||
|
||||
return pending;
|
||||
}
|
||||
|
@ -215,11 +215,10 @@ function _do_main() {
|
||||
|
||||
_testLogger.info("running event loop");
|
||||
|
||||
var thr = Components.classes["@mozilla.org/thread-manager;1"]
|
||||
.getService().currentThread;
|
||||
var tm = Components.classes["@mozilla.org/thread-manager;1"].getService();
|
||||
var thr = tm.currentThread;
|
||||
|
||||
while (!_quit)
|
||||
thr.processNextEvent(true);
|
||||
tm.spinEventLoopUntil(() => _quit);
|
||||
|
||||
while (thr.hasPendingEvents())
|
||||
thr.processNextEvent(true);
|
||||
@ -479,12 +478,14 @@ function _initDebugging(port) {
|
||||
listener.open();
|
||||
|
||||
// spin an event loop until the debugger connects.
|
||||
let thr = Components.classes["@mozilla.org/thread-manager;1"]
|
||||
.getService().currentThread;
|
||||
while (!initialized) {
|
||||
let tm = Components.classes["@mozilla.org/thread-manager;1"].getService();
|
||||
tm.spinEventLoopUntil(() => {
|
||||
if (initialized) {
|
||||
return true;
|
||||
}
|
||||
do_print("Still waiting for debugger to connect...");
|
||||
thr.processNextEvent(true);
|
||||
}
|
||||
return false;
|
||||
});
|
||||
// NOTE: if you want to debug the harness itself, you can now add a 'debugger'
|
||||
// statement anywhere and it will stop - but we've already added a breakpoint
|
||||
// for the first line of the test scripts, so we just continue...
|
||||
@ -614,11 +615,8 @@ function _execute_test() {
|
||||
}
|
||||
_cleanupFunctions = [];
|
||||
}).catch(reportCleanupError).then(() => complete = true);
|
||||
let thr = Components.classes["@mozilla.org/thread-manager;1"]
|
||||
.getService().currentThread;
|
||||
while (!complete) {
|
||||
thr.processNextEvent(true);
|
||||
}
|
||||
let tm = Components.classes["@mozilla.org/thread-manager;1"].getService();
|
||||
tm.spinEventLoopUntil(() => complete);
|
||||
|
||||
// Restore idle service to avoid leaks.
|
||||
_fakeIdleService.deactivate();
|
||||
|
@ -3565,10 +3565,7 @@ PlacesEditBookmarkKeywordTransaction.prototype = {
|
||||
.then(() => done = true);
|
||||
// TODO: Until we can move to PlacesTransactions.jsm, we must spin the
|
||||
// events loop :(
|
||||
let thread = Services.tm.currentThread;
|
||||
while (!done) {
|
||||
thread.processNextEvent(true);
|
||||
}
|
||||
Services.tm.spinEventLoopUntil(() => done);
|
||||
},
|
||||
|
||||
undoTransaction: function EBKTXN_undoTransaction() {
|
||||
@ -3590,10 +3587,9 @@ PlacesEditBookmarkKeywordTransaction.prototype = {
|
||||
.then(() => done = true);
|
||||
// TODO: Until we can move to PlacesTransactions.jsm, we must spin the
|
||||
// events loop :(
|
||||
let thread = Services.tm.currentThread;
|
||||
while (!done) {
|
||||
thread.processNextEvent(true);
|
||||
}
|
||||
Services.tm.spinEventLoopUntil(() => {
|
||||
return done;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -8,7 +8,7 @@ Cu.import("resource://gre/modules/PlacesUtils.jsm");
|
||||
// This function "waits" for a promise to resolve by spinning a nested event
|
||||
// loop.
|
||||
function waitForPromise(promise) {
|
||||
let thread = Cc["@mozilla.org/thread-manager;1"].getService().currentThread;
|
||||
let tm = Cc["@mozilla.org/thread-manager;1"].getService();
|
||||
|
||||
let finalResult, finalException;
|
||||
|
||||
@ -19,9 +19,8 @@ function waitForPromise(promise) {
|
||||
});
|
||||
|
||||
// Keep waiting until our callback is triggered (unless the app is quitting).
|
||||
while (!finalResult && !finalException) {
|
||||
thread.processNextEvent(true);
|
||||
}
|
||||
tm.spinEventLoopUntil(() => finalResult || finalException);
|
||||
|
||||
if (finalException) {
|
||||
throw finalException;
|
||||
}
|
||||
|
@ -415,9 +415,7 @@ function openTabPrompt(domWin, tabPrompt, args) {
|
||||
// there's other stuff in nsWindowWatcher::OpenWindowInternal
|
||||
// that we might need to do here as well.
|
||||
|
||||
let thread = Services.tm.currentThread;
|
||||
while (args.promptActive)
|
||||
thread.processNextEvent(true);
|
||||
Services.tm.spinEventLoopUntil(() => !args.promptActive);
|
||||
delete args.promptActive;
|
||||
|
||||
if (args.promptAborted)
|
||||
@ -489,10 +487,7 @@ function openRemotePrompt(domWin, args, tabPrompt) {
|
||||
|
||||
messageManager.sendAsyncMessage("Prompt:Open", args, {});
|
||||
|
||||
let thread = Services.tm.currentThread;
|
||||
while (!closed) {
|
||||
thread.processNextEvent(true);
|
||||
}
|
||||
Services.tm.spinEventLoopUntil(() => closed);
|
||||
}
|
||||
|
||||
function ModalPrompter(domWin) {
|
||||
|
@ -605,10 +605,7 @@ function dbClose(aShutdown) {
|
||||
_dbConnection.asyncClose(() => closed = true);
|
||||
|
||||
if (!aShutdown) {
|
||||
let thread = Services.tm.currentThread;
|
||||
while (!closed) {
|
||||
thread.processNextEvent(true);
|
||||
}
|
||||
Services.tm.spinEventLoopUntil(() => closed);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -89,11 +89,11 @@ function setTestPluginEnabledState(newEnabledState, pluginName) {
|
||||
return;
|
||||
}
|
||||
var plugin = getTestPlugin(pluginName);
|
||||
while (plugin.enabledState != newEnabledState) {
|
||||
// Run a nested event loop to wait for the preference change to
|
||||
// propagate to the child. Yuck!
|
||||
SpecialPowers.Services.tm.currentThread.processNextEvent(true);
|
||||
}
|
||||
// Run a nested event loop to wait for the preference change to
|
||||
// propagate to the child. Yuck!
|
||||
SpecialPowers.Services.tm.spinEventLoopUntil(() => {
|
||||
return plugin.enabledState == newEnabledState;
|
||||
});
|
||||
SimpleTest.registerCleanupFunction(function() {
|
||||
SpecialPowers.setTestPluginEnabledState(oldEnabledState, pluginName);
|
||||
});
|
||||
|
@ -3,14 +3,13 @@
|
||||
// Let the event loop process a bit before crashing.
|
||||
if (shouldDelay) {
|
||||
let shouldCrashNow = false;
|
||||
let thr = Components.classes["@mozilla.org/thread-manager;1"]
|
||||
.getService().currentThread;
|
||||
let tm = Components.classes["@mozilla.org/thread-manager;1"]
|
||||
.getService();
|
||||
let thr = tm.currentThread;
|
||||
thr.dispatch({ run: () => { shouldCrashNow = true; } },
|
||||
Components.interfaces.nsIThread.DISPATCH_NORMAL);
|
||||
|
||||
while (!shouldCrashNow) {
|
||||
thr.processNextEvent(true);
|
||||
}
|
||||
tm.spinEventLoopUntil(() => shouldCrashNow);
|
||||
}
|
||||
|
||||
// now actually crash
|
||||
|
@ -24,9 +24,7 @@ function setup_crash() {
|
||||
terminator.observe(null, "profile-before-change", null);
|
||||
|
||||
dump("Waiting (actively) for the crash\n");
|
||||
while (true) {
|
||||
Services.tm.currentThread.processNextEvent(true);
|
||||
}
|
||||
Services.tm.spinEventLoopUntil(() => false);
|
||||
}
|
||||
|
||||
|
||||
|
@ -114,9 +114,7 @@ this.PromiseTestUtils = {
|
||||
|
||||
PromiseDebugging.addUncaughtRejectionObserver(observer);
|
||||
Promise.reject(this._ensureDOMPromiseRejectionsProcessedReason);
|
||||
while (!observed) {
|
||||
Services.tm.mainThread.processNextEvent(true);
|
||||
}
|
||||
Services.tm.spinEventLoopUntil(() => observed);
|
||||
PromiseDebugging.removeUncaughtRejectionObserver(observer);
|
||||
},
|
||||
_ensureDOMPromiseRejectionsProcessedReason: {},
|
||||
|
@ -922,10 +922,7 @@ tests.push(
|
||||
let shouldExitNestedEventLoop = false;
|
||||
|
||||
function event_loop() {
|
||||
let thr = Services.tm.mainThread;
|
||||
while (!shouldExitNestedEventLoop) {
|
||||
thr.processNextEvent(true);
|
||||
}
|
||||
Services.tm.spinEventLoopUntil(() => shouldExitNestedEventLoop);
|
||||
}
|
||||
|
||||
// I wish there was a way to cancel xpcshell do_timeout()s
|
||||
|
@ -401,8 +401,7 @@ var AddonTestUtils = {
|
||||
done = true;
|
||||
});
|
||||
|
||||
while (!done)
|
||||
Services.tm.mainThread.processNextEvent(true);
|
||||
Services.tm.spinEventLoopUntil(() => done);
|
||||
|
||||
if (error !== undefined)
|
||||
throw error;
|
||||
|
@ -1012,10 +1012,7 @@ function syncLoadManifestFromFile(aFile, aInstallLocation) {
|
||||
result = val
|
||||
});
|
||||
|
||||
let thread = Services.tm.currentThread;
|
||||
|
||||
while (success === undefined)
|
||||
thread.processNextEvent(true);
|
||||
Services.tm.spinEventLoopUntil(() => success !== undefined);
|
||||
|
||||
if (!success)
|
||||
throw result;
|
||||
|
@ -60,12 +60,10 @@ var WindowWatcher = {
|
||||
// The dialog is meant to be opened modally and the install operation can be
|
||||
// asynchronous, so we must spin an event loop (like the modal window does)
|
||||
// until the install is complete
|
||||
let thr = AM_Cc["@mozilla.org/thread-manager;1"].
|
||||
getService(AM_Ci.nsIThreadManager).
|
||||
mainThread;
|
||||
let tm = AM_Cc["@mozilla.org/thread-manager;1"].
|
||||
getService(AM_Ci.nsIThreadManager);
|
||||
|
||||
while (!installed || !updated)
|
||||
thr.processNextEvent(false);
|
||||
tm.spinEventLoopUntil(() => installed && updated);
|
||||
},
|
||||
|
||||
QueryInterface(iid) {
|
||||
|
164
xpcom/tests/gtest/TestThreadManager.cpp
Normal file
164
xpcom/tests/gtest/TestThreadManager.cpp
Normal file
@ -0,0 +1,164 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsIThreadManager.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsXPCOM.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "mozilla/Atomics.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
using mozilla::Atomic;
|
||||
using mozilla::Runnable;
|
||||
|
||||
class WaitCondition final : public nsINestedEventLoopCondition
|
||||
{
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
|
||||
WaitCondition(Atomic<uint32_t>& aCounter, uint32_t aMaxCount)
|
||||
: mCounter(aCounter)
|
||||
, mMaxCount(aMaxCount)
|
||||
{
|
||||
}
|
||||
|
||||
NS_IMETHODIMP IsDone(bool* aDone) override
|
||||
{
|
||||
*aDone = (mCounter == mMaxCount);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
~WaitCondition() = default;
|
||||
|
||||
Atomic<uint32_t>& mCounter;
|
||||
const uint32_t mMaxCount;
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS(WaitCondition, nsINestedEventLoopCondition)
|
||||
|
||||
class SpinRunnable final : public Runnable
|
||||
{
|
||||
public:
|
||||
explicit SpinRunnable(nsINestedEventLoopCondition* aCondition)
|
||||
: mCondition(aCondition)
|
||||
, mResult(NS_OK)
|
||||
{
|
||||
}
|
||||
|
||||
NS_IMETHODIMP Run()
|
||||
{
|
||||
nsCOMPtr<nsIThreadManager> threadMan =
|
||||
do_GetService("@mozilla.org/thread-manager;1");
|
||||
mResult = threadMan->SpinEventLoopUntil(mCondition);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult SpinLoopResult()
|
||||
{
|
||||
return mResult;
|
||||
}
|
||||
|
||||
private:
|
||||
~SpinRunnable() = default;
|
||||
|
||||
nsCOMPtr<nsINestedEventLoopCondition> mCondition;
|
||||
Atomic<nsresult> mResult;
|
||||
};
|
||||
|
||||
class CountRunnable final : public Runnable
|
||||
{
|
||||
public:
|
||||
explicit CountRunnable(Atomic<uint32_t>& aCounter)
|
||||
: mCounter(aCounter)
|
||||
{
|
||||
}
|
||||
|
||||
NS_IMETHODIMP Run()
|
||||
{
|
||||
mCounter++;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
Atomic<uint32_t>& mCounter;
|
||||
};
|
||||
|
||||
TEST(ThreadManager, SpinEventLoopUntilSuccess)
|
||||
{
|
||||
const uint32_t kRunnablesToDispatch = 100;
|
||||
nsresult rv;
|
||||
mozilla::Atomic<uint32_t> count(0);
|
||||
|
||||
nsCOMPtr<nsINestedEventLoopCondition> condition =
|
||||
new WaitCondition(count, kRunnablesToDispatch);
|
||||
RefPtr<SpinRunnable> spinner = new SpinRunnable(condition);
|
||||
nsCOMPtr<nsIThread> thread;
|
||||
rv = NS_NewNamedThread("SpinEventLoop", getter_AddRefs(thread), spinner);
|
||||
ASSERT_TRUE(NS_SUCCEEDED(rv));
|
||||
|
||||
nsCOMPtr<nsIRunnable> counter = new CountRunnable(count);
|
||||
for (uint32_t i = 0; i < kRunnablesToDispatch; ++i) {
|
||||
rv = thread->Dispatch(counter, NS_DISPATCH_NORMAL);
|
||||
ASSERT_TRUE(NS_SUCCEEDED(rv));
|
||||
}
|
||||
|
||||
rv = thread->Shutdown();
|
||||
ASSERT_TRUE(NS_SUCCEEDED(rv));
|
||||
ASSERT_TRUE(NS_SUCCEEDED(spinner->SpinLoopResult()));
|
||||
}
|
||||
|
||||
class ErrorCondition final : public nsINestedEventLoopCondition
|
||||
{
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
|
||||
ErrorCondition(Atomic<uint32_t>& aCounter, uint32_t aMaxCount)
|
||||
: mCounter(aCounter)
|
||||
, mMaxCount(aMaxCount)
|
||||
{
|
||||
}
|
||||
|
||||
NS_IMETHODIMP IsDone(bool* aDone) override
|
||||
{
|
||||
if (mCounter == mMaxCount) {
|
||||
return NS_ERROR_ILLEGAL_VALUE;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
~ErrorCondition() = default;
|
||||
|
||||
Atomic<uint32_t>& mCounter;
|
||||
const uint32_t mMaxCount;
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS(ErrorCondition, nsINestedEventLoopCondition)
|
||||
|
||||
TEST(ThreadManager, SpinEventLoopUntilError)
|
||||
{
|
||||
const uint32_t kRunnablesToDispatch = 100;
|
||||
nsresult rv;
|
||||
mozilla::Atomic<uint32_t> count(0);
|
||||
|
||||
nsCOMPtr<nsINestedEventLoopCondition> condition =
|
||||
new ErrorCondition(count, kRunnablesToDispatch);
|
||||
RefPtr<SpinRunnable> spinner = new SpinRunnable(condition);
|
||||
nsCOMPtr<nsIThread> thread;
|
||||
rv = NS_NewNamedThread("SpinEventLoop", getter_AddRefs(thread), spinner);
|
||||
ASSERT_TRUE(NS_SUCCEEDED(rv));
|
||||
|
||||
nsCOMPtr<nsIRunnable> counter = new CountRunnable(count);
|
||||
for (uint32_t i = 0; i < kRunnablesToDispatch; ++i) {
|
||||
rv = thread->Dispatch(counter, NS_DISPATCH_NORMAL);
|
||||
ASSERT_TRUE(NS_SUCCEEDED(rv));
|
||||
}
|
||||
|
||||
rv = thread->Shutdown();
|
||||
ASSERT_TRUE(NS_SUCCEEDED(rv));
|
||||
ASSERT_TRUE(NS_FAILED(spinner->SpinLoopResult()));
|
||||
}
|
@ -44,6 +44,7 @@ UNIFIED_SOURCES += [
|
||||
'TestTArray2.cpp',
|
||||
'TestTaskQueue.cpp',
|
||||
'TestTextFormatter.cpp',
|
||||
'TestThreadManager.cpp',
|
||||
'TestThreadPool.cpp',
|
||||
'TestThreadPoolListener.cpp',
|
||||
'TestThreads.cpp',
|
||||
|
@ -11,6 +11,15 @@
|
||||
interface nsIRunnable;
|
||||
interface nsIThread;
|
||||
|
||||
[scriptable, function, uuid(039a227d-0cb7-44a5-a8f9-dbb7071979f2)]
|
||||
interface nsINestedEventLoopCondition : nsISupports
|
||||
{
|
||||
/**
|
||||
* Returns true if the current nested event loop should stop spinning.
|
||||
*/
|
||||
bool isDone();
|
||||
};
|
||||
|
||||
/**
|
||||
* An interface for creating and locating nsIThread instances.
|
||||
*/
|
||||
@ -85,6 +94,17 @@ interface nsIThreadManager : nsISupports
|
||||
*/
|
||||
void dispatchToMainThread(in nsIRunnable event);
|
||||
|
||||
/**
|
||||
* Enter a nested event loop on the current thread, waiting on, and
|
||||
* processing events until condition.isDone() returns true.
|
||||
*
|
||||
* If condition.isDone() throws, this function will throw as well.
|
||||
*
|
||||
* C++ code should not use this function, instead preferring
|
||||
* mozilla::SpinEventLoopUntil.
|
||||
*/
|
||||
void spinEventLoopUntil(in nsINestedEventLoopCondition condition);
|
||||
|
||||
/**
|
||||
* This queues a runnable to the main thread's idle queue.
|
||||
*
|
||||
|
@ -338,6 +338,32 @@ nsThreadManager::GetCurrentThread(nsIThread** aResult)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsThreadManager::SpinEventLoopUntil(nsINestedEventLoopCondition* aCondition)
|
||||
{
|
||||
nsCOMPtr<nsINestedEventLoopCondition> condition(aCondition);
|
||||
nsresult rv = NS_OK;
|
||||
|
||||
if (!mozilla::SpinEventLoopUntil([&]() -> bool {
|
||||
bool isDone = false;
|
||||
rv = condition->IsDone(&isDone);
|
||||
// JS failure should be unusual, but we need to stop and propagate
|
||||
// the error back to the caller.
|
||||
if (NS_FAILED(rv)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return isDone;
|
||||
})) {
|
||||
// We stopped early for some reason, which is unexpected.
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
// If we exited when the condition told us to, we need to return whether
|
||||
// the condition encountered failure when executing.
|
||||
return rv;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
nsThreadManager::GetHighestNumberOfThreads()
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user