Skip to content

Commit ab0d1e2

Browse files
feat(js): improve synchorization mechanism
1 parent 4077ab8 commit ab0d1e2

File tree

4 files changed

+38
-38
lines changed

4 files changed

+38
-38
lines changed

src/shell/script/quickjs/quickjs.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2008,10 +2008,13 @@ void JS_SetSharedArrayBufferFunctions(JSRuntime *rt,
20082008
rt->sab_funcs = *sf;
20092009
}
20102010

2011+
extern void qjs_notify_job_enqueued(JSContext*);
2012+
20112013
/* return 0 if OK, < 0 if exception */
20122014
int JS_EnqueueJob(JSContext *ctx, JSJobFunc *job_func,
20132015
int argc, JSValueConst *argv)
20142016
{
2017+
qjs_notify_job_enqueued(ctx);
20152018
JSRuntime *rt = ctx->rt;
20162019
JSJobEntry *e;
20172020
int i;

src/shell/script/quickjspp.hpp

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include "quickjs.h"
44

55
#include <algorithm>
6+
#include <atomic>
67
#include <cassert>
78
#include <cstddef>
89
#include <cstdio>
@@ -1550,10 +1551,8 @@ class Context : public std::enable_shared_from_this<Context> {
15501551
JSContext *ctx;
15511552
thread_local static Context *current;
15521553
// for starting jobs quickly
1553-
std::condition_variable js_job_start_cv = {};
1554-
std::mutex js_job_start_mutex = {};
1554+
std::atomic_int pending_job_count = 0;
15551555
std::recursive_mutex js_mutex;
1556-
bool has_pending_job = false;
15571556
/** Module wrapper
15581557
* Workaround for lack of opaque pointer for module load function by keeping
15591558
* a list of modules in qjs::Context.
@@ -2235,8 +2234,7 @@ template <typename Key> property_proxy<Key>::operator Value() const {
22352234
} // namespace detail
22362235

22372236
template <typename Function> void Context::enqueueJob(Function &&job) {
2238-
std::lock_guard<std::mutex> lock(js_job_start_mutex);
2239-
std::unique_lock lock2(js_mutex);
2237+
std::unique_lock lock(js_mutex);
22402238
JSValue job_val = js_traits<std::function<void()>>::wrap(
22412239
ctx, std::forward<Function>(job));
22422240
JSValueConst arg = job_val;
@@ -2259,8 +2257,6 @@ template <typename Function> void Context::enqueueJob(Function &&job) {
22592257
},
22602258
1, &arg);
22612259

2262-
has_pending_job = true;
2263-
js_job_start_cv.notify_all();
22642260
JS_FreeValue(ctx, job_val);
22652261
if (err < 0)
22662262
throw exception{ctx};

src/shell/script/script.cc

Lines changed: 32 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -114,17 +114,20 @@ void script_context::watch_folder(const std::filesystem::path &path,
114114
auto reload_all = [&]() {
115115
dbgout("Reloading all scripts");
116116

117-
*stop_signal = true;
118-
stop_signal = std::make_shared<int>(false);
117+
if (js) {
118+
js->pending_job_count.exchange(-1);
119+
js->pending_job_count.notify_all();
120+
if (js_thread)
121+
js_thread->join();
122+
js->pending_job_count.exchange(0);
123+
}
119124

120-
if (js_thread)
121-
js_thread->join();
122125
dbgout("Creating JS thread");
123126
menu_callbacks_js.clear();
124127

125128
is_js_ready.exchange(false);
126129
js_thread.emplace(
127-
[&, this, stop_signal = stop_signal]() {
130+
[&, this]() {
128131
CPPTRACE_TRY {
129132
is_thread_js_main = true;
130133
set_thread_locale_utf8();
@@ -253,33 +256,26 @@ void script_context::watch_folder(const std::filesystem::path &path,
253256
is_js_ready.exchange(true);
254257
is_js_ready.notify_all();
255258

256-
while (auto ptr = js) {
257-
if (ptr->ctx) {
258-
while (ptr->has_pending_job && !*stop_signal) {
259-
std::unique_lock lock2(ptr->js_mutex);
260-
auto ctx = ptr->ctx;
261-
auto res = JS_ExecutePendingJob(rt->rt, &ctx);
262-
if (res == -999) {
263-
has_update = true;
264-
dbgout("JS loop critical error! "
265-
"Restarting...");
266-
return;
267-
}
268-
lock2.unlock();
269-
std::this_thread::yield();
270-
std::lock_guard lock(ptr->js_job_start_mutex);
271-
ptr->has_pending_job = JS_IsJobPending(rt->rt);
259+
while (true) {
260+
while (js->pending_job_count.load() > 0) {
261+
std::unique_lock lock(js->js_mutex);
262+
auto ctx = js->ctx;
263+
if (auto res = JS_ExecutePendingJob(rt->rt, &ctx);
264+
res < 0) {
265+
std::cerr << "Error executing pending JS job: ";
266+
auto val = qjs::Value{js->ctx,
267+
JS_GetException(js->ctx)};
268+
std::cerr << (std::string)val
269+
<< (std::string)val["stack"]
270+
<< std::endl;
272271
}
272+
lock.unlock();
273+
js->pending_job_count.fetch_sub(1);
274+
std::this_thread::yield();
273275
}
274-
if (*stop_signal)
276+
js->pending_job_count.wait(0);
277+
if (js->pending_job_count.load() == -1)
275278
break;
276-
std::unique_lock lock(js->js_job_start_mutex);
277-
if (js->has_pending_job)
278-
continue;
279-
js->js_job_start_cv.wait_for(
280-
lock, std::chrono::milliseconds(100), [&]() {
281-
return js->has_pending_job || *stop_signal;
282-
});
283279
}
284280
is_thread_js_main = false;
285281
}
@@ -290,7 +286,7 @@ void script_context::watch_folder(const std::filesystem::path &path,
290286
cpptrace::rethrow_and_wrap_if_needed();
291287
}
292288
},
293-
104857600); // 100 MB stack
289+
10485760); // 10 MB stack
294290
};
295291

296292
reload_all();
@@ -316,3 +312,9 @@ void script_context::watch_folder(const std::filesystem::path &path,
316312
}
317313

318314
} // namespace mb_shell
315+
316+
extern "C" void qjs_notify_job_enqueued(JSContext *jsctx) {
317+
auto &ctx = qjs::Context::get(jsctx);
318+
ctx.pending_job_count.fetch_add(1);
319+
ctx.pending_job_count.notify_all();
320+
}

src/shell/script/script.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ struct script_context {
2020
public:
2121
std::atomic<bool> is_js_ready{false};
2222

23-
std::shared_ptr<int> stop_signal = std::make_shared<int>(0);
2423
script_context();
2524
void bind();
2625

0 commit comments

Comments
 (0)