test-lockers.cc 31.2 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
// Copyright 2007-2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
//       copyright notice, this list of conditions and the following
//       disclaimer in the documentation and/or other materials provided
//       with the distribution.
//     * Neither the name of Google Inc. nor the names of its
//       contributors may be used to endorse or promote products derived
//       from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#include <limits.h>

30 31
#include <memory>

32
#include "src/v8.h"
33

34
#include "src/base/platform/platform.h"
35
#include "src/codegen/compilation-cache.h"
36 37
#include "src/execution/execution.h"
#include "src/execution/isolate.h"
38
#include "src/objects-inl.h"
39
#include "src/strings/unicode-inl.h"
40 41
#include "src/utils.h"
#include "test/cctest/cctest.h"
42

43 44 45 46 47 48 49 50 51 52 53
namespace {

class DeoptimizeCodeThread : public v8::base::Thread {
 public:
  DeoptimizeCodeThread(v8::Isolate* isolate, v8::Local<v8::Context> context,
                       const char* trigger)
      : Thread(Options("DeoptimizeCodeThread")),
        isolate_(isolate),
        context_(isolate, context),
        source_(trigger) {}

54
  void Run() override {
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
    v8::Locker locker(isolate_);
    isolate_->Enter();
    v8::HandleScope handle_scope(isolate_);
    v8::Local<v8::Context> context =
        v8::Local<v8::Context>::New(isolate_, context_);
    v8::Context::Scope context_scope(context);
    CHECK_EQ(isolate_, v8::Isolate::GetCurrent());
    // This code triggers deoptimization of some function that will be
    // used in a different thread.
    CompileRun(source_);
    isolate_->Exit();
  }

 private:
  v8::Isolate* isolate_;
70
  v8::Persistent<v8::Context> context_;
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
  // The code that triggers the deoptimization.
  const char* source_;
};

void UnlockForDeoptimization(const v8::FunctionCallbackInfo<v8::Value>& args) {
  v8::Isolate* isolate = v8::Isolate::GetCurrent();
  // Gets the pointer to the thread that will trigger the deoptimization of the
  // code.
  DeoptimizeCodeThread* deoptimizer =
      reinterpret_cast<DeoptimizeCodeThread*>(isolate->GetData(0));
  {
    // Exits and unlocks the isolate.
    isolate->Exit();
    v8::Unlocker unlocker(isolate);
    // Starts the deoptimizing thread.
    deoptimizer->Start();
    // Waits for deoptimization to finish.
    deoptimizer->Join();
  }
  // The deoptimizing thread has finished its work, and the isolate
  // will now be used by the current thread.
  isolate->Enter();
}

void UnlockForDeoptimizationIfReady(
    const v8::FunctionCallbackInfo<v8::Value>& args) {
  v8::Isolate* isolate = v8::Isolate::GetCurrent();
  bool* ready_to_deoptimize = reinterpret_cast<bool*>(isolate->GetData(1));
  if (*ready_to_deoptimize) {
    // The test should enter here only once, so put the flag back to false.
    *ready_to_deoptimize = false;
    // Gets the pointer to the thread that will trigger the deoptimization of
    // the code.
    DeoptimizeCodeThread* deoptimizer =
        reinterpret_cast<DeoptimizeCodeThread*>(isolate->GetData(0));
    {
      // Exits and unlocks the thread.
      isolate->Exit();
      v8::Unlocker unlocker(isolate);
      // Starts the thread that deoptimizes the function.
      deoptimizer->Start();
      // Waits for the deoptimizing thread to finish.
      deoptimizer->Join();
    }
    // The deoptimizing thread has finished its work, and the isolate
    // will now be used by the current thread.
    isolate->Enter();
  }
}
}  // namespace

