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 33 34
#include "include/v8-extension.h"
#include "include/v8-function.h"
#include "include/v8-locker.h"
35
#include "src/base/platform/platform.h"
36
#include "src/codegen/compilation-cache.h"
37 38
#include "src/execution/execution.h"
#include "src/execution/isolate.h"
39
#include "src/init/v8.h"
40
#include "src/objects/objects-inl.h"
41
#include "src/strings/unicode-inl.h"
42
#include "src/utils/utils.h"
43
#include "test/cctest/cctest.h"
44

45 46 47 48 49 50 51 52 53 54 55
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) {}

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

 private:
  v8::Isolate* isolate_;
73
  v8::Persistent<v8::Context> context_;
74 75 76 77 78
  // The code that triggers the deoptimization.
  const char* source_;
};

void UnlockForDeoptimization(const v8::FunctionCallbackInfo<v8::Value>& args) {
79
  v8::Isolate* isolate = args.GetIsolate();
80 81 82 83 84 85 86 87 88
  // 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.
89
    CHECK(deoptimizer->Start());
90 91 92 93 94 95 96 97 98 99
    // 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) {
100
  v8::Isolate* isolate = args.GetIsolate();
101 102 103 104 105 106 107 108 109 110 111 112 113
  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.
114
      CHECK(deoptimizer->Start());
115 116 117 118 119 120 121 122 123 124
      // 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

125 126 127 128
namespace v8 {
namespace internal {
namespace test_lockers {

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 160 161 162
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);"
163
        "%PrepareFunctionForOptimization(f);"
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 216 217 218
        "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);"
219
        "%PrepareFunctionForOptimization(f);"
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 267 268 269
        "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; }"
270
        "%PrepareFunctionForOptimization(f);"
271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289
        "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();
}
290 291

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

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

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

330

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

350

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

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

369
  virtual ~JoinableThread() = default;
370 371
  JoinableThread(const JoinableThread&) = delete;
  JoinableThread& operator=(const JoinableThread&) = delete;
372

373
  void Start() { CHECK(thread_.Start()); }
374 375

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

  virtual void Run() = 0;
381

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

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

   private:
    JoinableThread* joinable_thread_;
  };

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

  friend class ThreadWithSemaphore;
};


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

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

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


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


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

482

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


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

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

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

528

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

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

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

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

581
  void Run() override {
582
    isolate_->DiscardThreadSpecificMetadata();  // No-op
583
    {
584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603
      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);
      }
604
    }
605 606
    isolate_->DiscardThreadSpecificMetadata();
    isolate_->DiscardThreadSpecificMetadata();  // No-op
607
  }
608

609 610 611 612
 private:
  v8::Isolate* isolate_;
};

613

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

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

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

667 668 669 670
 private:
  v8::Isolate* isolate_;
};

671

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

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

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

738 739 740 741 742
 private:
  v8::Isolate* isolate1_;
  v8::Isolate* isolate2_;
};

743

744 745
// Lock two isolates and unlock one of them.
TEST(LockAndUnlockDifferentIsolates) {
746 747 748 749
  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);
750 751 752 753 754 755 756 757 758
  LockAndUnlockDifferentIsolatesThread thread(isolate1, isolate2);
  thread.Start();
  thread.Join();
  isolate2->Dispose();
  isolate1->Dispose();
}

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

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

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

799

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

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

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

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

859

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


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


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)
  {}

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

931 932 933 934 935
 private:
  int count_;
  const char** extension_names_;
};

936

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

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