Skip to content

Commit

Permalink
Introduce drainMicrotasks to JSI
Browse files Browse the repository at this point in the history
Summary:
Changelog:
[Internal][Added] - Introduce drainMicrotasks to JSI

This diff proposed a new JSI API `drainMicrotasks` to define
how hosts may integrate with the JSVMs' internal microtask
queue (a.k.a. job queue) and hence their native Promise.

The name `drainMicrotasks` is slightly preferred over `drainJobs`
to favor *host-friendliness* over *engine-friendliness*.

This diff auto implement the new API in JSC and Hermes as stubs
to make sure things compiled.

Please refer to the doc-comments in the `jsi.h` for a detailed
documentation on the semantics and the design of this API.

### Notes on the existing APIs from JSVMs

The presence of such queue and APIs to operate on them are
ubiquitous:
- Hermes: `Runtime::drainJobs`
- V8: `MicrotaskQueue::PerformCheckpoint`
- JSC: `VM::drainMicrotasks`
- QuickJS: `JS_ExecutePendingJob`

The only exception is ChakraCore, which requires hosts to provide
the queue and set up the `JsSetPromiseContinuationCallback`,
but a JSI implementation can provide that queue trivially.

### Extra note on ECMA-262

ECMA-262 changed the spec at Mar 2020 from "asking an ECMAScript
implementation to maintain a Job Queue" to "Ask the embedder to
define its event loop, including the job queue" so technically:
- the internal approach is closer to the old spec.
- the ChakraCore approach is closer to the current spec.

Reviewed By: mhorowitz

Differential Revision: D27727920

fbshipit-source-id: b839b959facbc009f7d14b781e9287c46ea64373
  • Loading branch information
Huxpro authored and facebook-github-bot committed Apr 23, 2021
1 parent 266b21b commit bddff73
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 0 deletions.
7 changes: 7 additions & 0 deletions ReactCommon/jsi/JSCRuntime.cpp
Expand Up @@ -50,6 +50,9 @@ class JSCRuntime : public jsi::Runtime {
jsi::Value evaluateJavaScript(
const std::shared_ptr<const jsi::Buffer> &buffer,
const std::string &sourceURL) override;

bool drainMicrotasks(int maxMicrotasksHint = -1) override;

jsi::Object global() override;

std::string description() override;
Expand Down Expand Up @@ -432,6 +435,10 @@ jsi::Value JSCRuntime::evaluateJavaScript(
return createValue(res);
}

bool JSCRuntime::drainMicrotasks(int maxMicrotasksHint) {
return true;
}

jsi::Object JSCRuntime::global() {
return createObject(JSContextGetGlobalObject(ctx_));
}
Expand Down
7 changes: 7 additions & 0 deletions ReactCommon/jsi/jsi/decorator.h
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);
}
bool drainMicrotasks(int maxMicrotasksHint) override {
return plain().drainMicrotasks(maxMicrotasksHint);
}
Object global() override {
return plain().global();
}
Expand Down Expand Up @@ -491,6 +494,10 @@ class WithRuntimeDecorator : public RuntimeDecorator<Plain, Base> {
Around around{with_};
return RD::evaluatePreparedJavaScript(js);
}
bool drainMicrotasks(int maxMicrotasksHint) override {
Around around{with_};
return RD::drainMicrotasks(maxMicrotasksHint);
}
Object global() override {
Around around{with_};
return RD::global();
Expand Down
29 changes: 29 additions & 0 deletions ReactCommon/jsi/jsi/jsi.h
Expand Up @@ -185,6 +185,35 @@ class JSI_EXPORT Runtime {
virtual Value evaluatePreparedJavaScript(
const std::shared_ptr<const PreparedJavaScript>& js) = 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
/// make a best effort not execute more than the given number. It's default
/// to -1 for infinity (unbounded execution).
/// \return true if the queue is drained or false if there is more work to do.
///
/// When there were exceptions thrown from the execution of microtasks,
/// implementations shall discard the exceptional jobs. An implementation may
/// \throw a \c JSError object to signal the hosts to handle. In that case, an
/// implementation may or may not suspend the draining.
///
/// Hosts may call this function again to resume the draining if it was
/// suspended due to either exceptions or the \p maxMicrotasksHint bound.
/// E.g. a host may repetitively invoke this function until the queue is
/// drained to implement the "microtask checkpint" defined in WHATWG HTML
/// event loop: https://html.spec.whatwg.org/C#perform-a-microtask-checkpoint.
///
/// Note that error propagation is only a concern if a host needs to implement
/// `queueMicrotask`, a recent API that allows enqueueing aribitary functions
/// (hence may throw) as microtasks. Exceptions from ECMA-262 Promise Jobs are
/// handled internally to VMs and are never propagrated to hosts.
///
/// This API offers some queue management to hosts at its best effort due to
/// different behaviors and limitations imposed by different VMs and APIs. By
/// the time this is written, An implementation may swallow exceptions (JSC),
/// may not pause (V8), and may not support bounded executions.
virtual bool drainMicrotasks(int maxMicrotasksHint = -1) = 0;

/// \return the global object
virtual Object global() = 0;

Expand Down

0 comments on commit bddff73

Please sign in to comment.