122 123 124 125
namespace v8 {
namespace internal {
namespace test_lockers {

126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
TEST(LazyDeoptimizationMultithread) {
  i::FLAG_allow_natives_syntax = true;
  v8::Isolate::CreateParams create_params;
  create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
  v8::Isolate* isolate = v8::Isolate::New(create_params);
  {
    v8::Locker locker(isolate);
    v8::Isolate::Scope isolate_scope(isolate);
    v8::HandleScope scope(isolate);
    v8::Local<v8::Context> context = v8::Context::New(isolate);
    const char* trigger_deopt = "obj = { y: 0, x: 1 };";

    // We use the isolate to pass arguments to the UnlockForDeoptimization
    // function. Namely, we pass a pointer to the deoptimizing thread.
    DeoptimizeCodeThread deoptimize_thread(isolate, context, trigger_deopt);
    isolate->SetData(0, &deoptimize_thread);
    v8::Context::Scope context_scope(context);

    // Create the function templace for C++ code that is invoked from
    // JavaScript code.
    Local<v8::FunctionTemplate> fun_templ =
        v8::FunctionTemplate::New(isolate, UnlockForDeoptimization);
    Local<Function> fun = fun_templ->GetFunction(context).ToLocalChecked();
    CHECK(context->Global()
              ->Set(context, v8_str("unlock_for_deoptimization"), fun)
              .FromJust());

    // Optimizes a function f, which will be deoptimized in another
    // thread.
    CompileRun(
        "var b = false; var obj = { x: 1 };"
        "function f() { g(); return obj.x; }"
        "function g() { if (b) { unlock_for_deoptimization(); } }"
        "%NeverOptimizeFunction(g);"
160
        "%PrepareFunctionForOptimization(f);"
161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215
        "f(); f(); %OptimizeFunctionOnNextCall(f);"
        "f();");

    // Trigger the unlocking.
    Local<Value> v = CompileRun("b = true; f();");

    // Once the isolate has been unlocked, the thread will wait for the
    // other thread to finish its task. Once this happens, this thread
    // continues with its execution, that is, with the execution of the
    // function g, which then returns to f. The function f should have
    // also been deoptimized. If the replacement did not happen on this
    // thread's stack, then the test will fail here.
    CHECK(v->IsNumber());
    CHECK_EQ(1, static_cast<int>(v->NumberValue(context).FromJust()));
  }
  isolate->Dispose();
}

TEST(LazyDeoptimizationMultithreadWithNatives) {
  i::FLAG_allow_natives_syntax = true;
  v8::Isolate::CreateParams create_params;
  create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
  v8::Isolate* isolate = v8::Isolate::New(create_params);
  {
    v8::Locker locker(isolate);
    v8::Isolate::Scope isolate_scope(isolate);
    v8::HandleScope scope(isolate);
    v8::Local<v8::Context> context = v8::Context::New(isolate);
    const char* trigger_deopt = "%DeoptimizeFunction(f);";

    // We use the isolate to pass arguments to the UnlockForDeoptimization
    // function. Namely, we pass a pointer to the deoptimizing thread.
    DeoptimizeCodeThread deoptimize_thread(isolate, context, trigger_deopt);
    isolate->SetData(0, &deoptimize_thread);
    bool ready_to_deopt = false;
    isolate->SetData(1, &ready_to_deopt);
    v8::Context::Scope context_scope(context);

    // Create the function templace for C++ code that is invoked from
    // JavaScript code.
    Local<v8::FunctionTemplate> fun_templ =
        v8::FunctionTemplate::New(isolate, UnlockForDeoptimizationIfReady);
    Local<Function> fun = fun_templ->GetFunction(context).ToLocalChecked();
    CHECK(context->Global()
              ->Set(context, v8_str("unlock_for_deoptimization"), fun)
              .FromJust());

    // Optimizes a function f, which will be deoptimized in another
    // thread.
    CompileRun(
        "var obj = { x: 1 };"
        "function f() { g(); return obj.x;}"
        "function g() { "
        "  unlock_for_deoptimization(); }"
        "%NeverOptimizeFunction(g);"
216
        "%PrepareFunctionForOptimization(f);"
217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266
        "f(); f(); %OptimizeFunctionOnNextCall(f);");

    // Trigger the unlocking.
    ready_to_deopt = true;
    isolate->SetData(1, &ready_to_deopt);
    Local<Value> v = CompileRun("f();");

    // Once the isolate has been unlocked, the thread will wait for the
    // other thread to finish its task. Once this happens, this thread
    // continues with its execution, that is, with the execution of the
    // function g, which then returns to f. The function f should have
    // also been deoptimized. Otherwise, the test will fail here.
    CHECK(v->IsNumber());
    CHECK_EQ(1, static_cast<int>(v->NumberValue(context).FromJust()));
  }
  isolate->Dispose();
}

TEST(EagerDeoptimizationMultithread) {
  i::FLAG_allow_natives_syntax = true;
  v8::Isolate::CreateParams create_params;
  create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
  v8::Isolate* isolate = v8::Isolate::New(create_params);
  {
    v8::Locker locker(isolate);
    v8::Isolate::Scope isolate_scope(isolate);
    v8::HandleScope scope(isolate);
    v8::Local<v8::Context> context = v8::Context::New(isolate);
    const char* trigger_deopt = "f({y: 0, x: 1});";

    // We use the isolate to pass arguments to the UnlockForDeoptimization
    // function. Namely, we pass a pointer to the deoptimizing thread.
    DeoptimizeCodeThread deoptimize_thread(isolate, context, trigger_deopt);
    isolate->SetData(0, &deoptimize_thread);
    bool ready_to_deopt = false;
    isolate->SetData(1, &ready_to_deopt);
    v8::Context::Scope context_scope(context);

    // Create the function templace for C++ code that is invoked from
    // JavaScript code.
    Local<v8::FunctionTemplate> fun_templ =
        v8::FunctionTemplate::New(isolate, UnlockForDeoptimizationIfReady);
    Local<Function> fun = fun_templ->GetFunction(context).ToLocalChecked();
    CHECK(context->Global()
              ->Set(context, v8_str("unlock_for_deoptimization"), fun)
              .FromJust());

    // Optimizes a function f, which will be deoptimized by another thread.
    CompileRun(
        "function f(obj) { unlock_for_deoptimization(); return obj.x; }"
267
        "%PrepareFunctionForOptimization(f);"
268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286
        "f({x: 1}); f({x: 1});"
        "%OptimizeFunctionOnNextCall(f);"
        "f({x: 1});");

    // Trigger the unlocking.
    ready_to_deopt = true;
    isolate->SetData(1, &ready_to_deopt);
    Local<Value> v = CompileRun("f({x: 1});");

    // Once the isolate has been unlocked, the thread will wait for the
    // other thread to finish its task. Once this happens, this thread
    // continues with its execution, that is, with the execution of the
    // function g, which then returns to f. The function f should have
    // also been deoptimized. Otherwise, the test will fail here.
    CHECK(v->IsNumber());
    CHECK_EQ(1, static_cast<int>(v->NumberValue(context).FromJust()));
  }
  isolate->Dispose();
}
287 288

// Migrating an isolate
289
class KangarooThread : public v8::base::Thread {
290
 public:
291
  KangarooThread(v8::Isolate* isolate, v8::Local<v8::Context> context)
292
      : Thread(Options("KangarooThread")),
293 294
        isolate_(isolate),
        context_(isolate, context) {}
295

296
  void Run() override {
297 298 299
    {
      v8::Locker locker(isolate_);
      v8::Isolate::Scope isolate_scope(isolate_);
300
      CHECK_EQ(isolate_, v8::Isolate::GetCurrent());
301
      v8::HandleScope scope(isolate_);
302 303 304
      v8::Local<v8::Context> context =
          v8::Local<v8::Context>::New(isolate_, context_);
      v8::Context::Scope context_scope(context);
305 306
      Local<Value> v = CompileRun("getValue()");
      CHECK(v->IsNumber());
307
      CHECK_EQ(30, static_cast<int>(v->NumberValue(context).FromJust()));
308 309 310 311
    }
    {
      v8::Locker locker(isolate_);
      v8::Isolate::Scope isolate_scope(isolate_);
312
      v8::HandleScope scope(isolate_);
313 314 315
      v8::Local<v8::Context> context =
          v8::Local<v8::Context>::New(isolate_, context_);
      v8::Context::Scope context_scope(context);
316 317
      Local<Value> v = CompileRun("getValue()");
      CHECK(v->IsNumber());
318
      CHECK_EQ(30, static_cast<int>(v->NumberValue(context).FromJust()));
319 320 321 322 323 324
    }
    isolate_->Dispose();
  }

