Skip to content

Commit

Permalink
[Hermes] Bump Hermes (#45220)
Browse files Browse the repository at this point in the history
* [Hermes] Bump Hermes

* Implement queueMicrotask and drainMicrotasks in JSC

Summary:
Changelog: [internal]

## Context

We want to enable the new React Native event loop by default for all users on the new RN architecture (on the bridgeless initialization path more concretely), which requires support for microtasks in all the JS engines that the support (Hermes already has it, JSC doesn't).

## Changes

This adds initial support for microtasks in JSC, so we can schedule and execute microtasks in this runtime.

One limitation about this approach is that, AFAIK, the public API for JSC doesn't allow us to customize its internal microtask queue or specify the method to be used by its built-in `Promise` or native `async function`, so we're forced to continue using a polyfill in that case (which uses `setImmediate` that will be mapped to `queueMicrotask`).

Reviewed By: NickGerleman

Differential Revision: D54302534

fbshipit-source-id: 47f71620344a81bc6624917f77452106ffbf55a3

---------

Co-authored-by: Rubén Norte <rubennorte@meta.com>
  • Loading branch information
cipolleschi and rubennorte committed Jul 1, 2024
1 parent 28ccfdc commit 3a1ff94
Show file tree
Hide file tree
Showing 4 changed files with 45 additions and 3 deletions.
32 changes: 30 additions & 2 deletions packages/react-native/ReactCommon/jsc/JSCRuntime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
#include <atomic>
#include <condition_variable>
#include <cstdlib>
#include <deque>
#include <mutex>
#include <queue>
#include <sstream>
#include <thread>

Expand Down Expand Up @@ -51,6 +51,12 @@ class JSCRuntime : public jsi::Runtime {
const std::shared_ptr<const jsi::Buffer>& buffer,
const std::string& sourceURL) override;

// If we use this interface to implement microtasks in the host we need to
// polyfill `Promise` to use these methods, because JSC doesn't currently
// support providing a custom queue for its built-in implementation.
// Not doing this would result in a non-compliant behavior, as microtasks
// wouldn't execute in the order in which they were queued.
void queueMicrotask(const jsi::Function& callback) override;
bool drainMicrotasks(int maxMicrotasksHint = -1) override;

jsi::Object global() override;
Expand Down Expand Up @@ -265,6 +271,7 @@ class JSCRuntime : public jsi::Runtime {
std::atomic<bool> ctxInvalid_;
std::string desc_;
JSValueRef nativeStateSymbol_ = nullptr;
std::deque<jsi::Function> microtaskQueue_;
#ifndef NDEBUG
mutable std::atomic<intptr_t> objectCounter_;
mutable std::atomic<intptr_t> symbolCounter_;
Expand Down Expand Up @@ -380,6 +387,10 @@ JSCRuntime::JSCRuntime(JSGlobalContextRef ctx)
}

JSCRuntime::~JSCRuntime() {
// We need to clear the microtask queue to remove all references to the
// callbacks, so objectCounter_ would be 0 below.
microtaskQueue_.clear();

// On shutting down and cleaning up: when JSC is actually torn down,
// it calls JSC::Heap::lastChanceToFinalize internally which
// finalizes anything left over. But at this point,
Expand Down Expand Up @@ -436,7 +447,24 @@ jsi::Value JSCRuntime::evaluateJavaScript(
return createValue(res);
}

bool JSCRuntime::drainMicrotasks(int maxMicrotasksHint) {
void JSCRuntime::queueMicrotask(const jsi::Function& callback) {
microtaskQueue_.emplace_back(
jsi::Value(*this, callback).asObject(*this).asFunction(*this));
}

bool JSCRuntime::drainMicrotasks(int /*maxMicrotasksHint*/) {
// Note that new jobs can be enqueued during the draining.
while (!microtaskQueue_.empty()) {
jsi::Function callback = std::move(microtaskQueue_.front());

// We need to pop before calling the callback because that might throw.
// When that happens, the host will call `drainMicrotasks` again to execute
// the remaining microtasks, and this one shouldn't run again.
microtaskQueue_.pop_front();

callback.call(*this);
}

return true;
}

Expand Down
7 changes: 7 additions & 0 deletions packages/react-native/ReactCommon/jsi/jsi/decorator.h
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,9 @@ class RuntimeDecorator : public Base, private jsi::Instrumentation {
const std::shared_ptr<const PreparedJavaScript>& js) override {
return plain().evaluatePreparedJavaScript(js);
}
void queueMicrotask(const jsi::Function& callback) override {
return plain().queueMicrotask(callback);
}
bool drainMicrotasks(int maxMicrotasksHint) override {
return plain().drainMicrotasks(maxMicrotasksHint);
}
Expand Down Expand Up @@ -544,6 +547,10 @@ class WithRuntimeDecorator : public RuntimeDecorator<Plain, Base> {
Around around{with_};
return RD::evaluatePreparedJavaScript(js);
}
void queueMicrotask(const Function& callback) override {
Around around{with_};
RD::queueMicrotask(callback);
}
bool drainMicrotasks(int maxMicrotasksHint) override {
Around around{with_};
return RD::drainMicrotasks(maxMicrotasksHint);
Expand Down
7 changes: 7 additions & 0 deletions packages/react-native/ReactCommon/jsi/jsi/jsi.h
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,13 @@ class JSI_EXPORT Runtime {
virtual Value evaluatePreparedJavaScript(
const std::shared_ptr<const PreparedJavaScript>& js) = 0;

/// Queues a microtask in the JavaScript VM internal Microtask (a.k.a. Job in
/// ECMA262) queue, to be executed when the host drains microtasks in
/// its event loop implementation.
///
/// \param callback a function to be executed as a microtask.
virtual void queueMicrotask(const jsi::Function& callback) = 0;

/// Drain the JavaScript VM internal Microtask (a.k.a. Job in ECMA262) queue.
///
/// \param maxMicrotasksHint a hint to tell an implementation that it should
Expand Down
2 changes: 1 addition & 1 deletion packages/react-native/sdks/.hermesversion
Original file line number Diff line number Diff line change
@@ -1 +1 @@
hermes-2024-06-03-RNv0.74.2-bb1e74fe1e95c2b5a2f4f9311152da052badc2bc
hermes-2024-06-28-RNv0.74.3-7bda0c267e76d11b68a585f84cfdd65000babf85

0 comments on commit 3a1ff94

Please sign in to comment.