summaryrefslogtreecommitdiff
path: root/gjs/context.cpp
diff options
context:
space:
mode:
authorPhilip Chimento <philip.chimento@gmail.com>2023-03-04 23:39:48 -0800
committerPhilip Chimento <philip.chimento@gmail.com>2023-03-04 23:44:19 -0800
commit52adc35e38b2475fd6468982bb9ed31d50304f36 (patch)
treeb9ece2e1fda25100814480aaea1c21cf0701bdd4 /gjs/context.cpp
parent38161234b69941ee6dc3e3006300dea0d5ee3e45 (diff)
downloadgjs-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.cpp10
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;