 private:
  v8::Isolate* isolate_;
325
  v8::Persistent<v8::Context> context_;
326 327
};

328

329 330
// Migrates an isolate from one thread to another
TEST(KangarooIsolates) {
331 332 333
  v8::Isolate::CreateParams create_params;
  create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
  v8::Isolate* isolate = v8::Isolate::New(create_params);
334
  std::unique_ptr<KangarooThread> thread1;
335 336 337
  {
    v8::Locker locker(isolate);
    v8::Isolate::Scope isolate_scope(isolate);
338
    v8::HandleScope handle_scope(isolate);
339
    v8::Local<v8::Context> context = v8::Context::New(isolate);
340
    v8::Context::Scope context_scope(context);
341
    CHECK_EQ(isolate, v8::Isolate::GetCurrent());
342
    CompileRun("function getValue() { return 30; }");
343
    thread1.reset(new KangarooThread(isolate, context));
344
  }
345 346
  thread1->Start();
  thread1->Join();
347 348
}

349

350
static void CalcFibAndCheck(v8::Local<v8::Context> context) {
351 352 353 354 355 356
  Local<Value> v = CompileRun("function fib(n) {"
                              "  if (n <= 2) return 1;"
                              "  return fib(n-1) + fib(n-2);"
                              "}"
                              "fib(10)");
  CHECK(v->IsNumber());
357
  CHECK_EQ(55, static_cast<int>(v->NumberValue(context).FromJust()));
358 359 360 361 362 363
}

class JoinableThread {
 public:
  explicit JoinableThread(const char* name)
    : name_(name),
364
      semaphore_(0),
365 366 367
      thread_(this) {
  }

368
  virtual ~JoinableThread() = default;
369 370 371 372 373 374

