diff options
author | Philip Chimento <philip.chimento@gmail.com> | 2023-03-04 23:39:48 -0800 |
---|---|---|
committer | Philip Chimento <philip.chimento@gmail.com> | 2023-03-04 23:44:19 -0800 |
commit | 52adc35e38b2475fd6468982bb9ed31d50304f36 (patch) | |
tree | b9ece2e1fda25100814480aaea1c21cf0701bdd4 /gjs/context.cpp | |
parent | 38161234b69941ee6dc3e3006300dea0d5ee3e45 (diff) | |
download | gjs-52adc35e38b2475fd6468982bb9ed31d50304f36.tar.gz |
context: Fix job queue ending prematurely when dispatcher reset
When the promise job dispatcher is reset, its GCancellable is replaced
with a different instance. However, the original GCancellable instance was
passed to GjsContextPrivate::runJobs(), which aborts the job queue if the
GCancellable is cancelled.
This wasn't normally a problem, because the GCancellable isn't usually
cancelled. However, running JS with code coverage instrumentation starts
and stops the job dispatcher after every job, in order for the debugger
code to do its thing. So the GCancellable was getting cancelled and
immediately replaced with a fresh one, but the job queue was still getting
aborted.
This still didn't seem to have any noticeable effect until we merged the
runAsync() feature recently. From that point, async module evaluate
operations started getting dropped on the floor when code coverage was
active, which is very bad news.
The fix is to not hold on to the GCancellable across jobs. Instead of
checking whether the GCancellable is cancelled, instead check whether the
promise job dispatcher is running.
Diffstat (limited to 'gjs/context.cpp')
-rw-r--r-- | gjs/context.cpp | 10 |
1 files changed, 4 insertions, 6 deletions
diff --git a/gjs/context.cpp b/gjs/context.cpp index 8d2cbc6e..59ac8afd 100644 --- a/gjs/context.cpp +++ b/gjs/context.cpp @@ -1045,12 +1045,10 @@ bool GjsContextPrivate::enqueuePromiseJob(JSContext* cx [[maybe_unused]], // Override of JobQueue::runJobs(). Called by js::RunJobs(), and when execution // of the job queue was interrupted by the debugger and is resuming. -void GjsContextPrivate::runJobs(JSContext* cx) { runJobs(cx, nullptr); } - -void GjsContextPrivate::runJobs(JSContext* cx, GCancellable* cancellable) { +void GjsContextPrivate::runJobs(JSContext* cx) { g_assert(cx == m_cx); g_assert(from_cx(cx) == this); - if (!run_jobs_fallible(cancellable)) + if (!run_jobs_fallible()) gjs_log_exception(cx); } @@ -1066,7 +1064,7 @@ void GjsContextPrivate::runJobs(JSContext* cx, GCancellable* cancellable) { * Returns: false if one of the jobs threw an uncatchable exception; * otherwise true. */ -bool GjsContextPrivate::run_jobs_fallible(GCancellable* cancellable) { +bool GjsContextPrivate::run_jobs_fallible() { bool retval = true; if (m_draining_job_queue || m_should_exit) @@ -1083,7 +1081,7 @@ bool GjsContextPrivate::run_jobs_fallible(GCancellable* cancellable) { * it's crucial to recheck the queue length during each iteration. */ for (size_t ix = 0; ix < m_job_queue.length(); ix++) { /* A previous job might have set this flag. e.g., System.exit(). */ - if (m_should_exit || g_cancellable_is_cancelled(cancellable)) { + if (m_should_exit || !m_dispatcher.is_running()) { gjs_debug(GJS_DEBUG_MAINLOOP, "Stopping jobs because of %s", m_should_exit ? "exit" : "main loop cancel"); break; |