Bug 1529163 - Wait for all threads in debugger client, r=jlast.

--HG--
extra : rebase_source : 6ba3159c47a69befbcf98950240ed072b25a8c5d
This commit is contained in:
Brian Hackett 2019-07-11 17:47:22 +00:00
Родитель 8fbd4d6692
Коммит 53443837f2
4 изменённых файлов: 29 добавлений и 59 удалений

Просмотреть файл

@ -49,8 +49,6 @@ let breakpoints: { [string]: Object };
let eventBreakpoints: ?EventListenerActiveList; let eventBreakpoints: ?EventListenerActiveList;
let supportsWasm: boolean; let supportsWasm: boolean;
let shouldWaitForWorkers = false;
type Dependencies = { type Dependencies = {
threadFront: ThreadFront, threadFront: ThreadFront,
tabTarget: TabTarget, tabTarget: TabTarget,
@ -121,15 +119,23 @@ function listWorkerThreadFronts() {
return (Object.values(workerClients): any).map(({ thread }) => thread); return (Object.values(workerClients): any).map(({ thread }) => thread);
} }
function forEachWorkerThread(iteratee) { function forEachThread(iteratee) {
const promises = listWorkerThreadFronts().map(thread => iteratee(thread)); // We have to be careful here to atomically initiate the operation on every
// thread, with no intervening await. Otherwise, other code could run and
// Do not return promises for the caller to wait on unless a flag is set. // trigger additional thread operations. Requests on server threads will
// Currently, worker threads are not guaranteed to respond to all requests, // resolve in FIFO order, and this could result in client and server state
// if we send a request while they are shutting down. See bug 1529163. // going out of sync.
if (shouldWaitForWorkers) { const mainThreadPromise = iteratee(threadFront);
return Promise.all(promises); const workerPromises = listWorkerThreadFronts().map(t => {
try {
iteratee(t);
} catch (e) {
// If a worker thread shuts down while sending the message then it will
// throw. Ignore these errors.
console.error(e);
} }
});
return Promise.all([mainThreadPromise, ...workerPromises]);
} }
function resume(thread: string): Promise<*> { function resume(thread: string): Promise<*> {
@ -186,10 +192,6 @@ function locationKey(location: BreakpointLocation) {
return `${(sourceUrl: any)}:${(sourceId: any)}:${line}:${(column: any)}`; return `${(sourceUrl: any)}:${(sourceId: any)}:${line}:${(column: any)}`;
} }
function waitForWorkers(shouldWait: boolean) {
shouldWaitForWorkers = shouldWait;
}
function detachWorkers() { function detachWorkers() {
for (const thread of listWorkerThreadFronts()) { for (const thread of listWorkerThreadFronts()) {
thread.detach(); thread.detach();
@ -217,7 +219,7 @@ function hasBreakpoint(location: BreakpointLocation) {
return !!breakpoints[locationKey(location)]; return !!breakpoints[locationKey(location)];
} }
async function setBreakpoint( function setBreakpoint(
location: BreakpointLocation, location: BreakpointLocation,
options: BreakpointOptions options: BreakpointOptions
) { ) {
@ -225,27 +227,14 @@ async function setBreakpoint(
options = maybeGenerateLogGroupId(options); options = maybeGenerateLogGroupId(options);
breakpoints[locationKey(location)] = { location, options }; breakpoints[locationKey(location)] = { location, options };
// We have to be careful here to atomically initiate the setBreakpoint() call return forEachThread(thread => thread.setBreakpoint(location, options));
// on every thread, with no intervening await. Otherwise, other code could run
// and change or remove the breakpoint before we finish calling setBreakpoint
// on all threads. Requests on server threads will resolve in FIFO order, and
// this could result in the breakpoint state here being out of sync with the
// breakpoints that are installed in the server.
const mainThreadPromise = threadFront.setBreakpoint(location, options);
await forEachWorkerThread(thread => thread.setBreakpoint(location, options));
await mainThreadPromise;
} }
async function removeBreakpoint(location: PendingLocation) { function removeBreakpoint(location: PendingLocation) {
maybeClearLogpoint((location: any)); maybeClearLogpoint((location: any));
delete breakpoints[locationKey((location: any))]; delete breakpoints[locationKey((location: any))];
// Delay waiting on this promise, for the same reason as in setBreakpoint. return forEachThread(thread => thread.removeBreakpoint(location));
const mainThreadPromise = threadFront.removeBreakpoint(location);
await forEachWorkerThread(thread => thread.removeBreakpoint(location));
await mainThreadPromise;
} }
async function evaluateInFrame(script: Script, options: EvaluateParam) { async function evaluateInFrame(script: Script, options: EvaluateParam) {
@ -325,21 +314,16 @@ async function getFrameScopes(frame: Frame): Promise<*> {
return sourceThreadFront.getEnvironment(frame.id); return sourceThreadFront.getEnvironment(frame.id);
} }
async function pauseOnExceptions( function pauseOnExceptions(
shouldPauseOnExceptions: boolean, shouldPauseOnExceptions: boolean,
shouldPauseOnCaughtExceptions: boolean shouldPauseOnCaughtExceptions: boolean
): Promise<*> { ): Promise<*> {
await threadFront.pauseOnExceptions( return forEachThread(thread =>
thread.pauseOnExceptions(
shouldPauseOnExceptions, shouldPauseOnExceptions,
// Providing opposite value because server // Providing opposite value because server
// uses "shouldIgnoreCaughtExceptions" // uses "shouldIgnoreCaughtExceptions"
!shouldPauseOnCaughtExceptions !shouldPauseOnCaughtExceptions
);
await forEachWorkerThread(thread =>
thread.pauseOnExceptions(
shouldPauseOnExceptions,
!shouldPauseOnCaughtExceptions
) )
); );
} }
@ -357,20 +341,18 @@ async function blackBox(
} }
} }
async function setSkipPausing(shouldSkip: boolean) { function setSkipPausing(shouldSkip: boolean) {
await threadFront.skipBreakpoints(shouldSkip); return forEachThread(thread => thread.skipBreakpoints(shouldSkip));
await forEachWorkerThread(thread => thread.skipBreakpoints(shouldSkip));
} }
function interrupt(thread: string): Promise<*> { function interrupt(thread: string): Promise<*> {
return lookupThreadFront(thread).interrupt(); return lookupThreadFront(thread).interrupt();
} }
async function setEventListenerBreakpoints(ids: string[]) { function setEventListenerBreakpoints(ids: string[]) {
eventBreakpoints = ids; eventBreakpoints = ids;
await threadFront.setActiveEventBreakpoints(ids); return forEachThread(thread => thread.setActiveEventBreakpoints(ids));
await forEachWorkerThread(thread => thread.setActiveEventBreakpoints(ids));
} }
// eslint-disable-next-line // eslint-disable-next-line
@ -563,7 +545,6 @@ const clientCommands = {
setSkipPausing, setSkipPausing,
setEventListenerBreakpoints, setEventListenerBreakpoints,
getEventListenerBreakpointTypes, getEventListenerBreakpointTypes,
waitForWorkers,
detachWorkers, detachWorkers,
hasWasmSupport, hasWasmSupport,
lookupConsoleClient, lookupConsoleClient,

Просмотреть файл

@ -9,11 +9,6 @@ add_task(async function() {
const workerSource = findSource(dbg, "simple-worker.js"); const workerSource = findSource(dbg, "simple-worker.js");
// NOTE: by default we do not wait on worker
// commands to complete because the thread could be
// shutting down.
dbg.client.waitForWorkers(true);
await selectSource(dbg, "simple-worker.js"); await selectSource(dbg, "simple-worker.js");
await waitForSelectedSource(dbg, "simple-worker.js"); await waitForSelectedSource(dbg, "simple-worker.js");
await addBreakpoint(dbg, workerSource, 1); await addBreakpoint(dbg, workerSource, 1);

Просмотреть файл

@ -44,11 +44,6 @@ add_task(async function() {
const dbg = await initDebugger("doc-windowless-workers.html"); const dbg = await initDebugger("doc-windowless-workers.html");
const mainThread = dbg.toolbox.threadFront.actor; const mainThread = dbg.toolbox.threadFront.actor;
// NOTE: by default we do not wait on worker
// commands to complete because the thread could be
// shutting down.
dbg.client.waitForWorkers(true);
const workers = await getWorkers(dbg); const workers = await getWorkers(dbg);
ok(workers.length == 2, "Got two workers"); ok(workers.length == 2, "Got two workers");
const thread1 = workers[0].actor; const thread1 = workers[0].actor;

Просмотреть файл

@ -531,7 +531,6 @@ async function initDebugger(url, ...sources) {
await clearDebuggerPreferences(); await clearDebuggerPreferences();
const toolbox = await openNewTabAndToolbox(EXAMPLE_URL + url, "jsdebugger"); const toolbox = await openNewTabAndToolbox(EXAMPLE_URL + url, "jsdebugger");
const dbg = createDebuggerContext(toolbox); const dbg = createDebuggerContext(toolbox);
dbg.client.waitForWorkers(false);
await waitForSources(dbg, ...sources); await waitForSources(dbg, ...sources);
return dbg; return dbg;