  void Start() {
    thread_.Start();
  }

  void Join() {
375
    semaphore_.Wait();
376
    thread_.Join();
377 378 379
  }

  virtual void Run() = 0;
380

381
 private:
382
  class ThreadWithSemaphore : public v8::base::Thread {
383
   public:
384
    explicit ThreadWithSemaphore(JoinableThread* joinable_thread)
385 386
        : Thread(Options(joinable_thread->name_)),
          joinable_thread_(joinable_thread) {}
387

388
    void Run() override {
389
      joinable_thread_->Run();
390
      joinable_thread_->semaphore_.Signal();
391 392 393 394 395 396 397
    }

   private:
    JoinableThread* joinable_thread_;
  };

  const char* name_;
398
  v8::base::Semaphore semaphore_;
399 400 401 402 403 404 405 406 407 408 409 410 411 412 413
  ThreadWithSemaphore thread_;

  friend class ThreadWithSemaphore;

  DISALLOW_COPY_AND_ASSIGN(JoinableThread);
};


class IsolateLockingThreadWithLocalContext : public JoinableThread {
 public:
  explicit IsolateLockingThreadWithLocalContext(v8::Isolate* isolate)
    : JoinableThread("IsolateLockingThread"),
      isolate_(isolate) {
  }

414
  void Run() override {
415 416
    v8::Locker locker(isolate_);
    v8::Isolate::Scope isolate_scope(isolate_);
417
    v8::HandleScope handle_scope(isolate_);
418
    LocalContext local_context(isolate_);
419
    CHECK_EQ(isolate_, v8::Isolate::GetCurrent());
420
    CalcFibAndCheck(local_context.local());
421 422 423 424 425
  }
 private:
  v8::Isolate* isolate_;
};

426 427 428 429
static void StartJoinAndDeleteThreads(
    const std::vector<JoinableThread*>& threads) {
  for (const auto& thread : threads) {
    thread->Start();
430
  }
431 432
  for (const auto& thread : threads) {
    thread->Join();
433
  }
434 435
  for (const auto& thread : threads) {
    delete thread;
436 437 438 439 440 441
  }
}


// Run many threads all locking on the same isolate
TEST(IsolateLockingStress) {
442
  i::FLAG_always_opt = false;
443
#if V8_TARGET_ARCH_MIPS
444 445
  const int kNThreads = 50;
#else
446
  const int kNThreads = 100;
447
#endif
448 449
  std::vector<JoinableThread*> threads;
  threads.reserve(kNThreads);
450 451 452
  v8::Isolate::CreateParams create_params;
  create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
  v8::Isolate* isolate = v8::Isolate::New(create_params);
453
  for (int i = 0; i < kNThreads; i++) {
454
    threads.push_back(new IsolateLockingThreadWithLocalContext(isolate));
455 456 457 458 459 460 461 462 463 464 465
  }
  StartJoinAndDeleteThreads(threads);
  isolate->Dispose();
}


class IsolateNestedLockingThread : public JoinableThread {
 public:
  explicit IsolateNestedLockingThread(v8::Isolate* isolate)
    : JoinableThread("IsolateNestedLocking"), isolate_(isolate) {
  }
466
  void Run() override {
467 468
    v8::Locker lock(isolate_);
    v8::Isolate::Scope isolate_scope(isolate_);
469
    v8::HandleScope handle_scope(isolate_);
470
    LocalContext local_context(isolate_);
471 472
    {
      v8::Locker another_lock(isolate_);
473
      CalcFibAndCheck(local_context.local());
474 475 476
    }
    {
      v8::Locker another_lock(isolate_);
477
      CalcFibAndCheck(local_context.local());
478 479 480 481 482 483
    }
  }
 private:
  v8::Isolate* isolate_;
};

484

485 486
// Run  many threads with nested locks
TEST(IsolateNestedLocking) {
487
  i::FLAG_always_opt = false;
488
#if V8_TARGET_ARCH_MIPS
489 490
  const int kNThreads = 50;
#else
491
  const int kNThreads = 100;
492
#endif
493 494 495
  v8::Isolate::CreateParams create_params;
  create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
  v8::Isolate* isolate = v8::Isolate::New(create_params);
496 497
  std::vector<JoinableThread*> threads;
  threads.reserve(kNThreads);
498
  for (int i = 0; i < kNThreads; i++) {
499
    threads.push_back(new IsolateNestedLockingThread(isolate));
500 501
  }
  StartJoinAndDeleteThreads(threads);
502
  isolate->Dispose();
503 504 505 506 507 508 509 510 511 512 513
}


class SeparateIsolatesLocksNonexclusiveThread : public JoinableThread {
 public:
  SeparateIsolatesLocksNonexclusiveThread(v8::Isolate* isolate1,
                                          v8::Isolate* isolate2)
    : JoinableThread("SeparateIsolatesLocksNonexclusiveThread"),
      isolate1_(isolate1), isolate2_(isolate2) {
  }

514
  void Run() override {
515 516
    v8::Locker lock(isolate1_);
    v8::Isolate::Scope isolate_scope(isolate1_);
517
    v8::HandleScope handle_scope(isolate1_);
518
    LocalContext local_context(isolate1_);
519 520 521

    IsolateLockingThreadWithLocalContext threadB(isolate2_);
    threadB.Start();
522
    CalcFibAndCheck(local_context.local());
523 524 525 526 527 528 529
    threadB.Join();
  }
 private:
  v8::Isolate* isolate1_;
  v8::Isolate* isolate2_;
};

530

531 532
// Run parallel threads that lock and access different isolates in parallel
TEST(SeparateIsolatesLocksNonexclusive) {
533
  i::FLAG_always_opt = false;
534
#if V8_TARGET_ARCH_ARM || V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_S390
535 536
  const int kNThreads = 50;
#else
537
  const int kNThreads = 100;
538
#endif
539 540 541 542
  v8::Isolate::CreateParams create_params;
  create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
  v8::Isolate* isolate1 = v8::Isolate::New(create_params);
  v8::Isolate* isolate2 = v8::Isolate::New(create_params);
543 544
  std::vector<JoinableThread*> threads;
  threads.reserve(kNThreads);
545
  for (int i = 0; i < kNThreads; i++) {
546 547
    threads.push_back(
        new SeparateIsolatesLocksNonexclusiveThread(isolate1, isolate2));
548 549 550 551 552 553 554 555 556
  }
  StartJoinAndDeleteThreads(threads);
  isolate2->Dispose();
  isolate1->Dispose();
}

class LockIsolateAndCalculateFibSharedContextThread : public JoinableThread {
 public:
  explicit LockIsolateAndCalculateFibSharedContextThread(
557 558 559 560
      v8::Isolate* isolate, v8::Local<v8::Context> context)
      : JoinableThread("LockIsolateAndCalculateFibThread"),
        isolate_(isolate),
        context_(isolate, context) {}
561

562
  void Run() override {
563 564
    v8::Locker lock(isolate_);
    v8::Isolate::Scope isolate_scope(isolate_);
565
    v8::HandleScope handle_scope(isolate_);
566 567 568
    v8::Local<v8::Context> context =
        v8::Local<v8::Context>::New(isolate_, context_);
    v8::Context::Scope context_scope(context);
569
    CalcFibAndCheck(context);
570 571 572
  }
 private:
  v8::Isolate* isolate_;
573
  v8::Persistent<v8::Context> context_;
574 575 576 577 578 579 580 581 582
};

class LockerUnlockerThread : public JoinableThread {
 public:
  explicit LockerUnlockerThread(v8::Isolate* isolate)
    : JoinableThread("LockerUnlockerThread"),
      isolate_(isolate) {
  }

583
  void Run() override {
584
    isolate_->DiscardThreadSpecificMetadata();  // No-op
585
    {
586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605
      v8::Locker lock(isolate_);
      v8::Isolate::Scope isolate_scope(isolate_);
      v8::HandleScope handle_scope(isolate_);
      v8::Local<v8::Context> context = v8::Context::New(isolate_);
      {
        v8::Context::Scope context_scope(context);
        CalcFibAndCheck(context);
      }
      {
        LockIsolateAndCalculateFibSharedContextThread thread(isolate_, context);
        isolate_->Exit();
        v8::Unlocker unlocker(isolate_);
        thread.Start();
        thread.Join();
      }
      isolate_->Enter();
      {
        v8::Context::Scope context_scope(context);
        CalcFibAndCheck(context);
      }
606
    }
607 608
    isolate_->DiscardThreadSpecificMetadata();
    isolate_->DiscardThreadSpecificMetadata();  // No-op
609
  }
610

611 612 613 614
 private:
  v8::Isolate* isolate_;
};

615

616 617
// Use unlocker inside of a Locker, multiple threads.
TEST(LockerUnlocker) {
618
  i::FLAG_always_opt = false;
619
#if V8_TARGET_ARCH_ARM || V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_S390
620 621
  const int kNThreads = 50;
#else
622
  const int kNThreads = 100;
623
#endif
624 625
  std::vector<JoinableThread*> threads;
  threads.reserve(kNThreads);
626 627 628
  v8::Isolate::CreateParams create_params;
  create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
  v8::Isolate* isolate = v8::Isolate::New(create_params);
629
  for (int i = 0; i < kNThreads; i++) {
630
    threads.push_back(new LockerUnlockerThread(isolate));
631 632 633 634 635 636 637 638 639 640 641 642
  }
  StartJoinAndDeleteThreads(threads);
  isolate->Dispose();
}

class LockTwiceAndUnlockThread : public JoinableThread {
 public:
  explicit LockTwiceAndUnlockThread(v8::Isolate* isolate)
    : JoinableThread("LockTwiceAndUnlockThread"),
      isolate_(isolate) {
  }

643
  void Run() override {
644 645
    v8::Locker lock(isolate_);
    v8::Isolate::Scope isolate_scope(isolate_);
646
    v8::HandleScope handle_scope(isolate_);
647
    v8::Local<v8::Context> context = v8::Context::New(isolate_);
648 649
    {
      v8::Context::Scope context_scope(context);
650
      CalcFibAndCheck(context);
651 652 653 654
    }
    {
      v8::Locker second_lock(isolate_);
      {
655
        LockIsolateAndCalculateFibSharedContextThread thread(isolate_, context);
656 657 658 659 660 661 662 663 664
        isolate_->Exit();
        v8::Unlocker unlocker(isolate_);
        thread.Start();
        thread.Join();
      }
    }
    isolate_->Enter();
    {
      v8::Context::Scope context_scope(context);
665
      CalcFibAndCheck(context);
666 667
    }
  }
668

669 670 671 672
 private:
  v8::Isolate* isolate_;
};

673

674 675
// Use Unlocker inside two Lockers.
TEST(LockTwiceAndUnlock) {
676
  i::FLAG_always_opt = false;
677
#if V8_TARGET_ARCH_ARM || V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_S390
678 679
  const int kNThreads = 50;
#else
680
  const int kNThreads = 100;
681
#endif
682 683
  std::vector<JoinableThread*> threads;
  threads.reserve(kNThreads);
684 685 686
  v8::Isolate::CreateParams create_params;
  create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
  v8::Isolate* isolate = v8::Isolate::New(create_params);
687
  for (int i = 0; i < kNThreads; i++) {
688
    threads.push_back(new LockTwiceAndUnlockThread(isolate));
689 690 691 692 693 694 695 696 697 698 699 700 701 702
  }
  StartJoinAndDeleteThreads(threads);
  isolate->Dispose();
}

class LockAndUnlockDifferentIsolatesThread : public JoinableThread {
 public:
  LockAndUnlockDifferentIsolatesThread(v8::Isolate* isolate1,
                                       v8::Isolate* isolate2)
    : JoinableThread("LockAndUnlockDifferentIsolatesThread"),
      isolate1_(isolate1),
      isolate2_(isolate2) {
  }

703
  void Run() override {
704
    std::unique_ptr<LockIsolateAndCalculateFibSharedContextThread> thread;
705 706 707 708 709
    v8::Locker lock1(isolate1_);
    CHECK(v8::Locker::IsLocked(isolate1_));
    CHECK(!v8::Locker::IsLocked(isolate2_));
    {
      v8::Isolate::Scope isolate_scope(isolate1_);
710
      v8::HandleScope handle_scope(isolate1_);
711
      v8::Local<v8::Context> context1 = v8::Context::New(isolate1_);
712 713
      {
        v8::Context::Scope context_scope(context1);
714
        CalcFibAndCheck(context1);
715
      }
716 717
      thread.reset(new LockIsolateAndCalculateFibSharedContextThread(isolate1_,
                                                                     context1));
718 719 720 721 722 723
    }
    v8::Locker lock2(isolate2_);
    CHECK(v8::Locker::IsLocked(isolate1_));
    CHECK(v8::Locker::IsLocked(isolate2_));
    {
      v8::Isolate::Scope isolate_scope(isolate2_);
724
      v8::HandleScope handle_scope(isolate2_);
725
      v8::Local<v8::Context> context2 = v8::Context::New(isolate2_);
726 727
      {
        v8::Context::Scope context_scope(context2);
728
        CalcFibAndCheck(context2);
729 730 731 732 733
      }
      v8::Unlocker unlock1(isolate1_);
      CHECK(!v8::Locker::IsLocked(isolate1_));
      CHECK(v8::Locker::IsLocked(isolate2_));
      v8::Context::Scope context_scope(context2);
734
      thread->Start();
735
      CalcFibAndCheck(context2);
736
      thread->Join();
737 738
    }
  }
739

740 741 742 743 744
 private:
  v8::Isolate* isolate1_;
  v8::Isolate* isolate2_;
};

745

746 747
// Lock two isolates and unlock one of them.
TEST(LockAndUnlockDifferentIsolates) {
748 749 750 751
  v8::Isolate::CreateParams create_params;
  create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
  v8::Isolate* isolate1 = v8::Isolate::New(create_params);
  v8::Isolate* isolate2 = v8::Isolate::New(create_params);
752 753 754 755 756 757 758 759 760
  LockAndUnlockDifferentIsolatesThread thread(isolate1, isolate2);
  thread.Start();
  thread.Join();
  isolate2->Dispose();
  isolate1->Dispose();
}

class LockUnlockLockThread : public JoinableThread {
 public:
761 762 763 764
  LockUnlockLockThread(v8::Isolate* isolate, v8::Local<v8::Context> context)
      : JoinableThread("LockUnlockLockThread"),
        isolate_(isolate),
        context_(isolate, context) {}
765

766
  void Run() override {
767 768
    v8::Locker lock1(isolate_);
    CHECK(v8::Locker::IsLocked(isolate_));
769
    CHECK(!v8::Locker::IsLocked(CcTest::isolate()));
770 771
    {
      v8::Isolate::Scope isolate_scope(isolate_);
772
      v8::HandleScope handle_scope(isolate_);
773 774 775
      v8::Local<v8::Context> context =
          v8::Local<v8::Context>::New(isolate_, context_);
      v8::Context::Scope context_scope(context);
776
      CalcFibAndCheck(context);
777 778 779 780
    }
    {
      v8::Unlocker unlock1(isolate_);
      CHECK(!v8::Locker::IsLocked(isolate_));
781
      CHECK(!v8::Locker::IsLocked(CcTest::isolate()));
782 783 784
      {
        v8::Locker lock2(isolate_);
        v8::Isolate::Scope isolate_scope(isolate_);
785
        v8::HandleScope handle_scope(isolate_);
786
        CHECK(v8::Locker::IsLocked(isolate_));
787
        CHECK(!v8::Locker::IsLocked(CcTest::isolate()));
788 789 790
        v8::Local<v8::Context> context =
            v8::Local<v8::Context>::New(isolate_, context_);
        v8::Context::Scope context_scope(context);
791
        CalcFibAndCheck(context);
792 793 794 795 796 797 798 799 800
      }
    }
  }

 private:
  v8::Isolate* isolate_;
  v8::Persistent<v8::Context> context_;
};

801

802 803
// Locker inside an Unlocker inside a Locker.
TEST(LockUnlockLockMultithreaded) {
804
#if V8_TARGET_ARCH_MIPS
805 806
  const int kNThreads = 50;
#else
807
  const int kNThreads = 100;
808
#endif
809 810 811
  v8::Isolate::CreateParams create_params;
  create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
  v8::Isolate* isolate = v8::Isolate::New(create_params);
812 813
  std::vector<JoinableThread*> threads;
  threads.reserve(kNThreads);
814 815 816
  {
    v8::Locker locker_(isolate);
    v8::Isolate::Scope isolate_scope(isolate);
817
    v8::HandleScope handle_scope(isolate);
818
    v8::Local<v8::Context> context = v8::Context::New(isolate);
819
    for (int i = 0; i < kNThreads; i++) {
820
      threads.push_back(new LockUnlockLockThread(isolate, context));
821
    }
822 823
  }
  StartJoinAndDeleteThreads(threads);
824
  isolate->Dispose();
825 826 827 828
}

class LockUnlockLockDefaultIsolateThread : public JoinableThread {
 public:
829
  explicit LockUnlockLockDefaultIsolateThread(v8::Local<v8::Context> context)
830
      : JoinableThread("LockUnlockLockDefaultIsolateThread"),
831
        context_(CcTest::isolate(), context) {}
832

833
  void Run() override {
834
    v8::Locker lock1(CcTest::isolate());
835
    {
836
      v8::Isolate::Scope isolate_scope(CcTest::isolate());
837
      v8::HandleScope handle_scope(CcTest::isolate());
838
      v8::Local<v8::Context> context =
839
          v8::Local<v8::Context>::New(CcTest::isolate(), context_);
840
      v8::Context::Scope context_scope(context);
841
      CalcFibAndCheck(context);
842 843
    }
    {
844
      v8::Unlocker unlock1(CcTest::isolate());
845
      {
846
        v8::Locker lock2(CcTest::isolate());
847
        v8::Isolate::Scope isolate_scope(CcTest::isolate());
848
        v8::HandleScope handle_scope(CcTest::isolate());
849
        v8::Local<v8::Context> context =
850
            v8::Local<v8::Context>::New(CcTest::isolate(), context_);
851
        v8::Context::Scope context_scope(context);
852
        CalcFibAndCheck(context);
853 854 855 856 857 858 859 860
      }
    }
  }

