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}
This commit is contained in:
alph 2014-12-19 23:54:03 -08:00 committed by Commit bot
parent 600354826b
commit 87e4bba31e
6 changed files with 42 additions and 50 deletions

View File

@ -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

View File

@ -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);
}

View File

@ -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();

View File

@ -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);
}
}

View File

@ -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)

View File

@ -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;