Skip to content

Commit d418db6

Browse files
committed
Hop to the main thread only when something drains its queue
deno test runs JS off the main thread while Deno's main thread waits in its own loop, so every call hopped to a queue nobody served and the Deno smoke test hung on macOS.
1 parent b5b2fd8 commit d418db6

1 file changed

Lines changed: 32 additions & 2 deletions

File tree

‎bindings/js/src/napi_support.cc‎

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
#include "napi_support.h"
22

33
#include <atomic>
4+
#include <chrono>
45
#include <condition_variable>
56
#include <map>
7+
#include <memory>
68
#include <mutex>
79
#include <thread>
810
#include <tuple>
@@ -475,10 +477,38 @@ void RunOnMainThreadSync(const std::function<void()>& work) {
475477
done_signal.wait(lock, [&] { return done; });
476478
}
477479

480+
namespace {
481+
482+
// Whether something on the UI thread drains its queue — a host running the
483+
// platform loop, as under `deno desktop`. `deno test` also runs JS off the main
484+
// thread, but its main thread only waits in Deno's own loop: hopping there
485+
// would block forever, so calls stay on the JS thread instead.
486+
bool MainThreadIsServiced() {
487+
struct Probe {
488+
std::mutex mutex;
489+
std::condition_variable signal;
490+
bool ran = false;
491+
};
492+
// Shared, because a late run can land after this returns.
493+
auto probe = std::make_shared<Probe>();
494+
bool posted = nativeapi::RunOnMainThread([probe] {
495+
std::lock_guard<std::mutex> lock(probe->mutex);
496+
probe->ran = true;
497+
probe->signal.notify_one();
498+
});
499+
if (!posted) {
500+
return false;
501+
}
502+
std::unique_lock<std::mutex> lock(probe->mutex);
503+
return probe->signal.wait_for(lock, std::chrono::seconds(1), [&] { return probe->ran; });
504+
}
505+
506+
} // namespace
507+
478508
void InitRuntime(napi_env env) {
479509
g_js_thread = std::this_thread::get_id();
480-
g_hop_to_main_thread =
481-
!nativeapi::IsMainThread() && nativeapi::IsMainThreadDispatchSupported();
510+
g_hop_to_main_thread = !nativeapi::IsMainThread() &&
511+
nativeapi::IsMainThreadDispatchSupported() && MainThreadIsServiced();
482512
g_env_alive.store(true);
483513
napi_add_env_cleanup_hook(
484514
env, [](void*) { g_env_alive.store(false); }, nullptr);

0 commit comments

Comments
 (0)