 private:
  v8::Persistent<v8::Context> context_;
};

861

862
// Locker inside an Unlocker inside a Locker for default isolate.
863
TEST(LockUnlockLockDefaultIsolateMultithreaded) {
864
#if V8_TARGET_ARCH_MIPS
865 866
  const int kNThreads = 50;
#else
867
  const int kNThreads = 100;
868
#endif
869
  Local<v8::Context> context;
870 871
  std::vector<JoinableThread*> threads;
  threads.reserve(kNThreads);
872
  {
873
    v8::Locker locker_(CcTest::isolate());
874
    v8::Isolate::Scope isolate_scope(CcTest::isolate());
875 876
    v8::HandleScope handle_scope(CcTest::isolate());
    context = v8::Context::New(CcTest::isolate());
877
    for (int i = 0; i < kNThreads; i++) {
878
      threads.push_back(new LockUnlockLockDefaultIsolateThread(context));
879
    }
880 881 882
  }
  StartJoinAndDeleteThreads(threads);
}
883 884 885 886


TEST(Regress1433) {
  for (int i = 0; i < 10; i++) {
887 888 889
    v8::Isolate::CreateParams create_params;
    create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
    v8::Isolate* isolate = v8::Isolate::New(create_params);
890 891 892
    {
      v8::Locker lock(isolate);
      v8::Isolate::Scope isolate_scope(isolate);
893
      v8::HandleScope handle_scope(isolate);
894
      v8::Local<v8::Context> context = v8::Context::New(isolate);
895
      v8::Context::Scope context_scope(context);
896 897
      v8::Local<v8::String> source = v8_str("1+1");
      v8::Local<v8::Script> script =
898
          v8::Script::Compile(context, source).ToLocalChecked();
899
      v8::Local<v8::Value> result = script->Run(context).ToLocalChecked();
900
      v8::String::Utf8Value utf8(isolate, result);
901 902 903 904
    }
    isolate->Dispose();
  }
}
905 906 907 908 909 910 911 912 913 914 915 916 917 918 919


static const char* kSimpleExtensionSource =
  "(function Foo() {"
  "  return 4;"
  "})() ";

class IsolateGenesisThread : public JoinableThread {
 public:
  IsolateGenesisThread(int count, const char* extension_names[])
    : JoinableThread("IsolateGenesisThread"),
      count_(count),
      extension_names_(extension_names)
  {}

920
  void Run() override {
921 922 923
    v8::Isolate::CreateParams create_params;
    create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
    v8::Isolate* isolate = v8::Isolate::New(create_params);
924 925 926
    {
      v8::Isolate::Scope isolate_scope(isolate);
      v8::ExtensionConfiguration extensions(count_, extension_names_);
927 928
      v8::HandleScope handle_scope(isolate);
      v8::Context::New(isolate, &extensions);
929 930 931
    }
    isolate->Dispose();
  }
932

933 934 935 936 937
 private:
  int count_;
  const char** extension_names_;
};

938

939 940 941
// Test installing extensions in separate isolates concurrently.
// http://code.google.com/p/v8/issues/detail?id=1821
TEST(ExtensionsRegistration) {
942
#if V8_TARGET_ARCH_ARM || V8_TARGET_ARCH_MIPS
943
  const int kNThreads = 10;
944 945
#elif V8_TARGET_ARCH_S390 && V8_TARGET_ARCH_32_BIT
  const int kNThreads = 10;
946
#else
947
  const int kNThreads = 40;
948
#endif
949 950 951 952 953 954
  const char* extension_names[] = {"test0", "test1", "test2", "test3",
                                   "test4", "test5", "test6", "test7"};
  for (const char* name : extension_names) {
    v8::RegisterExtension(
        v8::base::make_unique<v8::Extension>(name, kSimpleExtensionSource));
  }
955 956
  std::vector<JoinableThread*> threads;
  threads.reserve(kNThreads);
957
  for (int i = 0; i < kNThreads; i++) {
958
    threads.push_back(new IsolateGenesisThread(8, extension_names));
959 960 961
  }
  StartJoinAndDeleteThreads(threads);
}
962 963 964 965

}  // namespace test_lockers
}  // namespace internal
}  // namespace v8