Commit 13d2f365 authored by adamk@chromium.org's avatar adamk@chromium.org

Add API support for passing a C++ function as a microtask callback

This allows embedders to enqueue microtasks without having any v8::Context
handy, as happens in Blink in some cases (such as DOM mutations due to editing
triggering MutationObservers).

LOG=Y
R=dcarney@chromium.org

Review URL: https://codereview.chromium.org/306053003

git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@21658 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent bd99223c
......@@ -3966,6 +3966,9 @@ typedef void (*MemoryAllocationCallback)(ObjectSpace space,
// --- Leave Script Callback ---
typedef void (*CallCompletedCallback)();
// --- Microtask Callback ---
typedef void (*MicrotaskCallback)(void* data);
// --- Failed Access Check Callback ---
typedef void (*FailedAccessCheckCallback)(Local<Object> target,
AccessType type,
......@@ -4385,6 +4388,11 @@ class V8_EXPORT Isolate {
*/
void EnqueueMicrotask(Handle<Function> microtask);
/**
* Experimental: Enqueues the callback to the Microtask Work Queue
*/
void EnqueueMicrotask(MicrotaskCallback microtask, void* data = NULL);
/**
* Experimental: Controls whether the Microtask Work Queue is automatically
* run when the script call depth decrements to zero.
......
......@@ -6679,6 +6679,18 @@ void Isolate::EnqueueMicrotask(Handle<Function> microtask) {
}
void Isolate::EnqueueMicrotask(MicrotaskCallback microtask, void* data) {
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(this);
i::HandleScope scope(isolate);
i::Handle<i::CallHandlerInfo> callback_info =
i::Handle<i::CallHandlerInfo>::cast(
isolate->factory()->NewStruct(i::CALL_HANDLER_INFO_TYPE));
SET_FIELD_WRAPPED(callback_info, set_callback, microtask);
SET_FIELD_WRAPPED(callback_info, set_data, data);
isolate->EnqueueMicrotask(callback_info);
}
void Isolate::SetAutorunMicrotasks(bool autorun) {
reinterpret_cast<i::Isolate*>(this)->set_autorun_microtasks(autorun);
}
......
......@@ -2262,7 +2262,8 @@ void Isolate::FireCallCompletedCallback() {
}
void Isolate::EnqueueMicrotask(Handle<JSFunction> microtask) {
void Isolate::EnqueueMicrotask(Handle<Object> microtask) {
ASSERT(microtask->IsJSFunction() || microtask->IsCallHandlerInfo());
Handle<FixedArray> queue(heap()->microtask_queue(), this);
int num_tasks = pending_microtask_count();
ASSERT(num_tasks <= queue->length());
......@@ -2301,18 +2302,30 @@ void Isolate::RunMicrotasks() {
for (int i = 0; i < num_tasks; i++) {
HandleScope scope(this);
Handle<JSFunction> microtask(JSFunction::cast(queue->get(i)), this);
Handle<Object> exception;
MaybeHandle<Object> result = Execution::TryCall(
microtask, factory()->undefined_value(), 0, NULL, &exception);
// If execution is terminating, just bail out.
if (result.is_null() &&
!exception.is_null() &&
*exception == heap()->termination_exception()) {
// Clear out any remaining callbacks in the queue.
heap()->set_microtask_queue(heap()->empty_fixed_array());
set_pending_microtask_count(0);
return;
Handle<Object> microtask(queue->get(i), this);
if (microtask->IsJSFunction()) {
Handle<JSFunction> microtask_function =
Handle<JSFunction>::cast(microtask);
Handle<Object> exception;
MaybeHandle<Object> result = Execution::TryCall(
microtask_function, factory()->undefined_value(),
0, NULL, &exception);
// If execution is terminating, just bail out.
if (result.is_null() &&
!exception.is_null() &&
*exception == heap()->termination_exception()) {
// Clear out any remaining callbacks in the queue.
heap()->set_microtask_queue(heap()->empty_fixed_array());
set_pending_microtask_count(0);
return;
}
} else {
Handle<CallHandlerInfo> callback_info =
Handle<CallHandlerInfo>::cast(microtask);
v8::MicrotaskCallback callback =
v8::ToCData<v8::MicrotaskCallback>(callback_info->callback());
void* data = v8::ToCData<void*>(callback_info->data());
callback(data);
}
}
}
......
......@@ -1077,7 +1077,7 @@ class Isolate {
void RemoveCallCompletedCallback(CallCompletedCallback callback);
void FireCallCompletedCallback();
void EnqueueMicrotask(Handle<JSFunction> microtask);
void EnqueueMicrotask(Handle<Object> microtask);
void RunMicrotasks();
private:
......
......@@ -20689,6 +20689,14 @@ static void MicrotaskTwo(const v8::FunctionCallbackInfo<Value>& info) {
}
void* g_passed_to_three = NULL;
static void MicrotaskThree(void* data) {
g_passed_to_three = data;
}
TEST(EnqueueMicrotask) {
LocalContext env;
v8::HandleScope scope(env->GetIsolate());
......@@ -20722,6 +20730,25 @@ TEST(EnqueueMicrotask) {
CompileRun("1+1;");
CHECK_EQ(2, CompileRun("ext1Calls")->Int32Value());
CHECK_EQ(2, CompileRun("ext2Calls")->Int32Value());
g_passed_to_three = NULL;
env->GetIsolate()->EnqueueMicrotask(MicrotaskThree);
CompileRun("1+1;");
CHECK_EQ(NULL, g_passed_to_three);
CHECK_EQ(2, CompileRun("ext1Calls")->Int32Value());
CHECK_EQ(2, CompileRun("ext2Calls")->Int32Value());
int dummy;
env->GetIsolate()->EnqueueMicrotask(
Function::New(env->GetIsolate(), MicrotaskOne));
env->GetIsolate()->EnqueueMicrotask(MicrotaskThree, &dummy);
env->GetIsolate()->EnqueueMicrotask(
Function::New(env->GetIsolate(), MicrotaskTwo));
CompileRun("1+1;");
CHECK_EQ(&dummy, g_passed_to_three);
CHECK_EQ(3, CompileRun("ext1Calls")->Int32Value());
CHECK_EQ(3, CompileRun("ext2Calls")->Int32Value());
g_passed_to_three = NULL;
}
......
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