Commit cce33f37 authored by tzik's avatar tzik Committed by Commit Bot

Expose MicrotaskQueue as a V8 API

This introduces v8::MicrotaskQueue backed by v8::internal::MicrotaskQueue.

The embedder will get an option to use non-default MicrotaskQueue by creating
the instance by v8::MicrotaskQueue::New(). The instance can be attached to
a Context by passing it to Context::New().

Bug: v8:8124
Change-Id: Iee0711785d5748860eb94e30a8d83199a743ffaa
Reviewed-on: https://chromium-review.googlesource.com/c/1414950
Commit-Queue: Taiju Tsuiki <tzik@chromium.org>
Reviewed-by: 's avatarBenedikt Meurer <bmeurer@chromium.org>
Reviewed-by: 's avatarYang Guo <yangguo@chromium.org>
Reviewed-by: 's avatarAdam Klein <adamk@chromium.org>
Cr-Commit-Position: refs/heads/master@{#59933}
parent b2f8280e
...@@ -54,6 +54,7 @@ class Integer; ...@@ -54,6 +54,7 @@ class Integer;
class Isolate; class Isolate;
template <class T> template <class T>
class Maybe; class Maybe;
class MicrotaskQueue;
class Name; class Name;
class Number; class Number;
class NumberObject; class NumberObject;
...@@ -6736,6 +6737,7 @@ typedef void (*PromiseRejectCallback)(PromiseRejectMessage message); ...@@ -6736,6 +6737,7 @@ typedef void (*PromiseRejectCallback)(PromiseRejectMessage message);
// --- Microtasks Callbacks --- // --- Microtasks Callbacks ---
typedef void (*MicrotasksCompletedCallback)(Isolate*); typedef void (*MicrotasksCompletedCallback)(Isolate*);
typedef void (*MicrotasksCompletedCallbackWithData)(Isolate*, void*);
typedef void (*MicrotaskCallback)(void* data); typedef void (*MicrotaskCallback)(void* data);
...@@ -6748,6 +6750,80 @@ typedef void (*MicrotaskCallback)(void* data); ...@@ -6748,6 +6750,80 @@ typedef void (*MicrotaskCallback)(void* data);
*/ */
enum class MicrotasksPolicy { kExplicit, kScoped, kAuto }; enum class MicrotasksPolicy { kExplicit, kScoped, kAuto };
/**
* Represents the microtask queue, where microtasks are stored and processed.
* https://html.spec.whatwg.org/multipage/webappapis.html#microtask-queue
* https://html.spec.whatwg.org/multipage/webappapis.html#enqueuejob(queuename,-job,-arguments)
* https://html.spec.whatwg.org/multipage/webappapis.html#perform-a-microtask-checkpoint
*
* A MicrotaskQueue instance may be associated to multiple Contexts by passing
* it to Context::New(), and they can be detached by Context::DetachGlobal().
* The embedder must keep the MicrotaskQueue instance alive until all associated
* Contexts are gone or detached.
*
* Use the same instance of MicrotaskQueue for all Contexts that may access each
* other synchronously. E.g. for Web embedding, use the same instance for all
* origins that share the same URL scheme and eTLD+1.
*/
class V8_EXPORT MicrotaskQueue {
public:
/**
* Creates an empty MicrotaskQueue instance.
*/
static std::unique_ptr<MicrotaskQueue> New();
virtual ~MicrotaskQueue() = default;
/**
* Enqueues the callback to the queue.
*/
virtual void EnqueueMicrotask(Isolate* isolate,
Local<Function> microtask) = 0;
/**
* Enqueues the callback to the queue.
*/
virtual void EnqueueMicrotask(v8::Isolate* isolate,
MicrotaskCallback callback,
void* data = nullptr) = 0;
/**
* Adds a callback to notify the embedder after microtasks were run. The
* callback is triggered by explicit RunMicrotasks call or automatic
* microtasks execution (see Isolate::SetMicrotasksPolicy).
*
* Callback will trigger even if microtasks were attempted to run,
* but the microtasks queue was empty and no single microtask was actually
* executed.
*
* Executing scripts inside the callback will not re-trigger microtasks and
* the callback.
*/
virtual void AddMicrotasksCompletedCallback(
MicrotasksCompletedCallbackWithData callback, void* data = nullptr) = 0;
/**
* Removes callback that was installed by AddMicrotasksCompletedCallback.
*/
virtual void RemoveMicrotasksCompletedCallback(
MicrotasksCompletedCallbackWithData callback, void* data = nullptr) = 0;
/**
* Runs microtasks if no microtask is running on this MicrotaskQueue instance.
*/
virtual void PerformCheckpoint(Isolate* isolate) = 0;
/**
* Returns true if a microtask is running on this MicrotaskQueue instance.
*/
virtual bool IsRunningMicrotasks() const = 0;
private:
friend class internal::MicrotaskQueue;
MicrotaskQueue() = default;
MicrotaskQueue(const MicrotaskQueue&) = delete;
MicrotaskQueue& operator=(const MicrotaskQueue&) = delete;
};
/** /**
* This scope is used to control microtasks when kScopeMicrotasksInvocation * This scope is used to control microtasks when kScopeMicrotasksInvocation
...@@ -6763,6 +6839,7 @@ class V8_EXPORT MicrotasksScope { ...@@ -6763,6 +6839,7 @@ class V8_EXPORT MicrotasksScope {
enum Type { kRunMicrotasks, kDoNotRunMicrotasks }; enum Type { kRunMicrotasks, kDoNotRunMicrotasks };
MicrotasksScope(Isolate* isolate, Type type); MicrotasksScope(Isolate* isolate, Type type);
MicrotasksScope(Isolate* isolate, MicrotaskQueue* microtask_queue, Type type);
~MicrotasksScope(); ~MicrotasksScope();
/** /**
...@@ -7432,6 +7509,7 @@ class V8_EXPORT Isolate { ...@@ -7432,6 +7509,7 @@ class V8_EXPORT Isolate {
class V8_EXPORT SuppressMicrotaskExecutionScope { class V8_EXPORT SuppressMicrotaskExecutionScope {
public: public:
explicit SuppressMicrotaskExecutionScope(Isolate* isolate); explicit SuppressMicrotaskExecutionScope(Isolate* isolate);
explicit SuppressMicrotaskExecutionScope(MicrotaskQueue* microtask_queue);
~SuppressMicrotaskExecutionScope(); ~SuppressMicrotaskExecutionScope();
// Prevent copying of Scope objects. // Prevent copying of Scope objects.
...@@ -8107,18 +8185,18 @@ class V8_EXPORT Isolate { ...@@ -8107,18 +8185,18 @@ class V8_EXPORT Isolate {
void SetPromiseRejectCallback(PromiseRejectCallback callback); void SetPromiseRejectCallback(PromiseRejectCallback callback);
/** /**
* Runs the Microtask Work Queue until empty * Runs the default MicrotaskQueue until it gets empty.
* Any exceptions thrown by microtask callbacks are swallowed. * Any exceptions thrown by microtask callbacks are swallowed.
*/ */
void RunMicrotasks(); void RunMicrotasks();
/** /**
* Enqueues the callback to the Microtask Work Queue * Enqueues the callback to the default MicrotaskQueue
*/ */
void EnqueueMicrotask(Local<Function> microtask); void EnqueueMicrotask(Local<Function> microtask);
/** /**
* Enqueues the callback to the Microtask Work Queue * Enqueues the callback to the default MicrotaskQueue
*/ */
void EnqueueMicrotask(MicrotaskCallback callback, void* data = nullptr); void EnqueueMicrotask(MicrotaskCallback callback, void* data = nullptr);
...@@ -8134,14 +8212,15 @@ class V8_EXPORT Isolate { ...@@ -8134,14 +8212,15 @@ class V8_EXPORT Isolate {
/** /**
* Adds a callback to notify the host application after * Adds a callback to notify the host application after
* microtasks were run. The callback is triggered by explicit RunMicrotasks * microtasks were run on the default MicrotaskQueue. The callback is
* call or automatic microtasks execution (see SetAutorunMicrotasks). * triggered by explicit RunMicrotasks call or automatic microtasks execution
* (see SetMicrotaskPolicy).
* *
* Callback will trigger even if microtasks were attempted to run, * Callback will trigger even if microtasks were attempted to run,
* but the microtasks queue was empty and no single microtask was actually * but the microtasks queue was empty and no single microtask was actually
* executed. * executed.
* *
* Executing scriptsinside the callback will not re-trigger microtasks and * Executing scripts inside the callback will not re-trigger microtasks and
* the callback. * the callback.
*/ */
void AddMicrotasksCompletedCallback(MicrotasksCompletedCallback callback); void AddMicrotasksCompletedCallback(MicrotasksCompletedCallback callback);
...@@ -9172,7 +9251,8 @@ class V8_EXPORT Context { ...@@ -9172,7 +9251,8 @@ class V8_EXPORT Context {
MaybeLocal<ObjectTemplate> global_template = MaybeLocal<ObjectTemplate>(), MaybeLocal<ObjectTemplate> global_template = MaybeLocal<ObjectTemplate>(),
MaybeLocal<Value> global_object = MaybeLocal<Value>(), MaybeLocal<Value> global_object = MaybeLocal<Value>(),
DeserializeInternalFieldsCallback internal_fields_deserializer = DeserializeInternalFieldsCallback internal_fields_deserializer =
DeserializeInternalFieldsCallback()); DeserializeInternalFieldsCallback(),
MicrotaskQueue* microtask_queue = nullptr);
/** /**
* Create a new context from a (non-default) context snapshot. There * Create a new context from a (non-default) context snapshot. There
...@@ -9192,13 +9272,13 @@ class V8_EXPORT Context { ...@@ -9192,13 +9272,13 @@ class V8_EXPORT Context {
* *
* \param global_object See v8::Context::New. * \param global_object See v8::Context::New.
*/ */
static MaybeLocal<Context> FromSnapshot( static MaybeLocal<Context> FromSnapshot(
Isolate* isolate, size_t context_snapshot_index, Isolate* isolate, size_t context_snapshot_index,
DeserializeInternalFieldsCallback embedder_fields_deserializer = DeserializeInternalFieldsCallback embedder_fields_deserializer =
DeserializeInternalFieldsCallback(), DeserializeInternalFieldsCallback(),
ExtensionConfiguration* extensions = nullptr, ExtensionConfiguration* extensions = nullptr,
MaybeLocal<Value> global_object = MaybeLocal<Value>()); MaybeLocal<Value> global_object = MaybeLocal<Value>(),
MicrotaskQueue* microtask_queue = nullptr);
/** /**
* Returns an global object that isn't backed by an actual context. * Returns an global object that isn't backed by an actual context.
......
This diff is collapsed.
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#include "src/heap/heap-inl.h" #include "src/heap/heap-inl.h"
#include "src/isolate-inl.h" #include "src/isolate-inl.h"
#include "src/math-random.h" #include "src/math-random.h"
#include "src/microtask-queue.h"
#include "src/objects/api-callbacks.h" #include "src/objects/api-callbacks.h"
#include "src/objects/arguments.h" #include "src/objects/arguments.h"
#include "src/objects/builtin-function-id.h" #include "src/objects/builtin-function-id.h"
...@@ -139,7 +140,8 @@ class Genesis { ...@@ -139,7 +140,8 @@ class Genesis {
Genesis(Isolate* isolate, MaybeHandle<JSGlobalProxy> maybe_global_proxy, Genesis(Isolate* isolate, MaybeHandle<JSGlobalProxy> maybe_global_proxy,
v8::Local<v8::ObjectTemplate> global_proxy_template, v8::Local<v8::ObjectTemplate> global_proxy_template,
size_t context_snapshot_index, size_t context_snapshot_index,
v8::DeserializeEmbedderFieldsCallback embedder_fields_deserializer); v8::DeserializeEmbedderFieldsCallback embedder_fields_deserializer,
v8::MicrotaskQueue* microtask_queue);
Genesis(Isolate* isolate, MaybeHandle<JSGlobalProxy> maybe_global_proxy, Genesis(Isolate* isolate, MaybeHandle<JSGlobalProxy> maybe_global_proxy,
v8::Local<v8::ObjectTemplate> global_proxy_template); v8::Local<v8::ObjectTemplate> global_proxy_template);
~Genesis() = default; ~Genesis() = default;
...@@ -302,12 +304,14 @@ Handle<Context> Bootstrapper::CreateEnvironment( ...@@ -302,12 +304,14 @@ Handle<Context> Bootstrapper::CreateEnvironment(
MaybeHandle<JSGlobalProxy> maybe_global_proxy, MaybeHandle<JSGlobalProxy> maybe_global_proxy,
v8::Local<v8::ObjectTemplate> global_proxy_template, v8::Local<v8::ObjectTemplate> global_proxy_template,
v8::ExtensionConfiguration* extensions, size_t context_snapshot_index, v8::ExtensionConfiguration* extensions, size_t context_snapshot_index,
v8::DeserializeEmbedderFieldsCallback embedder_fields_deserializer) { v8::DeserializeEmbedderFieldsCallback embedder_fields_deserializer,
v8::MicrotaskQueue* microtask_queue) {
HandleScope scope(isolate_); HandleScope scope(isolate_);
Handle<Context> env; Handle<Context> env;
{ {
Genesis genesis(isolate_, maybe_global_proxy, global_proxy_template, Genesis genesis(isolate_, maybe_global_proxy, global_proxy_template,
context_snapshot_index, embedder_fields_deserializer); context_snapshot_index, embedder_fields_deserializer,
microtask_queue);
env = genesis.result(); env = genesis.result();
if (env.is_null() || !InstallExtensions(env, extensions)) { if (env.is_null() || !InstallExtensions(env, extensions)) {
return Handle<Context>(); return Handle<Context>();
...@@ -5504,7 +5508,8 @@ Genesis::Genesis( ...@@ -5504,7 +5508,8 @@ Genesis::Genesis(
Isolate* isolate, MaybeHandle<JSGlobalProxy> maybe_global_proxy, Isolate* isolate, MaybeHandle<JSGlobalProxy> maybe_global_proxy,
v8::Local<v8::ObjectTemplate> global_proxy_template, v8::Local<v8::ObjectTemplate> global_proxy_template,
size_t context_snapshot_index, size_t context_snapshot_index,
v8::DeserializeEmbedderFieldsCallback embedder_fields_deserializer) v8::DeserializeEmbedderFieldsCallback embedder_fields_deserializer,
v8::MicrotaskQueue* microtask_queue)
: isolate_(isolate), active_(isolate->bootstrapper()) { : isolate_(isolate), active_(isolate->bootstrapper()) {
RuntimeCallTimerScope rcs_timer(isolate, RuntimeCallCounterId::kGenesis); RuntimeCallTimerScope rcs_timer(isolate, RuntimeCallCounterId::kGenesis);
result_ = Handle<Context>::null(); result_ = Handle<Context>::null();
...@@ -5600,7 +5605,9 @@ Genesis::Genesis( ...@@ -5600,7 +5605,9 @@ Genesis::Genesis(
} }
} }
native_context()->set_microtask_queue(isolate->default_microtask_queue()); native_context()->set_microtask_queue(
microtask_queue ? static_cast<MicrotaskQueue*>(microtask_queue)
: isolate->default_microtask_queue());
// Install experimental natives. Do not include them into the // Install experimental natives. Do not include them into the
// snapshot as we should be able to turn them off at runtime. Re-installing // snapshot as we should be able to turn them off at runtime. Re-installing
......
...@@ -57,7 +57,8 @@ class Bootstrapper final { ...@@ -57,7 +57,8 @@ class Bootstrapper final {
MaybeHandle<JSGlobalProxy> maybe_global_proxy, MaybeHandle<JSGlobalProxy> maybe_global_proxy,
v8::Local<v8::ObjectTemplate> global_object_template, v8::Local<v8::ObjectTemplate> global_object_template,
v8::ExtensionConfiguration* extensions, size_t context_snapshot_index, v8::ExtensionConfiguration* extensions, size_t context_snapshot_index,
v8::DeserializeEmbedderFieldsCallback embedder_fields_deserializer); v8::DeserializeEmbedderFieldsCallback embedder_fields_deserializer,
v8::MicrotaskQueue* microtask_queue);
Handle<JSGlobalProxy> NewRemoteContext( Handle<JSGlobalProxy> NewRemoteContext(
MaybeHandle<JSGlobalProxy> maybe_global_proxy, MaybeHandle<JSGlobalProxy> maybe_global_proxy,
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
#include <stddef.h> #include <stddef.h>
#include <algorithm> #include <algorithm>
#include "src/api.h" #include "src/api-inl.h"
#include "src/base/logging.h" #include "src/base/logging.h"
#include "src/handles-inl.h" #include "src/handles-inl.h"
#include "src/isolate.h" #include "src/isolate.h"
...@@ -77,6 +77,26 @@ Address MicrotaskQueue::CallEnqueueMicrotask(Isolate* isolate, ...@@ -77,6 +77,26 @@ Address MicrotaskQueue::CallEnqueueMicrotask(Isolate* isolate,
return ReadOnlyRoots(isolate).undefined_value().ptr(); return ReadOnlyRoots(isolate).undefined_value().ptr();
} }
void MicrotaskQueue::EnqueueMicrotask(v8::Isolate* v8_isolate,
v8::Local<Function> function) {
Isolate* isolate = reinterpret_cast<Isolate*>(v8_isolate);
HandleScope scope(isolate);
Handle<CallableTask> microtask = isolate->factory()->NewCallableTask(
Utils::OpenHandle(*function), isolate->native_context());
EnqueueMicrotask(*microtask);
}
void MicrotaskQueue::EnqueueMicrotask(v8::Isolate* v8_isolate,
v8::MicrotaskCallback callback,
void* data) {
Isolate* isolate = reinterpret_cast<Isolate*>(v8_isolate);
HandleScope scope(isolate);
Handle<CallbackTask> microtask = isolate->factory()->NewCallbackTask(
isolate->factory()->NewForeign(reinterpret_cast<Address>(callback)),
isolate->factory()->NewForeign(reinterpret_cast<Address>(data)));
EnqueueMicrotask(*microtask);
}
void MicrotaskQueue::EnqueueMicrotask(Microtask microtask) { void MicrotaskQueue::EnqueueMicrotask(Microtask microtask) {
if (size_ == capacity_) { if (size_ == capacity_) {
// Keep the capacity of |ring_buffer_| power of 2, so that the JIT // Keep the capacity of |ring_buffer_| power of 2, so that the JIT
...@@ -90,6 +110,14 @@ void MicrotaskQueue::EnqueueMicrotask(Microtask microtask) { ...@@ -90,6 +110,14 @@ void MicrotaskQueue::EnqueueMicrotask(Microtask microtask) {
++size_; ++size_;
} }
void MicrotaskQueue::PerformCheckpoint(v8::Isolate* v8_isolate) {
if (!IsRunningMicrotasks() && !GetMicrotasksScopeDepth() &&
!HasMicrotasksSuppressions()) {
Isolate* isolate = reinterpret_cast<Isolate*>(v8_isolate);
RunMicrotasks(isolate);
}
}
namespace { namespace {
class SetIsRunningMicrotasks { class SetIsRunningMicrotasks {
...@@ -184,26 +212,29 @@ void MicrotaskQueue::IterateMicrotasks(RootVisitor* visitor) { ...@@ -184,26 +212,29 @@ void MicrotaskQueue::IterateMicrotasks(RootVisitor* visitor) {
} }
void MicrotaskQueue::AddMicrotasksCompletedCallback( void MicrotaskQueue::AddMicrotasksCompletedCallback(
MicrotasksCompletedCallback callback) { MicrotasksCompletedCallbackWithData callback, void* data) {
auto pos = std::find(microtasks_completed_callbacks_.begin(), CallbackWithData callback_with_data(callback, data);
microtasks_completed_callbacks_.end(), callback); auto pos =
std::find(microtasks_completed_callbacks_.begin(),
microtasks_completed_callbacks_.end(), callback_with_data);
if (pos != microtasks_completed_callbacks_.end()) return; if (pos != microtasks_completed_callbacks_.end()) return;
microtasks_completed_callbacks_.push_back(callback); microtasks_completed_callbacks_.push_back(callback_with_data);
} }
void MicrotaskQueue::RemoveMicrotasksCompletedCallback( void MicrotaskQueue::RemoveMicrotasksCompletedCallback(
MicrotasksCompletedCallback callback) { MicrotasksCompletedCallbackWithData callback, void* data) {
auto pos = std::find(microtasks_completed_callbacks_.begin(), CallbackWithData callback_with_data(callback, data);
microtasks_completed_callbacks_.end(), callback); auto pos =
std::find(microtasks_completed_callbacks_.begin(),
microtasks_completed_callbacks_.end(), callback_with_data);
if (pos == microtasks_completed_callbacks_.end()) return; if (pos == microtasks_completed_callbacks_.end()) return;
microtasks_completed_callbacks_.erase(pos); microtasks_completed_callbacks_.erase(pos);
} }
void MicrotaskQueue::FireMicrotasksCompletedCallback(Isolate* isolate) const { void MicrotaskQueue::FireMicrotasksCompletedCallback(Isolate* isolate) const {
std::vector<MicrotasksCompletedCallback> callbacks( std::vector<CallbackWithData> callbacks(microtasks_completed_callbacks_);
microtasks_completed_callbacks_);
for (auto& callback : callbacks) { for (auto& callback : callbacks) {
callback(reinterpret_cast<v8::Isolate*>(isolate)); callback.first(reinterpret_cast<v8::Isolate*>(isolate), callback.second);
} }
} }
......
...@@ -21,7 +21,7 @@ class Microtask; ...@@ -21,7 +21,7 @@ class Microtask;
class Object; class Object;
class RootVisitor; class RootVisitor;
class V8_EXPORT_PRIVATE MicrotaskQueue { class V8_EXPORT_PRIVATE MicrotaskQueue final : public v8::MicrotaskQueue {
public: public:
static void SetUpDefaultMicrotaskQueue(Isolate* isolate); static void SetUpDefaultMicrotaskQueue(Isolate* isolate);
static std::unique_ptr<MicrotaskQueue> New(Isolate* isolate); static std::unique_ptr<MicrotaskQueue> New(Isolate* isolate);
...@@ -35,7 +35,19 @@ class V8_EXPORT_PRIVATE MicrotaskQueue { ...@@ -35,7 +35,19 @@ class V8_EXPORT_PRIVATE MicrotaskQueue {
intptr_t microtask_queue_pointer, intptr_t microtask_queue_pointer,
Address raw_microtask); Address raw_microtask);
// v8::MicrotaskQueue implementations.
void EnqueueMicrotask(v8::Isolate* isolate,
v8::Local<Function> microtask) override;
void EnqueueMicrotask(v8::Isolate* isolate, v8::MicrotaskCallback callback,
void* data) override;
void PerformCheckpoint(v8::Isolate* isolate) override;
void EnqueueMicrotask(Microtask microtask); void EnqueueMicrotask(Microtask microtask);
void AddMicrotasksCompletedCallback(
MicrotasksCompletedCallbackWithData callback, void* data) override;
void RemoveMicrotasksCompletedCallback(
MicrotasksCompletedCallbackWithData callback, void* data) override;
bool IsRunningMicrotasks() const override { return is_running_microtasks_; }
// Runs all queued Microtasks. // Runs all queued Microtasks.
// Returns -1 if the execution is terminating, otherwise, returns the number // Returns -1 if the execution is terminating, otherwise, returns the number
...@@ -78,7 +90,6 @@ class V8_EXPORT_PRIVATE MicrotaskQueue { ...@@ -78,7 +90,6 @@ class V8_EXPORT_PRIVATE MicrotaskQueue {
void AddMicrotasksCompletedCallback(MicrotasksCompletedCallback callback); void AddMicrotasksCompletedCallback(MicrotasksCompletedCallback callback);
void RemoveMicrotasksCompletedCallback(MicrotasksCompletedCallback callback); void RemoveMicrotasksCompletedCallback(MicrotasksCompletedCallback callback);
void FireMicrotasksCompletedCallback(Isolate* isolate) const; void FireMicrotasksCompletedCallback(Isolate* isolate) const;
bool IsRunningMicrotasks() const { return is_running_microtasks_; }
intptr_t capacity() const { return capacity_; } intptr_t capacity() const { return capacity_; }
intptr_t size() const { return size_; } intptr_t size() const { return size_; }
...@@ -128,7 +139,9 @@ class V8_EXPORT_PRIVATE MicrotaskQueue { ...@@ -128,7 +139,9 @@ class V8_EXPORT_PRIVATE MicrotaskQueue {
v8::MicrotasksPolicy microtasks_policy_ = v8::MicrotasksPolicy::kAuto; v8::MicrotasksPolicy microtasks_policy_ = v8::MicrotasksPolicy::kAuto;
bool is_running_microtasks_ = false; bool is_running_microtasks_ = false;
std::vector<MicrotasksCompletedCallback> microtasks_completed_callbacks_; using CallbackWithData =
std::pair<MicrotasksCompletedCallbackWithData, void*>;
std::vector<CallbackWithData> microtasks_completed_callbacks_;
}; };
} // namespace internal } // namespace internal
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment