* 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.
*/
* 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
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);
}
}
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();
}
-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);
}
}
#ifndef V8_ISOLATE_H_
#define V8_ISOLATE_H_
+#include <queue>
#include "include/v8-debug.h"
#include "src/allocation.h"
#include "src/assert-scope.h"
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)
Object* TerminateExecution();
void CancelTerminateExecution();
- void InvokeApiInterruptCallback();
+ void RequestInterrupt(InterruptCallback callback, void* data);
+ void InvokeApiInterruptCallbacks();
// Administration
void Iterate(ObjectVisitor* v);
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)
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());
}
-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();
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;