diff --git a/include/v8.h b/include/v8.h index b34f43a7cc..60ad9812c0 100644 --- a/include/v8.h +++ b/include/v8.h @@ -5050,8 +5050,7 @@ class V8_EXPORT Isolate { * Request V8 to interrupt long running JavaScript code and invoke * the given |callback| passing the given |data| to it. After |callback| * returns control will be returned to the JavaScript code. - * At any given moment V8 can remember only a single callback for the very - * last interrupt request. + * There may be a number of interrupt requests in flight. * Can be called from another thread without acquiring a |Locker|. * Registered |callback| must not reenter interrupted Isolate. */ @@ -5061,7 +5060,8 @@ class V8_EXPORT Isolate { * Clear interrupt request created by |RequestInterrupt|. * Can be called from another thread without acquiring a |Locker|. */ - void ClearInterrupt(); + V8_DEPRECATED("There's no way to clear interrupts in flight.", + void ClearInterrupt()); /** * Request garbage collection in this Isolate. It is only valid to call this diff --git a/src/api.cc b/src/api.cc index 8ae7b858c3..65777bc0f0 100644 --- a/src/api.cc +++ b/src/api.cc @@ -6476,17 +6476,11 @@ void Isolate::CancelTerminateExecution() { void Isolate::RequestInterrupt(InterruptCallback callback, void* data) { i::Isolate* isolate = reinterpret_cast(this); - isolate->set_api_interrupt_callback(callback); - isolate->set_api_interrupt_callback_data(data); - isolate->stack_guard()->RequestApiInterrupt(); + isolate->RequestInterrupt(callback, data); } void Isolate::ClearInterrupt() { - i::Isolate* isolate = reinterpret_cast(this); - isolate->stack_guard()->ClearApiInterrupt(); - isolate->set_api_interrupt_callback(NULL); - isolate->set_api_interrupt_callback_data(NULL); } diff --git a/src/execution.cc b/src/execution.cc index b4d19872fb..a85effd403 100644 --- a/src/execution.cc +++ b/src/execution.cc @@ -713,8 +713,8 @@ Object* StackGuard::HandleInterrupts() { } if (CheckAndClearInterrupt(API_INTERRUPT)) { - // Callback must be invoked outside of ExecusionAccess lock. - isolate_->InvokeApiInterruptCallback(); + // Callbacks must be invoked outside of ExecusionAccess lock. + isolate_->InvokeApiInterruptCallbacks(); } isolate_->counters()->stack_interrupts()->Increment(); diff --git a/src/isolate.cc b/src/isolate.cc index e5aed8b3ab..b24182b6a0 100644 --- a/src/isolate.cc +++ b/src/isolate.cc @@ -929,22 +929,26 @@ void Isolate::CancelTerminateExecution() { } -void Isolate::InvokeApiInterruptCallback() { - // Note: callback below should be called outside of execution access lock. - InterruptCallback callback = NULL; - void* data = NULL; - { - ExecutionAccess access(this); - callback = api_interrupt_callback_; - data = api_interrupt_callback_data_; - api_interrupt_callback_ = NULL; - api_interrupt_callback_data_ = NULL; - } +void Isolate::RequestInterrupt(InterruptCallback callback, void* data) { + ExecutionAccess access(this); + api_interrupts_queue_.push(InterruptEntry(callback, data)); + stack_guard()->RequestApiInterrupt(); +} - if (callback != NULL) { + +void Isolate::InvokeApiInterruptCallbacks() { + // Note: callback below should be called outside of execution access lock. + while (true) { + InterruptEntry entry; + { + ExecutionAccess access(this); + if (api_interrupts_queue_.empty()) return; + entry = api_interrupts_queue_.front(); + api_interrupts_queue_.pop(); + } VMState state(this); HandleScope handle_scope(this); - callback(reinterpret_cast(this), data); + entry.first(reinterpret_cast(this), entry.second); } } diff --git a/src/isolate.h b/src/isolate.h index 5a7d2f9b0f..42a814ad72 100644 --- a/src/isolate.h +++ b/src/isolate.h @@ -5,6 +5,7 @@ #ifndef V8_ISOLATE_H_ #define V8_ISOLATE_H_ +#include #include "include/v8-debug.h" #include "src/allocation.h" #include "src/assert-scope.h" @@ -390,8 +391,6 @@ typedef List DebugObjectCache; V(bool, fp_stubs_generated, false) \ V(int, max_available_threads, 0) \ V(uint32_t, per_isolate_assert_data, 0xFFFFFFFFu) \ - V(InterruptCallback, api_interrupt_callback, NULL) \ - V(void*, api_interrupt_callback_data, NULL) \ V(PromiseRejectCallback, promise_reject_callback, NULL) \ ISOLATE_INIT_SIMULATOR_LIST(V) @@ -816,7 +815,8 @@ class Isolate { Object* TerminateExecution(); void CancelTerminateExecution(); - void InvokeApiInterruptCallback(); + void RequestInterrupt(InterruptCallback callback, void* data); + void InvokeApiInterruptCallbacks(); // Administration void Iterate(ObjectVisitor* v); @@ -1294,6 +1294,9 @@ class Isolate { HeapProfiler* heap_profiler_; FunctionEntryHook function_entry_hook_; + typedef std::pair InterruptEntry; + std::queue api_interrupts_queue_; + #define GLOBAL_BACKING_STORE(type, name, initialvalue) \ type name##_; ISOLATE_INIT_LIST(GLOBAL_BACKING_STORE) diff --git a/test/cctest/test-api.cc b/test/cctest/test-api.cc index 0bd28b22a2..1ca88527de 100644 --- a/test/cctest/test-api.cc +++ b/test/cctest/test-api.cc @@ -23140,8 +23140,6 @@ class RequestInterruptTestBase { TestBody(); - isolate_->ClearInterrupt(); - // Verify we arrived here because interruptor was called // not due to a bug causing us to exit the loop too early. CHECK(!should_continue()); @@ -23390,10 +23388,9 @@ TEST(RequestInterruptTestWithMathAbs) { } -class ClearInterruptFromAnotherThread - : public RequestInterruptTestBase { +class RequestMultipleInterrupts : public RequestInterruptTestBase { public: - ClearInterruptFromAnotherThread() : i_thread(this), sem2_(0) { } + RequestMultipleInterrupts() : i_thread(this), counter_(0) {} virtual void StartInterruptThread() { i_thread.Start(); @@ -23410,39 +23407,33 @@ class ClearInterruptFromAnotherThread private: class InterruptThread : public v8::base::Thread { public: - explicit InterruptThread(ClearInterruptFromAnotherThread* test) + enum { NUM_INTERRUPTS = 10 }; + explicit InterruptThread(RequestMultipleInterrupts* test) : Thread(Options("RequestInterruptTest")), test_(test) {} virtual void Run() { test_->sem_.Wait(); - test_->isolate_->RequestInterrupt(&OnInterrupt, test_); - test_->sem_.Wait(); - test_->isolate_->ClearInterrupt(); - test_->sem2_.Signal(); + for (int i = 0; i < NUM_INTERRUPTS; i++) { + test_->isolate_->RequestInterrupt(&OnInterrupt, test_); + } } static void OnInterrupt(v8::Isolate* isolate, void* data) { - ClearInterruptFromAnotherThread* test = - reinterpret_cast(data); - test->sem_.Signal(); - bool success = test->sem2_.WaitFor(v8::base::TimeDelta::FromSeconds(2)); - // Crash instead of timeout to make this failure more prominent. - CHECK(success); - test->should_continue_ = false; + RequestMultipleInterrupts* test = + reinterpret_cast(data); + test->should_continue_ = ++test->counter_ < NUM_INTERRUPTS; } private: - ClearInterruptFromAnotherThread* test_; + RequestMultipleInterrupts* test_; }; InterruptThread i_thread; - v8::base::Semaphore sem2_; + int counter_; }; -TEST(ClearInterruptFromAnotherThread) { - ClearInterruptFromAnotherThread().RunTest(); -} +TEST(RequestMultipleInterrupts) { RequestMultipleInterrupts().RunTest(); } static Local function_new_expected_env;