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:
parent
600354826b
commit
87e4bba31e
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user