summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEvan Welsh <contact@evanwelsh.com>2021-09-04 21:04:05 -0700
committerEvan Welsh <contact@evanwelsh.com>2021-09-04 21:15:10 -0700
commite51e0308d0bc8e2b8be23f177ee52bfe01ae83a4 (patch)
tree322eccfe7ea7695e1137a0fedb87b8f0a07a6d8c
parent71c481cc483e498365f4baca57f5581ecb8a3c43 (diff)
downloadgjs-ewlsh/top-level-await-mainloop.tar.gz
Add implicit main loop for dynamic importsewlsh/top-level-await-mainloop
-rw-r--r--gjs/context-private.h4
-rw-r--r--gjs/context.cpp10
-rw-r--r--gjs/internal.cpp9
-rw-r--r--gjs/mainloop.cpp55
-rw-r--r--gjs/mainloop.h30
-rw-r--r--meson.build1
6 files changed, 108 insertions, 1 deletions
diff --git a/gjs/context-private.h b/gjs/context-private.h
index 8004dbc0..6244a107 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"
namespace js {
@@ -81,6 +82,7 @@ class GjsContextPrivate : public JS::JobQueue {
JobQueueStorage m_job_queue;
GSource* m_promise_queue_source;
GCancellable* m_promise_queue_source_cancellable;
+ 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_ref() { m_main_loop->ref(); }
+ bool main_loop_unref() { return m_main_loop->unref(); }
[[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 83d5bedc..06f43e31 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"
@@ -453,6 +454,7 @@ void GjsContextPrivate::dispose(void) {
delete m_fundamental_table;
delete m_gtype_table;
delete m_atoms;
+ delete m_main_loop;
/* Tear down JS */
JS_DestroyContext(m_cx);
@@ -564,6 +566,8 @@ GjsContextPrivate::GjsContextPrivate(JSContext* cx, GjsContext* public_context)
m_atoms = new GjsAtoms();
+ m_main_loop = new Gjs::MainLoop();
+
if (ObjectBox::gtype() == 0)
g_error("Failed to initialize JSObject GType");
@@ -1264,6 +1268,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(m_public_context);
+
/* 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. */
@@ -1328,6 +1335,9 @@ bool GjsContextPrivate::eval_module(const char* identifier,
bool ok = JS::ModuleEvaluate(m_cx, obj);
+ if (ok)
+ m_main_loop->spin(m_public_context);
+
/* 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..be28ee38 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* priv = GjsContextPrivate::from_cx(promise->cx);
+ priv->main_loop_unref();
+
+ JSAutoRealm ac(promise->cx, priv->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 a reference to the main loop until this function resolves...
+ GjsContextPrivate::from_cx(cx)->main_loop_ref();
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..efe9d05a
--- /dev/null
+++ b/gjs/mainloop.cpp
@@ -0,0 +1,55 @@
+/* -*- 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 <gio/gio.h>
+#include <glib.h>
+
+#include "gjs/context-private.h"
+#include "gjs/mainloop.h"
+
+namespace Gjs {
+
+MainLoop::MainLoop() { m_refcount = 0; }
+
+void MainLoop::ref() { m_refcount++; }
+
+bool MainLoop::unref() {
+ if (m_refcount == 0)
+ return false;
+
+ m_refcount--;
+ return true;
+}
+
+void MainLoop::spin(GjsContext* context) {
+ auto priv = GjsContextPrivate::from_object(context);
+
+ // Check if System.exit() has been called.
+ if (priv->should_exit(nullptr))
+ return;
+
+ GjsAutoPointer<GMainContext, GMainContext, g_main_context_unref>
+ main_context(g_main_context_ref_thread_default());
+
+ do {
+ if (priv->should_exit(nullptr))
+ break;
+
+ // Allow the loop to block if the event loop is referenced.
+ bool can_block = m_refcount > 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 (priv->should_exit(nullptr))
+ break;
+ } while (
+ // If there are pending sources or the job queue is not empty
+ (m_refcount > 0 || !priv->empty()) &&
+ // and System.exit() has not been called
+ // continue spinning the event loop.
+ !priv->should_exit(nullptr));
+}
+}; // namespace Gjs
diff --git a/gjs/mainloop.h b/gjs/mainloop.h
new file mode 100644
index 00000000..9ab78a70
--- /dev/null
+++ b/gjs/mainloop.h
@@ -0,0 +1,30 @@
+/* -*- 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>
+ */
+
+#ifndef GJS_MAINLOOP_H_
+#define GJS_MAINLOOP_H_
+
+#include <config.h>
+#include <js/TypeDecls.h>
+
+#include "gjs/context.h"
+
+namespace Gjs {
+class MainLoop {
+ uint32_t m_refcount;
+
+ public:
+ MainLoop();
+
+ void ref();
+
+ bool unref();
+
+ void spin(GjsContext* context);
+};
+}; // namespace Gjs
+
+#endif // GJS_MAINLOOP_H_
diff --git a/meson.build b/meson.build
index bbfee34b..05c896e9 100644
--- a/meson.build
+++ b/meson.build
@@ -401,6 +401,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',