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: Benedikt Meurer <bmeurer@chromium.org>
Reviewed-by: Yang Guo <yangguo@chromium.org>
Reviewed-by: Adam Klein <adamk@chromium.org>
Cr-Commit-Position: refs/heads/master@{#59933}
This commit is contained in:
tzik 2019-02-28 15:54:55 +09:00 committed by Commit Bot
parent b2f8280e26
commit cce33f3752
6 changed files with 216 additions and 67 deletions

View File

@ -54,6 +54,7 @@ class Integer;
class Isolate;
template <class T>
class Maybe;
class MicrotaskQueue;
class Name;
class Number;
class NumberObject;
@ -6736,6 +6737,7 @@ typedef void (*PromiseRejectCallback)(PromiseRejectMessage message);
// --- Microtasks Callbacks ---
typedef void (*MicrotasksCompletedCallback)(Isolate*);
typedef void (*MicrotasksCompletedCallbackWithData)(Isolate*, void*);
typedef void (*MicrotaskCallback)(void* data);
@ -6748,6 +6750,80 @@ typedef void (*MicrotaskCallback)(void* data);
*/
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
@ -6763,6 +6839,7 @@ class V8_EXPORT MicrotasksScope {
enum Type { kRunMicrotasks, kDoNotRunMicrotasks };
MicrotasksScope(Isolate* isolate, Type type);
MicrotasksScope(Isolate* isolate, MicrotaskQueue* microtask_queue, Type type);
~MicrotasksScope();
/**
@ -7432,6 +7509,7 @@ class V8_EXPORT Isolate {
class V8_EXPORT SuppressMicrotaskExecutionScope {
public:
explicit SuppressMicrotaskExecutionScope(Isolate* isolate);
explicit SuppressMicrotaskExecutionScope(MicrotaskQueue* microtask_queue);
~SuppressMicrotaskExecutionScope();
// Prevent copying of Scope objects.
@ -8107,18 +8185,18 @@ class V8_EXPORT Isolate {
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.
*/
void RunMicrotasks();
/**
* Enqueues the callback to the Microtask Work Queue
* Enqueues the callback to the default MicrotaskQueue
*/
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);
@ -8134,14 +8212,15 @@ class V8_EXPORT Isolate {
/**
* Adds a callback to notify the host application after
* microtasks were run. The callback is triggered by explicit RunMicrotasks
* call or automatic microtasks execution (see SetAutorunMicrotasks).
* microtasks were run on the default MicrotaskQueue. The callback is
* triggered by explicit RunMicrotasks call or automatic microtasks execution
* (see SetMicrotaskPolicy).
*
* Callback will trigger even if microtasks were attempted to run,
* but the microtasks queue was empty and no single microtask was actually
* executed.
*
* Executing scriptsinside the callback will not re-trigger microtasks and
* Executing scripts inside the callback will not re-trigger microtasks and
* the callback.
*/
void AddMicrotasksCompletedCallback(MicrotasksCompletedCallback callback);
@ -9172,7 +9251,8 @@ class V8_EXPORT Context {
MaybeLocal<ObjectTemplate> global_template = MaybeLocal<ObjectTemplate>(),
MaybeLocal<Value> global_object = MaybeLocal<Value>(),
DeserializeInternalFieldsCallback internal_fields_deserializer =
DeserializeInternalFieldsCallback());
DeserializeInternalFieldsCallback(),
MicrotaskQueue* microtask_queue = nullptr);
/**
* Create a new context from a (non-default) context snapshot. There
@ -9192,13 +9272,13 @@ class V8_EXPORT Context {
*
* \param global_object See v8::Context::New.
*/
static MaybeLocal<Context> FromSnapshot(
Isolate* isolate, size_t context_snapshot_index,
DeserializeInternalFieldsCallback embedder_fields_deserializer =
DeserializeInternalFieldsCallback(),
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.

View File

@ -5880,10 +5880,11 @@ struct InvokeBootstrapper<i::Context> {
i::Isolate* isolate, i::MaybeHandle<i::JSGlobalProxy> maybe_global_proxy,
v8::Local<v8::ObjectTemplate> global_proxy_template,
v8::ExtensionConfiguration* extensions, size_t context_snapshot_index,
v8::DeserializeInternalFieldsCallback embedder_fields_deserializer) {
v8::DeserializeInternalFieldsCallback embedder_fields_deserializer,
v8::MicrotaskQueue* microtask_queue) {
return isolate->bootstrapper()->CreateEnvironment(
maybe_global_proxy, global_proxy_template, extensions,
context_snapshot_index, embedder_fields_deserializer);
context_snapshot_index, embedder_fields_deserializer, microtask_queue);
}
};
@ -5893,7 +5894,8 @@ struct InvokeBootstrapper<i::JSGlobalProxy> {
i::Isolate* isolate, i::MaybeHandle<i::JSGlobalProxy> maybe_global_proxy,
v8::Local<v8::ObjectTemplate> global_proxy_template,
v8::ExtensionConfiguration* extensions, size_t context_snapshot_index,
v8::DeserializeInternalFieldsCallback embedder_fields_deserializer) {
v8::DeserializeInternalFieldsCallback embedder_fields_deserializer,
v8::MicrotaskQueue* microtask_queue) {
USE(extensions);
USE(context_snapshot_index);
return isolate->bootstrapper()->NewRemoteContext(maybe_global_proxy,
@ -5906,7 +5908,8 @@ static i::Handle<ObjectType> CreateEnvironment(
i::Isolate* isolate, v8::ExtensionConfiguration* extensions,
v8::MaybeLocal<ObjectTemplate> maybe_global_template,
v8::MaybeLocal<Value> maybe_global_proxy, size_t context_snapshot_index,
v8::DeserializeInternalFieldsCallback embedder_fields_deserializer) {
v8::DeserializeInternalFieldsCallback embedder_fields_deserializer,
v8::MicrotaskQueue* microtask_queue) {
i::Handle<ObjectType> result;
{
@ -5982,9 +5985,9 @@ static i::Handle<ObjectType> CreateEnvironment(
}
// Create the environment.
InvokeBootstrapper<ObjectType> invoke;
result =
invoke.Invoke(isolate, maybe_proxy, proxy_template, extensions,
context_snapshot_index, embedder_fields_deserializer);
result = invoke.Invoke(isolate, maybe_proxy, proxy_template, extensions,
context_snapshot_index, embedder_fields_deserializer,
microtask_queue);
// Restore the access check info and interceptors on the global template.
if (!maybe_global_template.IsEmpty()) {
@ -6010,7 +6013,8 @@ Local<Context> NewContext(
v8::Isolate* external_isolate, v8::ExtensionConfiguration* extensions,
v8::MaybeLocal<ObjectTemplate> global_template,
v8::MaybeLocal<Value> global_object, size_t context_snapshot_index,
v8::DeserializeInternalFieldsCallback embedder_fields_deserializer) {
v8::DeserializeInternalFieldsCallback embedder_fields_deserializer,
v8::MicrotaskQueue* microtask_queue) {
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(external_isolate);
// TODO(jkummerow): This is for crbug.com/713699. Remove it if it doesn't
// fail.
@ -6024,7 +6028,7 @@ Local<Context> NewContext(
if (extensions == nullptr) extensions = &no_extensions;
i::Handle<i::Context> env = CreateEnvironment<i::Context>(
isolate, extensions, global_template, global_object,
context_snapshot_index, embedder_fields_deserializer);
context_snapshot_index, embedder_fields_deserializer, microtask_queue);
if (env.is_null()) {
if (isolate->has_pending_exception()) isolate->clear_pending_exception();
return Local<Context>();
@ -6036,15 +6040,18 @@ Local<Context> v8::Context::New(
v8::Isolate* external_isolate, v8::ExtensionConfiguration* extensions,
v8::MaybeLocal<ObjectTemplate> global_template,
v8::MaybeLocal<Value> global_object,
DeserializeInternalFieldsCallback internal_fields_deserializer) {
DeserializeInternalFieldsCallback internal_fields_deserializer,
v8::MicrotaskQueue* microtask_queue) {
return NewContext(external_isolate, extensions, global_template,
global_object, 0, internal_fields_deserializer);
global_object, 0, internal_fields_deserializer,
microtask_queue);
}
MaybeLocal<Context> v8::Context::FromSnapshot(
v8::Isolate* external_isolate, size_t context_snapshot_index,
v8::DeserializeInternalFieldsCallback embedder_fields_deserializer,
v8::ExtensionConfiguration* extensions, MaybeLocal<Value> global_object) {
v8::ExtensionConfiguration* extensions, MaybeLocal<Value> global_object,
v8::MicrotaskQueue* microtask_queue) {
size_t index_including_default_context = context_snapshot_index + 1;
if (!i::Snapshot::HasContextSnapshot(
reinterpret_cast<i::Isolate*>(external_isolate),
@ -6053,7 +6060,7 @@ MaybeLocal<Context> v8::Context::FromSnapshot(
}
return NewContext(external_isolate, extensions, MaybeLocal<ObjectTemplate>(),
global_object, index_including_default_context,
embedder_fields_deserializer);
embedder_fields_deserializer, microtask_queue);
}
MaybeLocal<Object> v8::Context::NewRemoteContext(
@ -6074,9 +6081,9 @@ MaybeLocal<Object> v8::Context::NewRemoteContext(
"v8::Context::NewRemoteContext",
"Global template needs to have access check handlers.");
i::Handle<i::JSGlobalProxy> global_proxy =
CreateEnvironment<i::JSGlobalProxy>(isolate, nullptr, global_template,
global_object, 0,
DeserializeInternalFieldsCallback());
CreateEnvironment<i::JSGlobalProxy>(
isolate, nullptr, global_template, global_object, 0,
DeserializeInternalFieldsCallback(), nullptr);
if (global_proxy.is_null()) {
if (isolate->has_pending_exception()) isolate->clear_pending_exception();
return MaybeLocal<Object>();
@ -8546,15 +8553,11 @@ void Isolate::RunMicrotasks() {
void Isolate::EnqueueMicrotask(Local<Function> v8_function) {
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(this);
i::Handle<i::JSReceiver> function = Utils::OpenHandle(*v8_function);
i::Handle<i::NativeContext> handler_context;
if (!i::JSReceiver::GetContextForMicrotask(function).ToHandle(
&handler_context))
handler_context = isolate->native_context();
i::Handle<i::CallableTask> microtask =
isolate->factory()->NewCallableTask(function, handler_context);
handler_context->microtask_queue()->EnqueueMicrotask(*microtask);
handler_context->microtask_queue()->EnqueueMicrotask(this, v8_function);
}
void Isolate::EnqueueMicrotask(MicrotaskCallback callback, void* data) {
@ -8579,12 +8582,21 @@ MicrotasksPolicy Isolate::GetMicrotasksPolicy() const {
return isolate->default_microtask_queue()->microtasks_policy();
}
namespace {
void MicrotasksCompletedCallbackAdapter(v8::Isolate* isolate, void* data) {
auto callback = reinterpret_cast<MicrotasksCompletedCallback>(data);
callback(isolate);
}
} // namespace
void Isolate::AddMicrotasksCompletedCallback(
MicrotasksCompletedCallback callback) {
DCHECK(callback);
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(this);
isolate->default_microtask_queue()->AddMicrotasksCompletedCallback(callback);
isolate->default_microtask_queue()->AddMicrotasksCompletedCallback(
&MicrotasksCompletedCallbackAdapter, reinterpret_cast<void*>(callback));
}
@ -8592,7 +8604,7 @@ void Isolate::RemoveMicrotasksCompletedCallback(
MicrotasksCompletedCallback callback) {
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(this);
isolate->default_microtask_queue()->RemoveMicrotasksCompletedCallback(
callback);
&MicrotasksCompletedCallbackAdapter, reinterpret_cast<void*>(callback));
}
@ -8892,8 +8904,16 @@ void Isolate::SetAllowAtomicsWait(bool allow) {
}
MicrotasksScope::MicrotasksScope(Isolate* isolate, MicrotasksScope::Type type)
: MicrotasksScope(
isolate,
reinterpret_cast<i::Isolate*>(isolate)->default_microtask_queue(),
type) {}
MicrotasksScope::MicrotasksScope(Isolate* isolate,
MicrotaskQueue* microtask_queue,
MicrotasksScope::Type type)
: isolate_(reinterpret_cast<i::Isolate*>(isolate)),
microtask_queue_(isolate_->default_microtask_queue()),
microtask_queue_(static_cast<i::MicrotaskQueue*>(microtask_queue)),
run_(type == MicrotasksScope::kRunMicrotasks) {
if (run_) microtask_queue_->IncrementMicrotasksScopeDepth();
#ifdef DEBUG
@ -8913,25 +8933,22 @@ MicrotasksScope::~MicrotasksScope() {
#endif
}
void MicrotasksScope::PerformCheckpoint(Isolate* v8Isolate) {
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(v8Isolate);
void MicrotasksScope::PerformCheckpoint(Isolate* v8_isolate) {
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(v8_isolate);
auto* microtask_queue = isolate->default_microtask_queue();
if (!microtask_queue->GetMicrotasksScopeDepth() &&
!microtask_queue->HasMicrotasksSuppressions()) {
microtask_queue->RunMicrotasks(isolate);
}
microtask_queue->PerformCheckpoint(v8_isolate);
}
int MicrotasksScope::GetCurrentDepth(Isolate* v8Isolate) {
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(v8Isolate);
return isolate->default_microtask_queue()->GetMicrotasksScopeDepth();
int MicrotasksScope::GetCurrentDepth(Isolate* v8_isolate) {
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(v8_isolate);
auto* microtask_queue = isolate->default_microtask_queue();
return microtask_queue->GetMicrotasksScopeDepth();
}
bool MicrotasksScope::IsRunningMicrotasks(Isolate* v8Isolate) {
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(v8Isolate);
return isolate->default_microtask_queue()->IsRunningMicrotasks();
bool MicrotasksScope::IsRunningMicrotasks(Isolate* v8_isolate) {
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(v8_isolate);
auto* microtask_queue = isolate->default_microtask_queue();
return microtask_queue->IsRunningMicrotasks();
}
String::Utf8Value::Utf8Value(v8::Isolate* isolate, v8::Local<v8::Value> obj)

View File

@ -21,6 +21,7 @@
#include "src/heap/heap-inl.h"
#include "src/isolate-inl.h"
#include "src/math-random.h"
#include "src/microtask-queue.h"
#include "src/objects/api-callbacks.h"
#include "src/objects/arguments.h"
#include "src/objects/builtin-function-id.h"
@ -139,7 +140,8 @@ class Genesis {
Genesis(Isolate* isolate, MaybeHandle<JSGlobalProxy> maybe_global_proxy,
v8::Local<v8::ObjectTemplate> global_proxy_template,
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,
v8::Local<v8::ObjectTemplate> global_proxy_template);
~Genesis() = default;
@ -302,12 +304,14 @@ Handle<Context> Bootstrapper::CreateEnvironment(
MaybeHandle<JSGlobalProxy> maybe_global_proxy,
v8::Local<v8::ObjectTemplate> global_proxy_template,
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_);
Handle<Context> env;
{
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();
if (env.is_null() || !InstallExtensions(env, extensions)) {
return Handle<Context>();
@ -5504,7 +5508,8 @@ Genesis::Genesis(
Isolate* isolate, MaybeHandle<JSGlobalProxy> maybe_global_proxy,
v8::Local<v8::ObjectTemplate> global_proxy_template,
size_t context_snapshot_index,
v8::DeserializeEmbedderFieldsCallback embedder_fields_deserializer)
v8::DeserializeEmbedderFieldsCallback embedder_fields_deserializer,
v8::MicrotaskQueue* microtask_queue)
: isolate_(isolate), active_(isolate->bootstrapper()) {
RuntimeCallTimerScope rcs_timer(isolate, RuntimeCallCounterId::kGenesis);
result_ = Handle<Context>::null();
@ -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
// snapshot as we should be able to turn them off at runtime. Re-installing

View File

@ -57,7 +57,8 @@ class Bootstrapper final {
MaybeHandle<JSGlobalProxy> maybe_global_proxy,
v8::Local<v8::ObjectTemplate> global_object_template,
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(
MaybeHandle<JSGlobalProxy> maybe_global_proxy,

View File

@ -7,7 +7,7 @@
#include <stddef.h>
#include <algorithm>
#include "src/api.h"
#include "src/api-inl.h"
#include "src/base/logging.h"
#include "src/handles-inl.h"
#include "src/isolate.h"
@ -77,6 +77,26 @@ Address MicrotaskQueue::CallEnqueueMicrotask(Isolate* isolate,
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) {
if (size_ == capacity_) {
// Keep the capacity of |ring_buffer_| power of 2, so that the JIT
@ -90,6 +110,14 @@ void MicrotaskQueue::EnqueueMicrotask(Microtask microtask) {
++size_;
}
void MicrotaskQueue::PerformCheckpoint(v8::Isolate* v8_isolate) {
if (!IsRunningMicrotasks() && !GetMicrotasksScopeDepth() &&
!HasMicrotasksSuppressions()) {
Isolate* isolate = reinterpret_cast<Isolate*>(v8_isolate);
RunMicrotasks(isolate);
}
}
namespace {
class SetIsRunningMicrotasks {
@ -184,26 +212,29 @@ void MicrotaskQueue::IterateMicrotasks(RootVisitor* visitor) {
}
void MicrotaskQueue::AddMicrotasksCompletedCallback(
MicrotasksCompletedCallback callback) {
auto pos = std::find(microtasks_completed_callbacks_.begin(),
microtasks_completed_callbacks_.end(), callback);
MicrotasksCompletedCallbackWithData callback, void* data) {
CallbackWithData callback_with_data(callback, data);
auto pos =
std::find(microtasks_completed_callbacks_.begin(),
microtasks_completed_callbacks_.end(), callback_with_data);
if (pos != microtasks_completed_callbacks_.end()) return;
microtasks_completed_callbacks_.push_back(callback);
microtasks_completed_callbacks_.push_back(callback_with_data);
}
void MicrotaskQueue::RemoveMicrotasksCompletedCallback(
MicrotasksCompletedCallback callback) {
auto pos = std::find(microtasks_completed_callbacks_.begin(),
microtasks_completed_callbacks_.end(), callback);
MicrotasksCompletedCallbackWithData callback, void* data) {
CallbackWithData callback_with_data(callback, data);
auto pos =
std::find(microtasks_completed_callbacks_.begin(),
microtasks_completed_callbacks_.end(), callback_with_data);
if (pos == microtasks_completed_callbacks_.end()) return;
microtasks_completed_callbacks_.erase(pos);
}
void MicrotaskQueue::FireMicrotasksCompletedCallback(Isolate* isolate) const {
std::vector<MicrotasksCompletedCallback> callbacks(
microtasks_completed_callbacks_);
std::vector<CallbackWithData> callbacks(microtasks_completed_callbacks_);
for (auto& callback : callbacks) {
callback(reinterpret_cast<v8::Isolate*>(isolate));
callback.first(reinterpret_cast<v8::Isolate*>(isolate), callback.second);
}
}

View File

@ -21,7 +21,7 @@ class Microtask;
class Object;
class RootVisitor;
class V8_EXPORT_PRIVATE MicrotaskQueue {
class V8_EXPORT_PRIVATE MicrotaskQueue final : public v8::MicrotaskQueue {
public:
static void SetUpDefaultMicrotaskQueue(Isolate* isolate);
static std::unique_ptr<MicrotaskQueue> New(Isolate* isolate);
@ -35,7 +35,19 @@ class V8_EXPORT_PRIVATE MicrotaskQueue {
intptr_t microtask_queue_pointer,
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 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.
// Returns -1 if the execution is terminating, otherwise, returns the number
@ -78,7 +90,6 @@ class V8_EXPORT_PRIVATE MicrotaskQueue {
void AddMicrotasksCompletedCallback(MicrotasksCompletedCallback callback);
void RemoveMicrotasksCompletedCallback(MicrotasksCompletedCallback callback);
void FireMicrotasksCompletedCallback(Isolate* isolate) const;
bool IsRunningMicrotasks() const { return is_running_microtasks_; }
intptr_t capacity() const { return capacity_; }
intptr_t size() const { return size_; }
@ -128,7 +139,9 @@ class V8_EXPORT_PRIVATE MicrotaskQueue {
v8::MicrotasksPolicy microtasks_policy_ = v8::MicrotasksPolicy::kAuto;
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