[gjs/ewlsh/top-level-await-mainloop] Add implicit main loop for dynamic imports
- From: Philip Chimento <pchimento src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gjs/ewlsh/top-level-await-mainloop] Add implicit main loop for dynamic imports
- Date: Sat, 16 Oct 2021 19:02:01 +0000 (UTC)
commit aea6b6b339b9905eee4921d60ef35143adfbccac
Author: Evan Welsh <contact evanwelsh com>
Date: Sat Sep 4 21:04:05 2021 -0700
Add implicit main loop for dynamic imports
gjs/context-private.h | 4 ++++
gjs/context.cpp | 13 ++++++++++++-
gjs/internal.cpp | 9 ++++++++-
gjs/mainloop.cpp | 44 ++++++++++++++++++++++++++++++++++++++++++++
gjs/mainloop.h | 35 +++++++++++++++++++++++++++++++++++
meson.build | 1 +
tools/run_iwyu.sh | 3 ++-
7 files changed, 106 insertions(+), 3 deletions(-)
---
diff --git a/gjs/context-private.h b/gjs/context-private.h
index ad24635c..0bfc8b1c 100644
--- a/gjs/context-private.h
+++ b/gjs/context-private.h
@@ -37,6 +37,7 @@
#include "gjs/context.h"
#include "gjs/jsapi-util.h"
#include "gjs/macros.h"
+#include "gjs/mainloop.h"
#include "gjs/profiler.h"
#include "gjs/promise.h"
@@ -81,6 +82,7 @@ class GjsContextPrivate : public JS::JobQueue {
JobQueueStorage m_job_queue;
Gjs::PromiseJobDispatcher m_dispatcher;
+ Gjs::MainLoop m_main_loop;
std::vector<std::pair<DestroyNotify, void*>> m_destroy_notifications;
std::vector<Gjs::Closure::Ptr> m_async_closures;
@@ -176,6 +178,8 @@ class GjsContextPrivate : public JS::JobQueue {
[[nodiscard]] JSObject* internal_global() const {
return m_internal_global.get();
}
+ void main_loop_hold() { m_main_loop.hold(); }
+ void main_loop_release() { m_main_loop.release(); }
[[nodiscard]] GjsProfiler* profiler() const { return m_profiler; }
[[nodiscard]] const GjsAtoms& atoms() const { return *m_atoms; }
[[nodiscard]] bool destroying() const { return m_destroying.load(); }
diff --git a/gjs/context.cpp b/gjs/context.cpp
index 6c369073..c0d1428c 100644
--- a/gjs/context.cpp
+++ b/gjs/context.cpp
@@ -75,6 +75,7 @@
#include "gjs/importer.h"
#include "gjs/internal.h"
#include "gjs/jsapi-util.h"
+#include "gjs/mainloop.h"
#include "gjs/mem.h"
#include "gjs/module.h"
#include "gjs/native.h"
@@ -1252,6 +1253,9 @@ bool GjsContextPrivate::eval(const char* script, ssize_t script_len,
JS::RootedValue retval(m_cx);
bool ok = eval_with_scope(nullptr, script, script_len, filename, &retval);
+ if (ok)
+ m_main_loop.spin(this);
+
/* The promise job queue should be drained even on error, to finish
* outstanding async tasks before the context is torn down. Drain after
* uncaught exceptions have been reported since draining runs callbacks. */
@@ -1268,13 +1272,17 @@ bool GjsContextPrivate::eval(const char* script, ssize_t script_len,
}
if (exit_status_p) {
+ uint8_t out_code;
if (retval.isInt32()) {
int code = retval.toInt32();
gjs_debug(GJS_DEBUG_CONTEXT,
"Script returned integer code %d", code);
*exit_status_p = code;
+ } else if (should_exit(&out_code)) {
+ *exit_status_p = out_code;
} else {
- /* Assume success if no integer was returned */
+ // Assume success if no integer was returned and should exit isn't
+ // set
*exit_status_p = 0;
}
}
@@ -1310,6 +1318,9 @@ bool GjsContextPrivate::eval_module(const char* identifier,
bool ok = JS::ModuleEvaluate(m_cx, obj);
+ if (ok)
+ m_main_loop.spin(this);
+
/* The promise job queue should be drained even on error, to finish
* outstanding async tasks before the context is torn down. Drain after
* uncaught exceptions have been reported since draining runs callbacks.
diff --git a/gjs/internal.cpp b/gjs/internal.cpp
index ed856ac4..a8ed70fc 100644
--- a/gjs/internal.cpp
+++ b/gjs/internal.cpp
@@ -39,6 +39,7 @@
#include "gjs/importer.h"
#include "gjs/jsapi-util-args.h"
#include "gjs/jsapi-util.h"
+#include "gjs/mainloop.h"
#include "gjs/mem-private.h"
#include "gjs/module.h"
#include "gjs/native.h"
@@ -503,7 +504,10 @@ class PromiseData {
static void load_async_callback(GObject* file, GAsyncResult* res, void* data) {
std::unique_ptr<PromiseData> promise(PromiseData::from_ptr(data));
- JSAutoRealm ac(promise->cx, gjs_get_import_global(promise->cx));
+ GjsContextPrivate* gjs = GjsContextPrivate::from_cx(promise->cx);
+ gjs->main_loop_release();
+
+ JSAutoRealm ar(promise->cx, gjs->global());
char* contents;
size_t length;
@@ -551,6 +555,9 @@ static bool load_async_executor(JSContext* cx, unsigned argc, JS::Value* vp) {
auto* data = new PromiseData(cx, JS_GetObjectFunction(resolve),
JS_GetObjectFunction(reject));
+
+ // Hold the main loop until this function resolves...
+ GjsContextPrivate::from_cx(cx)->main_loop_hold();
g_file_load_contents_async(file, nullptr, load_async_callback, data);
args.rval().setUndefined();
diff --git a/gjs/mainloop.cpp b/gjs/mainloop.cpp
new file mode 100644
index 00000000..6869a448
--- /dev/null
+++ b/gjs/mainloop.cpp
@@ -0,0 +1,44 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
+// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later
+// SPDX-FileCopyrightText: 2021 Evan Welsh <contact evanwelsh com>
+
+#include <config.h>
+
+#include <glib.h>
+
+#include "gjs/context-private.h"
+#include "gjs/jsapi-util.h"
+#include "gjs/mainloop.h"
+
+namespace Gjs {
+
+void MainLoop::spin(GjsContextPrivate* gjs) {
+ // Check if System.exit() has been called.
+ if (gjs->should_exit(nullptr))
+ return;
+
+ GjsAutoPointer<GMainContext, GMainContext, g_main_context_unref>
+ main_context(g_main_context_ref_thread_default());
+
+ do {
+ if (gjs->should_exit(nullptr))
+ break;
+
+ // Allow the loop to block if the event loop is being held.
+ bool can_block = m_hold_count > 0;
+ // Only run the loop if there are pending jobs.
+ if (g_main_context_pending(main_context))
+ g_main_context_iteration(main_context, can_block);
+
+ // Check if System.exit() has been called.
+ if (gjs->should_exit(nullptr))
+ break;
+ } while (
+ // If there are pending sources or the job queue is not empty
+ (m_hold_count > 0 || !gjs->empty()) &&
+ // and System.exit() has not been called
+ // continue spinning the event loop.
+ !gjs->should_exit(nullptr));
+}
+
+}; // namespace Gjs
diff --git a/gjs/mainloop.h b/gjs/mainloop.h
new file mode 100644
index 00000000..1252e969
--- /dev/null
+++ b/gjs/mainloop.h
@@ -0,0 +1,35 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
+// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later
+// SPDX-FileCopyrightText: 2021 Evan Welsh <contact evanwelsh com>
+
+#pragma once
+
+#include <config.h>
+
+#include <glib.h>
+
+class GjsContextPrivate;
+
+namespace Gjs {
+
+class MainLoop {
+ // grefcounts start at one and become invalidated when they are decremented
+ // to zero. So the actual hold count is equal to the "ref" count minus 1.
+ // We nonetheless use grefcount here because it takes care of dealing with
+ // integer overflow for us.
+ grefcount m_hold_count;
+
+ public:
+ MainLoop() { g_ref_count_init(&m_hold_count); }
+ ~MainLoop() { g_assert(g_ref_count_compare(&m_hold_count, 1)); }
+
+ void hold() { g_ref_count_inc(&m_hold_count); }
+ void release() {
+ bool zero [[maybe_unused]] = g_ref_count_dec(&m_hold_count);
+ g_assert(!zero && "main loop released too many times");
+ }
+
+ void spin(GjsContextPrivate*);
+};
+
+}; // namespace Gjs
diff --git a/meson.build b/meson.build
index 437b3fd3..53715bd1 100644
--- a/meson.build
+++ b/meson.build
@@ -418,6 +418,7 @@ libgjs_sources = [
'gjs/global.cpp', 'gjs/global.h',
'gjs/importer.cpp', 'gjs/importer.h',
'gjs/internal.cpp', 'gjs/internal.h',
+ 'gjs/mainloop.cpp', 'gjs/mainloop.h',
'gjs/mem.cpp', 'gjs/mem-private.h',
'gjs/module.cpp', 'gjs/module.h',
'gjs/native.cpp', 'gjs/native.h',
diff --git a/tools/run_iwyu.sh b/tools/run_iwyu.sh
index 7e1db246..520b8549 100755
--- a/tools/run_iwyu.sh
+++ b/tools/run_iwyu.sh
@@ -70,7 +70,8 @@ for FILE in $SRCDIR/gi/*.cpp $SRCDIR/gjs/atoms.cpp $SRCDIR/gjs/byteArray.cpp \
$SRCDIR/gjs/coverage.cpp $SRCDIR/gjs/debugger.cpp \
$SRCDIR/gjs/deprecation.cpp $SRCDIR/gjs/error-types.cpp \
$SRCDIR/gjs/engine.cpp $SRCDIR/gjs/global.cpp $SRCDIR/gjs/importer.cpp \
- $SRCDIR/gjs/jsapi-util*.cpp $SRCDIR/gjs/module.cpp $SRCDIR/gjs/native.cpp \
+ $SRCDIR/gjs/jsapi-util*.cpp $SRCDIR/gjs/mainloop.cpp \
+ $SRCDIR/gjs/module.cpp $SRCDIR/gjs/native.cpp \
$SRCDIR/gjs/objectbox.cpp $SRCDIR/gjs/promise.cpp $SRCDIR/gjs/stack.cpp \
$SRCDIR/modules/cairo-*.cpp $SRCDIR/modules/console.cpp \
$SRCDIR/modules/print.cpp $SRCDIR/modules/system.cpp $SRCDIR/test/*.cpp \
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]