Commit 87e4bba3 authored by alph's avatar alph Committed by Commit bot

Support multiple interrupt requests in v8 API.

There might be a number of clients that would like to
setup an interrupt request on the Isolate.

The patch also deprecates ClearInterrupt API. As long as
the interrupt handler is called outside of locks there's no way
to guarantee that the handler will not be called after
ClearInterrupt was invoked as it might have already started execution.

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

Cr-Commit-Position: refs/heads/master@{#25910}
parent 60035482
......@@ -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
......
......@@ -6476,17 +6476,11 @@ void Isolate::CancelTerminateExecution() {
void Isolate::RequestInterrupt(InterruptCallback callback, void* data) {
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(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<i::Isolate*>(this);
isolate->stack_guard()->ClearApiInterrupt();
isolate->set_api_interrupt_callback(NULL);
isolate->set_api_interrupt_callback_data(NULL);
}
......
......@@ -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();
......
......@@ -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<EXTERNAL> state(this);
HandleScope handle_scope(this);
callback(reinterpret_cast<v8::Isolate*>(this), data);
entry.first(reinterpret_cast<v8::Isolate*>(this), entry.second);
}
}
......
......@@ -5,6 +5,7 @@
#ifndef V8_ISOLATE_H_
#define V8_ISOLATE_H_
#include <queue>
#include "include/v8-debug.h"
#include "src/allocation.h"
#include "src/assert-scope.h"
......@@ -390,8 +391,6 @@ typedef List<HeapObject*> 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<InterruptCallback, void*> InterruptEntry;
std::queue<InterruptEntry> api_interrupts_queue_;
#define GLOBAL_BACKING_STORE(type, name, initialvalue) \
type name##_;
ISOLATE_INIT_LIST(GLOBAL_BACKING_STORE)
......
......@@ -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<ClearInterruptFromAnotherThread*>(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<RequestMultipleInterrupts*>(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<Value> function_new_expected_env;
......
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