Move microtask queueing logic from JavaScript to C++
This avoids the appearence of a leak due to storing a JSObject as the microtask_state in the strong root list, and allows callers to call Isolate::RunMicrotasks() without having any v8::Context available (as at least Blink has interest in doing). The queue is now a strong root, represented as a FixedArray of JSFunctions (or empty_fixed_array, if it's empty); it doubles in size when it needs to grow. The number of elements in the queue is stored in Isolate::pending_microtask_count(). LOG=Y R=dcarney@chromium.org Review URL: https://codereview.chromium.org/290633010 git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@21356 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
ec23d0b815
commit
35b8b0b27a
@ -6626,16 +6626,13 @@ void Isolate::RemoveCallCompletedCallback(CallCompletedCallback callback) {
|
||||
|
||||
|
||||
void Isolate::RunMicrotasks() {
|
||||
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(this);
|
||||
i::HandleScope scope(i_isolate);
|
||||
i_isolate->RunMicrotasks();
|
||||
reinterpret_cast<i::Isolate*>(this)->RunMicrotasks();
|
||||
}
|
||||
|
||||
|
||||
void Isolate::EnqueueMicrotask(Handle<Function> microtask) {
|
||||
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(this);
|
||||
ENTER_V8(i_isolate);
|
||||
i::Execution::EnqueueMicrotask(i_isolate, Utils::OpenHandle(*microtask));
|
||||
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(this);
|
||||
isolate->EnqueueMicrotask(Utils::OpenHandle(*microtask));
|
||||
}
|
||||
|
||||
|
||||
|
@ -1550,9 +1550,6 @@ void Genesis::InstallNativeFunctions() {
|
||||
|
||||
|
||||
void Genesis::InstallExperimentalNativeFunctions() {
|
||||
INSTALL_NATIVE(JSFunction, "RunMicrotasksJS", run_microtasks);
|
||||
INSTALL_NATIVE(JSFunction, "EnqueueMicrotask", enqueue_microtask);
|
||||
|
||||
if (FLAG_harmony_proxies) {
|
||||
INSTALL_NATIVE(JSFunction, "DerivedHasTrap", derived_has_trap);
|
||||
INSTALL_NATIVE(JSFunction, "DerivedGetTrap", derived_get_trap);
|
||||
|
@ -156,8 +156,6 @@ enum BindingFlags {
|
||||
V(ALLOW_CODE_GEN_FROM_STRINGS_INDEX, Object, allow_code_gen_from_strings) \
|
||||
V(ERROR_MESSAGE_FOR_CODE_GEN_FROM_STRINGS_INDEX, Object, \
|
||||
error_message_for_code_gen_from_strings) \
|
||||
V(RUN_MICROTASKS_INDEX, JSFunction, run_microtasks) \
|
||||
V(ENQUEUE_MICROTASK_INDEX, JSFunction, enqueue_microtask) \
|
||||
V(IS_PROMISE_INDEX, JSFunction, is_promise) \
|
||||
V(PROMISE_CREATE_INDEX, JSFunction, promise_create) \
|
||||
V(PROMISE_RESOLVE_INDEX, JSFunction, promise_resolve) \
|
||||
|
@ -307,28 +307,6 @@ MaybeHandle<Object> Execution::TryGetConstructorDelegate(
|
||||
}
|
||||
|
||||
|
||||
void Execution::RunMicrotasks(Isolate* isolate) {
|
||||
ASSERT(isolate->microtask_pending());
|
||||
Execution::Call(
|
||||
isolate,
|
||||
isolate->run_microtasks(),
|
||||
isolate->factory()->undefined_value(),
|
||||
0,
|
||||
NULL).Check();
|
||||
}
|
||||
|
||||
|
||||
void Execution::EnqueueMicrotask(Isolate* isolate, Handle<Object> microtask) {
|
||||
Handle<Object> args[] = { microtask };
|
||||
Execution::Call(
|
||||
isolate,
|
||||
isolate->enqueue_microtask(),
|
||||
isolate->factory()->undefined_value(),
|
||||
1,
|
||||
args).Check();
|
||||
}
|
||||
|
||||
|
||||
bool StackGuard::IsStackOverflow() {
|
||||
ExecutionAccess access(isolate_);
|
||||
return (thread_local_.jslimit_ != kInterruptLimit &&
|
||||
|
@ -121,9 +121,6 @@ class Execution V8_FINAL : public AllStatic {
|
||||
Handle<Object> object);
|
||||
static MaybeHandle<Object> TryGetConstructorDelegate(Isolate* isolate,
|
||||
Handle<Object> object);
|
||||
|
||||
static void RunMicrotasks(Isolate* isolate);
|
||||
static void EnqueueMicrotask(Isolate* isolate, Handle<Object> microtask);
|
||||
};
|
||||
|
||||
|
||||
|
@ -2897,9 +2897,9 @@ void Heap::CreateInitialObjects() {
|
||||
set_observation_state(*factory->NewJSObjectFromMap(
|
||||
factory->NewMap(JS_OBJECT_TYPE, JSObject::kHeaderSize)));
|
||||
|
||||
// Allocate object to hold object microtask state.
|
||||
set_microtask_state(*factory->NewJSObjectFromMap(
|
||||
factory->NewMap(JS_OBJECT_TYPE, JSObject::kHeaderSize)));
|
||||
// Microtask queue uses the empty fixed array as a sentinel for "empty".
|
||||
// Number of queued microtasks stored in Isolate::pending_microtask_count().
|
||||
set_microtask_queue(empty_fixed_array());
|
||||
|
||||
set_frozen_symbol(*factory->NewPrivateSymbol());
|
||||
set_nonexistent_symbol(*factory->NewPrivateSymbol());
|
||||
|
@ -196,7 +196,7 @@ namespace internal {
|
||||
V(Symbol, megamorphic_symbol, MegamorphicSymbol) \
|
||||
V(FixedArray, materialized_objects, MaterializedObjects) \
|
||||
V(FixedArray, allocation_sites_scratchpad, AllocationSitesScratchpad) \
|
||||
V(JSObject, microtask_state, MicrotaskState)
|
||||
V(FixedArray, microtask_queue, MicrotaskQueue)
|
||||
|
||||
// Entries in this list are limited to Smis and are not visited during GC.
|
||||
#define SMI_ROOT_LIST(V) \
|
||||
|
@ -2232,13 +2232,13 @@ void Isolate::RemoveCallCompletedCallback(CallCompletedCallback callback) {
|
||||
|
||||
void Isolate::FireCallCompletedCallback() {
|
||||
bool has_call_completed_callbacks = !call_completed_callbacks_.is_empty();
|
||||
bool run_microtasks = autorun_microtasks() && microtask_pending();
|
||||
bool run_microtasks = autorun_microtasks() && pending_microtask_count();
|
||||
if (!has_call_completed_callbacks && !run_microtasks) return;
|
||||
|
||||
if (!handle_scope_implementer()->CallDepthIsZero()) return;
|
||||
if (run_microtasks) RunMicrotasks();
|
||||
// Fire callbacks. Increase call depth to prevent recursive callbacks.
|
||||
handle_scope_implementer()->IncrementCallDepth();
|
||||
if (run_microtasks) Execution::RunMicrotasks(this);
|
||||
for (int i = 0; i < call_completed_callbacks_.length(); i++) {
|
||||
call_completed_callbacks_.at(i)();
|
||||
}
|
||||
@ -2246,15 +2246,46 @@ void Isolate::FireCallCompletedCallback() {
|
||||
}
|
||||
|
||||
|
||||
void Isolate::RunMicrotasks() {
|
||||
if (!microtask_pending())
|
||||
return;
|
||||
void Isolate::EnqueueMicrotask(Handle<JSFunction> microtask) {
|
||||
Handle<FixedArray> queue(heap()->microtask_queue(), this);
|
||||
int num_tasks = pending_microtask_count();
|
||||
ASSERT(num_tasks <= queue->length());
|
||||
if (num_tasks == 0) {
|
||||
queue = factory()->NewFixedArray(8);
|
||||
heap()->set_microtask_queue(*queue);
|
||||
} else if (num_tasks == queue->length()) {
|
||||
queue = FixedArray::CopySize(queue, num_tasks * 2);
|
||||
heap()->set_microtask_queue(*queue);
|
||||
}
|
||||
ASSERT(queue->get(num_tasks)->IsUndefined());
|
||||
queue->set(num_tasks, *microtask);
|
||||
set_pending_microtask_count(num_tasks + 1);
|
||||
}
|
||||
|
||||
|
||||
void Isolate::RunMicrotasks() {
|
||||
ASSERT(handle_scope_implementer()->CallDepthIsZero());
|
||||
|
||||
// Increase call depth to prevent recursive callbacks.
|
||||
handle_scope_implementer()->IncrementCallDepth();
|
||||
Execution::RunMicrotasks(this);
|
||||
|
||||
while (pending_microtask_count() > 0) {
|
||||
HandleScope scope(this);
|
||||
int num_tasks = pending_microtask_count();
|
||||
Handle<FixedArray> queue(heap()->microtask_queue(), this);
|
||||
ASSERT(num_tasks <= queue->length());
|
||||
set_pending_microtask_count(0);
|
||||
heap()->set_microtask_queue(heap()->empty_fixed_array());
|
||||
|
||||
for (int i = 0; i < num_tasks; i++) {
|
||||
HandleScope scope(this);
|
||||
Handle<JSFunction> microtask(JSFunction::cast(queue->get(i)), this);
|
||||
// TODO(adamk): This should ignore/clear exceptions instead of Checking.
|
||||
Execution::Call(this, microtask, factory()->undefined_value(),
|
||||
0, NULL).Check();
|
||||
}
|
||||
}
|
||||
|
||||
handle_scope_implementer()->DecrementCallDepth();
|
||||
}
|
||||
|
||||
|
@ -349,7 +349,7 @@ typedef List<HeapObject*> DebugObjectCache;
|
||||
/* AstNode state. */ \
|
||||
V(int, ast_node_id, 0) \
|
||||
V(unsigned, ast_node_count, 0) \
|
||||
V(bool, microtask_pending, false) \
|
||||
V(int, pending_microtask_count, 0) \
|
||||
V(bool, autorun_microtasks, true) \
|
||||
V(HStatistics*, hstatistics, NULL) \
|
||||
V(HTracer*, htracer, NULL) \
|
||||
@ -1067,6 +1067,7 @@ class Isolate {
|
||||
void RemoveCallCompletedCallback(CallCompletedCallback callback);
|
||||
void FireCallCompletedCallback();
|
||||
|
||||
void EnqueueMicrotask(Handle<JSFunction> microtask);
|
||||
void RunMicrotasks();
|
||||
|
||||
private:
|
||||
|
@ -419,8 +419,8 @@ function ObserverEnqueueIfActive(observer, objectInfo, changeRecord) {
|
||||
|
||||
var callbackInfo = CallbackInfoNormalize(callback);
|
||||
if (IS_NULL(GetPendingObservers())) {
|
||||
SetPendingObservers(nullProtoObject())
|
||||
EnqueueMicrotask(ObserveMicrotaskRunner);
|
||||
SetPendingObservers(nullProtoObject());
|
||||
%EnqueueMicrotask(ObserveMicrotaskRunner);
|
||||
}
|
||||
GetPendingObservers()[callbackInfo.priority] = callback;
|
||||
callbackInfo.push(changeRecord);
|
||||
|
@ -151,7 +151,7 @@ function PromiseCatch(onReject) {
|
||||
}
|
||||
|
||||
function PromiseEnqueue(value, tasks) {
|
||||
EnqueueMicrotask(function() {
|
||||
%EnqueueMicrotask(function() {
|
||||
for (var i = 0; i < tasks.length; i += 2) {
|
||||
PromiseHandle(value, tasks[i], tasks[i + 1])
|
||||
}
|
||||
|
@ -14880,31 +14880,23 @@ RUNTIME_FUNCTION(Runtime_SetIsObserved) {
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_SetMicrotaskPending) {
|
||||
SealHandleScope shs(isolate);
|
||||
RUNTIME_FUNCTION(Runtime_EnqueueMicrotask) {
|
||||
HandleScope scope(isolate);
|
||||
ASSERT(args.length() == 1);
|
||||
CONVERT_BOOLEAN_ARG_CHECKED(new_state, 0);
|
||||
bool old_state = isolate->microtask_pending();
|
||||
isolate->set_microtask_pending(new_state);
|
||||
return isolate->heap()->ToBoolean(old_state);
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSFunction, microtask, 0);
|
||||
isolate->EnqueueMicrotask(microtask);
|
||||
return isolate->heap()->undefined_value();
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_RunMicrotasks) {
|
||||
HandleScope scope(isolate);
|
||||
ASSERT(args.length() == 0);
|
||||
if (isolate->microtask_pending()) Execution::RunMicrotasks(isolate);
|
||||
isolate->RunMicrotasks();
|
||||
return isolate->heap()->undefined_value();
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_GetMicrotaskState) {
|
||||
SealHandleScope shs(isolate);
|
||||
ASSERT(args.length() == 0);
|
||||
return isolate->heap()->microtask_state();
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_GetObservationState) {
|
||||
SealHandleScope shs(isolate);
|
||||
ASSERT(args.length() == 0);
|
||||
|
@ -244,9 +244,6 @@ namespace internal {
|
||||
/* ES5 */ \
|
||||
F(ObjectFreeze, 1, 1) \
|
||||
\
|
||||
/* Harmony microtasks */ \
|
||||
F(GetMicrotaskState, 0, 1) \
|
||||
\
|
||||
/* Harmony modules */ \
|
||||
F(IsJSModule, 1, 1) \
|
||||
\
|
||||
@ -302,7 +299,7 @@ namespace internal {
|
||||
F(WeakCollectionSet, 3, 1) \
|
||||
\
|
||||
/* Harmony events */ \
|
||||
F(SetMicrotaskPending, 1, 1) \
|
||||
F(EnqueueMicrotask, 1, 1) \
|
||||
F(RunMicrotasks, 0, 1) \
|
||||
\
|
||||
/* Harmony observe */ \
|
||||
|
@ -1862,37 +1862,3 @@ function SetUpFunction() {
|
||||
}
|
||||
|
||||
SetUpFunction();
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
// TODO(rossberg): very simple abstraction for generic microtask queue.
|
||||
// Eventually, we should move to a real event queue that allows to maintain
|
||||
// relative ordering of different kinds of tasks.
|
||||
|
||||
function RunMicrotasksJS() {
|
||||
while (%SetMicrotaskPending(false)) {
|
||||
var microtaskState = %GetMicrotaskState();
|
||||
if (IS_UNDEFINED(microtaskState.queue))
|
||||
return;
|
||||
|
||||
var microtasks = microtaskState.queue;
|
||||
microtaskState.queue = null;
|
||||
|
||||
for (var i = 0; i < microtasks.length; i++) {
|
||||
microtasks[i]();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function EnqueueMicrotask(fn) {
|
||||
if (!IS_FUNCTION(fn)) {
|
||||
throw new $TypeError('Can only enqueue functions');
|
||||
}
|
||||
var microtaskState = %GetMicrotaskState();
|
||||
if (IS_UNDEFINED(microtaskState.queue) || IS_NULL(microtaskState.queue)) {
|
||||
microtaskState.queue = new InternalArray;
|
||||
}
|
||||
microtaskState.queue.push(fn);
|
||||
%SetMicrotaskPending(true);
|
||||
}
|
||||
|
@ -20821,6 +20821,25 @@ TEST(SetAutorunMicrotasks) {
|
||||
}
|
||||
|
||||
|
||||
TEST(RunMicrotasksWithoutEnteringContext) {
|
||||
v8::Isolate* isolate = CcTest::isolate();
|
||||
HandleScope handle_scope(isolate);
|
||||
isolate->SetAutorunMicrotasks(false);
|
||||
Handle<Context> context = Context::New(isolate);
|
||||
{
|
||||
Context::Scope context_scope(context);
|
||||
CompileRun("var ext1Calls = 0;");
|
||||
isolate->EnqueueMicrotask(Function::New(isolate, MicrotaskOne));
|
||||
}
|
||||
isolate->RunMicrotasks();
|
||||
{
|
||||
Context::Scope context_scope(context);
|
||||
CHECK_EQ(1, CompileRun("ext1Calls")->Int32Value());
|
||||
}
|
||||
isolate->SetAutorunMicrotasks(true);
|
||||
}
|
||||
|
||||
|
||||
static int probes_counter = 0;
|
||||
static int misses_counter = 0;
|
||||
static int updates_counter = 0;
|
||||
|
@ -1,4 +1,5 @@
|
||||
// Copyright 2014 the V8 project authors. All rights reserved.
|
||||
// AUTO-GENERATED BY tools/generate-runtime-tests.py, DO NOT MODIFY
|
||||
// Flags: --allow-natives-syntax --harmony
|
||||
%GetMicrotaskState();
|
||||
var _microtask = function() {};
|
||||
%EnqueueMicrotask(_microtask);
|
@ -1,5 +0,0 @@
|
||||
// Copyright 2014 the V8 project authors. All rights reserved.
|
||||
// AUTO-GENERATED BY tools/generate-runtime-tests.py, DO NOT MODIFY
|
||||
// Flags: --allow-natives-syntax --harmony
|
||||
var _new_state = true;
|
||||
%SetMicrotaskPending(_new_state);
|
@ -47,11 +47,11 @@ EXPAND_MACROS = [
|
||||
# that the parser doesn't bit-rot. Change the values as needed when you add,
|
||||
# remove or change runtime functions, but make sure we don't lose our ability
|
||||
# to parse them!
|
||||
EXPECTED_FUNCTION_COUNT = 362
|
||||
EXPECTED_FUNCTION_COUNT = 361
|
||||
EXPECTED_FUZZABLE_COUNT = 329
|
||||
EXPECTED_CCTEST_COUNT = 6
|
||||
EXPECTED_UNKNOWN_COUNT = 5
|
||||
EXPECTED_BUILTINS_COUNT = 826
|
||||
EXPECTED_BUILTINS_COUNT = 824
|
||||
|
||||
|
||||
# Don't call these at all.
|
||||
|
Loading…
Reference in New Issue
Block a user