| Index: runtime/vm/isolate_test.cc
|
| diff --git a/runtime/vm/isolate_test.cc b/runtime/vm/isolate_test.cc
|
| index f5f0506aeaa63bf4cc3356b4c4d89f994cdab854..d0d92c8a3cae41f49b5d8e7392247d859ae662be 100644
|
| --- a/runtime/vm/isolate_test.cc
|
| +++ b/runtime/vm/isolate_test.cc
|
| @@ -6,6 +6,8 @@
|
| #include "platform/assert.h"
|
| #include "vm/globals.h"
|
| #include "vm/isolate.h"
|
| +#include "vm/lockers.h"
|
| +#include "vm/thread_pool.h"
|
| #include "vm/unit_test.h"
|
|
|
| namespace dart {
|
| @@ -80,4 +82,134 @@ TEST_CASE(IsolateSpawn) {
|
| EXPECT_VALID(exception_result);
|
| }
|
|
|
| +
|
| +class InterruptChecker : public ThreadPool::Task {
|
| + public:
|
| + static const intptr_t kTaskCount;
|
| + static const intptr_t kIterations;
|
| +
|
| + InterruptChecker(Isolate* isolate,
|
| + Monitor* awake_monitor,
|
| + bool* awake,
|
| + Monitor* round_monitor,
|
| + const intptr_t* round)
|
| + : isolate_(isolate),
|
| + awake_monitor_(awake_monitor),
|
| + awake_(awake),
|
| + round_monitor_(round_monitor),
|
| + round_(round) {
|
| + }
|
| +
|
| + virtual void Run() {
|
| + Thread::EnterIsolateAsHelper(isolate_);
|
| + // Tell main thread that we are ready.
|
| + {
|
| + MonitorLocker ml(awake_monitor_);
|
| + ASSERT(!*awake_);
|
| + *awake_ = true;
|
| + ml.Notify();
|
| + }
|
| + for (intptr_t i = 0; i < kIterations; ++i) {
|
| + // Busy wait for interrupts.
|
| + while (!isolate_->HasInterruptsScheduled(Isolate::kVMInterrupt)) {
|
| + // Do nothing.
|
| + }
|
| + // Tell main thread that we observed the interrupt.
|
| + {
|
| + MonitorLocker ml(awake_monitor_);
|
| + ASSERT(!*awake_);
|
| + *awake_ = true;
|
| + ml.Notify();
|
| + }
|
| + // Wait for main thread to let us resume, i.e., until all tasks are here.
|
| + {
|
| + MonitorLocker ml(round_monitor_);
|
| + EXPECT(*round_ == i || *round_ == (i + 1));
|
| + while (*round_ == i) {
|
| + ml.Wait();
|
| + }
|
| + EXPECT(*round_ == i + 1);
|
| + }
|
| + }
|
| + Thread::ExitIsolateAsHelper();
|
| + // Use awake also to signal exit.
|
| + {
|
| + MonitorLocker ml(awake_monitor_);
|
| + *awake_ = true;
|
| + ml.Notify();
|
| + }
|
| + }
|
| +
|
| + private:
|
| + Isolate* isolate_;
|
| + Monitor* awake_monitor_;
|
| + bool* awake_;
|
| + Monitor* round_monitor_;
|
| + const intptr_t* round_;
|
| +};
|
| +
|
| +
|
| +const intptr_t InterruptChecker::kTaskCount = 50;
|
| +const intptr_t InterruptChecker::kIterations = 1000;
|
| +
|
| +
|
| +// Waits for all tasks to set their individual flag, then clears them all.
|
| +static void WaitForAllTasks(bool* flags, Monitor* monitor) {
|
| + MonitorLocker ml(monitor);
|
| + while (true) {
|
| + intptr_t count = 0;
|
| + for (intptr_t task = 0; task < InterruptChecker::kTaskCount; ++task) {
|
| + if (flags[task]) {
|
| + ++count;
|
| + }
|
| + }
|
| + if (count == InterruptChecker::kTaskCount) {
|
| + memset(flags, 0, sizeof(*flags) * count);
|
| + break;
|
| + } else {
|
| + ml.Wait();
|
| + }
|
| + }
|
| +}
|
| +
|
| +// Test and document usage of Isolate::HasInterruptsScheduled.
|
| +//
|
| +// Go through a number of rounds of scheduling interrupts and waiting until all
|
| +// unsynchronized busy-waiting tasks observe it (in the current implementation,
|
| +// the exact latency depends on cache coherence). Synchronization is then used
|
| +// to ensure that the response to the interrupt, i.e., starting a new round,
|
| +// happens *after* the interrupt is observed. Without this synchronization, the
|
| +// compiler and/or CPU could reorder operations to make the tasks observe the
|
| +// round update *before* the interrupt is set.
|
| +TEST_CASE(StackLimitInterrupts) {
|
| + Monitor awake_monitor; // Synchronizes the 'awake' flags.
|
| + bool awake[InterruptChecker::kTaskCount];
|
| + memset(awake, 0, sizeof(awake));
|
| + Monitor round_monitor; // Synchronizes the 'round' counter.
|
| + intptr_t round = 0;
|
| + Isolate* isolate = Thread::Current()->isolate();
|
| + // Start all tasks. They will busy-wait until interrupted in the first round.
|
| + for (intptr_t task = 0; task < InterruptChecker::kTaskCount; task++) {
|
| + Dart::thread_pool()->Run(new InterruptChecker(
|
| + isolate, &awake_monitor, &awake[task], &round_monitor, &round));
|
| + }
|
| + // Wait for all tasks to get ready for the first round.
|
| + WaitForAllTasks(awake, &awake_monitor);
|
| + for (intptr_t i = 0; i < InterruptChecker::kIterations; ++i) {
|
| + isolate->ScheduleInterrupts(Isolate::kVMInterrupt);
|
| + // Wait for all tasks to observe the interrupt.
|
| + WaitForAllTasks(awake, &awake_monitor);
|
| + // Continue with next round.
|
| + uword interrupts = isolate->GetAndClearInterrupts();
|
| + EXPECT((interrupts & Isolate::kVMInterrupt) != 0);
|
| + {
|
| + MonitorLocker ml(&round_monitor);
|
| + ++round;
|
| + ml.NotifyAll();
|
| + }
|
| + }
|
| + // Wait for tasks to exit cleanly.
|
| + WaitForAllTasks(awake, &awake_monitor);
|
| +}
|
| +
|
| } // namespace dart
|
|
|