Skip to content

Commit

Permalink
Add queueMicrotask method to JSI
Browse files Browse the repository at this point in the history
Summary:
Changelog: [internal]

## Context

Microtasks are an important aspect of JavaScript and they will become increasingly important in the hosts where we're currently using JSI.

For example, React Native is going to adopt an event loop processing model similar to the one on the Web, which means it would need the ability to schedule and execute microtasks in every iteration of the loop.

JSI already has a method to execute all pending microtasks (`drainMicrotasks`) but without a method to schedule microtasks this is incomplete.

We're currently testing microtasks with Hermes using an internal method to schedule microtasks (`HermesInternal.enqueueJob`) but we need a method in JSI so this also works in other runtimes like JSC and V8.

## Changes

This adds the `queueMicrotask` to the Runtime API in JSI so we have symmetric API for microtasks and we can implement the necessary functionality.

The expectation for JSI implementations is to queue microtasks from this method and from built-ins like Promises and async functions in the same queue, and not drain that queue until explicitly done via `drainMicrotasks` in JSI.

This also modifies Hermes and JSC to provide stubs for those methods, and the actual implementation will be done in following diffs.

Differential Revision: D54302536
  • Loading branch information
rubennorte authored and facebook-github-bot committed Feb 28, 2024
1 parent 6bc872f commit 5ef9730
Show file tree
Hide file tree
Showing 8 changed files with 24 additions and 0 deletions.
2 changes: 2 additions & 0 deletions API/hermes/TimerStats.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,8 @@ class TimedRuntime final : public jsi::RuntimeDecorator<jsi::Runtime> {
return RD::callAsConstructor(func, args, count);
}

void queueMicrotask(const jsi::Function & /*callback*/) override {}

bool drainMicrotasks(int maxMicrotasksHint) override {
auto timer = rts_.incomingTimer("drainMicrotasks");
return RD::drainMicrotasks(maxMicrotasksHint);
Expand Down
2 changes: 2 additions & 0 deletions API/hermes/TracingRuntime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,8 @@ jsi::Value TracingRuntime::evaluateJavaScript(
return res;
}

void TracingRuntime::queueMicrotask(const jsi::Function &callback) {}

bool TracingRuntime::drainMicrotasks(int maxMicrotasksHint) {
auto res = RD::drainMicrotasks(maxMicrotasksHint);
trace_.emplace_back<SynthTrace::DrainMicrotasksRecord>(
Expand Down
1 change: 1 addition & 0 deletions API/hermes/TracingRuntime.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ class TracingRuntime : public jsi::RuntimeDecorator<jsi::Runtime> {
const std::shared_ptr<const jsi::Buffer> &buffer,
const std::string &sourceURL) override;

void queueMicrotask(const jsi::Function &callback) override;
bool drainMicrotasks(int maxMicrotasksHint = -1) override;

jsi::Object createObject() override;
Expand Down
3 changes: 3 additions & 0 deletions API/hermes/hermes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -573,6 +573,7 @@ class HermesRuntimeImpl final : public HermesRuntime,
jsi::Value evaluateJavaScript(
const std::shared_ptr<const jsi::Buffer> &buffer,
const std::string &sourceURL) override;
void queueMicrotask(const jsi::Function &callback) override;
bool drainMicrotasks(int maxMicrotasksHint = -1) override;
jsi::Object global() override;

Expand Down Expand Up @@ -1502,6 +1503,8 @@ jsi::Value HermesRuntimeImpl::evaluateJavaScript(
return evaluateJavaScriptWithSourceMap(buffer, nullptr, sourceURL);
}

void HermesRuntimeImpl::queueMicrotask(const jsi::Function & /*callback*/) {}

bool HermesRuntimeImpl::drainMicrotasks(int maxMicrotasksHint) {
if (runtime_.hasMicrotaskQueue()) {
checkStatus(runtime_.drainJobs());
Expand Down
2 changes: 2 additions & 0 deletions API/hermes_abi/HermesABIRuntimeWrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -710,6 +710,8 @@ class HermesABIRuntimeWrapper : public Runtime {
return evaluateJavaScript(sjp, sjp->sourceURL());
}

void queueMicrotask(const Function & /*callback*/) override {}

bool drainMicrotasks(int maxMicrotasksHint = -1) override {
return unwrap(vtable_->drain_microtasks(abiRt_, maxMicrotasksHint));
}
Expand Down
2 changes: 2 additions & 0 deletions API/hermes_sandbox/HermesSandboxRuntime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1701,6 +1701,8 @@ class HermesSandboxRuntimeImpl : public facebook::hermes::HermesSandboxRuntime,
return evaluateJavaScript(sjp, sjp->sourceURL());
}

void queueMicrotask(const Function & /*callback*/) override {}

bool drainMicrotasks(int maxMicrotasksHint = -1) override {
SandboxBoolOrError resBoolOrError{
vt_.drain_microtasks(this, srt_, maxMicrotasksHint)};
Expand Down
7 changes: 7 additions & 0 deletions API/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
5 changes: 5 additions & 0 deletions API/jsi/jsi/jsi.h
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,11 @@ 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.
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

0 comments on commit 5ef9730

Please sign in to comment.