LCOV - code coverage report
Current view: top level - test/cctest - test-lockers.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 393 393 100.0 %
Date: 2019-01-20 Functions: 59 67 88.1 %

          Line data    Source code
       1             : // Copyright 2007-2011 the V8 project authors. All rights reserved.
       2             : // Redistribution and use in source and binary forms, with or without
       3             : // modification, are permitted provided that the following conditions are
       4             : // met:
       5             : //
       6             : //     * Redistributions of source code must retain the above copyright
       7             : //       notice, this list of conditions and the following disclaimer.
       8             : //     * Redistributions in binary form must reproduce the above
       9             : //       copyright notice, this list of conditions and the following
      10             : //       disclaimer in the documentation and/or other materials provided
      11             : //       with the distribution.
      12             : //     * Neither the name of Google Inc. nor the names of its
      13             : //       contributors may be used to endorse or promote products derived
      14             : //       from this software without specific prior written permission.
      15             : //
      16             : // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
      17             : // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
      18             : // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
      19             : // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
      20             : // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
      21             : // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
      22             : // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
      23             : // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
      24             : // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
      25             : // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
      26             : // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
      27             : 
      28             : #include <limits.h>
      29             : 
      30             : #include <memory>
      31             : 
      32             : #include "src/v8.h"
      33             : 
      34             : #include "src/base/platform/platform.h"
      35             : #include "src/compilation-cache.h"
      36             : #include "src/execution.h"
      37             : #include "src/isolate.h"
      38             : #include "src/objects-inl.h"
      39             : #include "src/unicode-inl.h"
      40             : #include "src/utils.h"
      41             : #include "test/cctest/cctest.h"
      42             : 
      43             : namespace {
      44             : 
      45          15 : class DeoptimizeCodeThread : public v8::base::Thread {
      46             :  public:
      47          15 :   DeoptimizeCodeThread(v8::Isolate* isolate, v8::Local<v8::Context> context,
      48             :                        const char* trigger)
      49             :       : Thread(Options("DeoptimizeCodeThread")),
      50             :         isolate_(isolate),
      51             :         context_(isolate, context),
      52          30 :         source_(trigger) {}
      53             : 
      54          15 :   void Run() override {
      55          15 :     v8::Locker locker(isolate_);
      56          15 :     isolate_->Enter();
      57          30 :     v8::HandleScope handle_scope(isolate_);
      58             :     v8::Local<v8::Context> context =
      59          15 :         v8::Local<v8::Context>::New(isolate_, context_);
      60             :     v8::Context::Scope context_scope(context);
      61          15 :     CHECK_EQ(isolate_, v8::Isolate::GetCurrent());
      62             :     // This code triggers deoptimization of some function that will be
      63             :     // used in a different thread.
      64          15 :     CompileRun(source_);
      65          30 :     isolate_->Exit();
      66          15 :   }
      67             : 
      68             :  private:
      69             :   v8::Isolate* isolate_;
      70             :   v8::Persistent<v8::Context> context_;
      71             :   // The code that triggers the deoptimization.
      72             :   const char* source_;
      73             : };
      74             : 
      75           5 : void UnlockForDeoptimization(const v8::FunctionCallbackInfo<v8::Value>& args) {
      76           5 :   v8::Isolate* isolate = v8::Isolate::GetCurrent();
      77             :   // Gets the pointer to the thread that will trigger the deoptimization of the
      78             :   // code.
      79             :   DeoptimizeCodeThread* deoptimizer =
      80             :       reinterpret_cast<DeoptimizeCodeThread*>(isolate->GetData(0));
      81             :   {
      82             :     // Exits and unlocks the isolate.
      83           5 :     isolate->Exit();
      84             :     v8::Unlocker unlocker(isolate);
      85             :     // Starts the deoptimizing thread.
      86           5 :     deoptimizer->Start();
      87             :     // Waits for deoptimization to finish.
      88           5 :     deoptimizer->Join();
      89             :   }
      90             :   // The deoptimizing thread has finished its work, and the isolate
      91             :   // will now be used by the current thread.
      92           5 :   isolate->Enter();
      93           5 : }
      94             : 
      95          40 : void UnlockForDeoptimizationIfReady(
      96             :     const v8::FunctionCallbackInfo<v8::Value>& args) {
      97          40 :   v8::Isolate* isolate = v8::Isolate::GetCurrent();
      98             :   bool* ready_to_deoptimize = reinterpret_cast<bool*>(isolate->GetData(1));
      99          40 :   if (*ready_to_deoptimize) {
     100             :     // The test should enter here only once, so put the flag back to false.
     101          10 :     *ready_to_deoptimize = false;
     102             :     // Gets the pointer to the thread that will trigger the deoptimization of
     103             :     // the code.
     104             :     DeoptimizeCodeThread* deoptimizer =
     105             :         reinterpret_cast<DeoptimizeCodeThread*>(isolate->GetData(0));
     106             :     {
     107             :       // Exits and unlocks the thread.
     108          10 :       isolate->Exit();
     109             :       v8::Unlocker unlocker(isolate);
     110             :       // Starts the thread that deoptimizes the function.
     111          10 :       deoptimizer->Start();
     112             :       // Waits for the deoptimizing thread to finish.
     113          10 :       deoptimizer->Join();
     114             :     }
     115             :     // The deoptimizing thread has finished its work, and the isolate
     116             :     // will now be used by the current thread.
     117          10 :     isolate->Enter();
     118             :   }
     119          40 : }
     120             : }  // namespace
     121             : 
     122             : namespace v8 {
     123             : namespace internal {
     124             : namespace test_lockers {
     125             : 
     126       28342 : TEST(LazyDeoptimizationMultithread) {
     127           5 :   i::FLAG_allow_natives_syntax = true;
     128             :   v8::Isolate::CreateParams create_params;
     129           5 :   create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
     130           5 :   v8::Isolate* isolate = v8::Isolate::New(create_params);
     131             :   {
     132             :     v8::Locker locker(isolate);
     133             :     v8::Isolate::Scope isolate_scope(isolate);
     134          10 :     v8::HandleScope scope(isolate);
     135           5 :     v8::Local<v8::Context> context = v8::Context::New(isolate);
     136             :     const char* trigger_deopt = "obj = { y: 0, x: 1 };";
     137             : 
     138             :     // We use the isolate to pass arguments to the UnlockForDeoptimization
     139             :     // function. Namely, we pass a pointer to the deoptimizing thread.
     140           5 :     DeoptimizeCodeThread deoptimize_thread(isolate, context, trigger_deopt);
     141             :     isolate->SetData(0, &deoptimize_thread);
     142             :     v8::Context::Scope context_scope(context);
     143             : 
     144             :     // Create the function templace for C++ code that is invoked from
     145             :     // JavaScript code.
     146             :     Local<v8::FunctionTemplate> fun_templ =
     147           5 :         v8::FunctionTemplate::New(isolate, UnlockForDeoptimization);
     148           5 :     Local<Function> fun = fun_templ->GetFunction(context).ToLocalChecked();
     149          20 :     CHECK(context->Global()
     150             :               ->Set(context, v8_str("unlock_for_deoptimization"), fun)
     151             :               .FromJust());
     152             : 
     153             :     // Optimizes a function f, which will be deoptimized in another
     154             :     // thread.
     155             :     CompileRun(
     156             :         "var b = false; var obj = { x: 1 };"
     157             :         "function f() { g(); return obj.x; }"
     158             :         "function g() { if (b) { unlock_for_deoptimization(); } }"
     159             :         "%NeverOptimizeFunction(g);"
     160             :         "f(); f(); %OptimizeFunctionOnNextCall(f);"
     161             :         "f();");
     162             : 
     163             :     // Trigger the unlocking.
     164             :     Local<Value> v = CompileRun("b = true; f();");
     165             : 
     166             :     // Once the isolate has been unlocked, the thread will wait for the
     167             :     // other thread to finish its task. Once this happens, this thread
     168             :     // continues with its execution, that is, with the execution of the
     169             :     // function g, which then returns to f. The function f should have
     170             :     // also been deoptimized. If the replacement did not happen on this
     171             :     // thread's stack, then the test will fail here.
     172           5 :     CHECK(v->IsNumber());
     173          15 :     CHECK_EQ(1, static_cast<int>(v->NumberValue(context).FromJust()));
     174             :   }
     175           5 :   isolate->Dispose();
     176           5 : }
     177             : 
     178       28342 : TEST(LazyDeoptimizationMultithreadWithNatives) {
     179           5 :   i::FLAG_allow_natives_syntax = true;
     180             :   v8::Isolate::CreateParams create_params;
     181           5 :   create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
     182           5 :   v8::Isolate* isolate = v8::Isolate::New(create_params);
     183             :   {
     184             :     v8::Locker locker(isolate);
     185             :     v8::Isolate::Scope isolate_scope(isolate);
     186          10 :     v8::HandleScope scope(isolate);
     187           5 :     v8::Local<v8::Context> context = v8::Context::New(isolate);
     188             :     const char* trigger_deopt = "%DeoptimizeFunction(f);";
     189             : 
     190             :     // We use the isolate to pass arguments to the UnlockForDeoptimization
     191             :     // function. Namely, we pass a pointer to the deoptimizing thread.
     192           5 :     DeoptimizeCodeThread deoptimize_thread(isolate, context, trigger_deopt);
     193             :     isolate->SetData(0, &deoptimize_thread);
     194           5 :     bool ready_to_deopt = false;
     195             :     isolate->SetData(1, &ready_to_deopt);
     196             :     v8::Context::Scope context_scope(context);
     197             : 
     198             :     // Create the function templace for C++ code that is invoked from
     199             :     // JavaScript code.
     200             :     Local<v8::FunctionTemplate> fun_templ =
     201           5 :         v8::FunctionTemplate::New(isolate, UnlockForDeoptimizationIfReady);
     202             :     Local<Function> fun = fun_templ->GetFunction(context).ToLocalChecked();
     203          20 :     CHECK(context->Global()
     204             :               ->Set(context, v8_str("unlock_for_deoptimization"), fun)
     205             :               .FromJust());
     206             : 
     207             :     // Optimizes a function f, which will be deoptimized in another
     208             :     // thread.
     209             :     CompileRun(
     210             :         "var obj = { x: 1 };"
     211             :         "function f() { g(); return obj.x;}"
     212             :         "function g() { "
     213             :         "  unlock_for_deoptimization(); }"
     214             :         "%NeverOptimizeFunction(g);"
     215             :         "f(); f(); %OptimizeFunctionOnNextCall(f);");
     216             : 
     217             :     // Trigger the unlocking.
     218           5 :     ready_to_deopt = true;
     219             :     isolate->SetData(1, &ready_to_deopt);
     220             :     Local<Value> v = CompileRun("f();");
     221             : 
     222             :     // Once the isolate has been unlocked, the thread will wait for the
     223             :     // other thread to finish its task. Once this happens, this thread
     224             :     // continues with its execution, that is, with the execution of the
     225             :     // function g, which then returns to f. The function f should have
     226             :     // also been deoptimized. Otherwise, the test will fail here.
     227           5 :     CHECK(v->IsNumber());
     228          15 :     CHECK_EQ(1, static_cast<int>(v->NumberValue(context).FromJust()));
     229             :   }
     230           5 :   isolate->Dispose();
     231           5 : }
     232             : 
     233       28342 : TEST(EagerDeoptimizationMultithread) {
     234           5 :   i::FLAG_allow_natives_syntax = true;
     235             :   v8::Isolate::CreateParams create_params;
     236           5 :   create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
     237           5 :   v8::Isolate* isolate = v8::Isolate::New(create_params);
     238             :   {
     239             :     v8::Locker locker(isolate);
     240             :     v8::Isolate::Scope isolate_scope(isolate);
     241          10 :     v8::HandleScope scope(isolate);
     242           5 :     v8::Local<v8::Context> context = v8::Context::New(isolate);
     243             :     const char* trigger_deopt = "f({y: 0, x: 1});";
     244             : 
     245             :     // We use the isolate to pass arguments to the UnlockForDeoptimization
     246             :     // function. Namely, we pass a pointer to the deoptimizing thread.
     247           5 :     DeoptimizeCodeThread deoptimize_thread(isolate, context, trigger_deopt);
     248             :     isolate->SetData(0, &deoptimize_thread);
     249           5 :     bool ready_to_deopt = false;
     250             :     isolate->SetData(1, &ready_to_deopt);
     251             :     v8::Context::Scope context_scope(context);
     252             : 
     253             :     // Create the function templace for C++ code that is invoked from
     254             :     // JavaScript code.
     255             :     Local<v8::FunctionTemplate> fun_templ =
     256           5 :         v8::FunctionTemplate::New(isolate, UnlockForDeoptimizationIfReady);
     257           5 :     Local<Function> fun = fun_templ->GetFunction(context).ToLocalChecked();
     258          20 :     CHECK(context->Global()
     259             :               ->Set(context, v8_str("unlock_for_deoptimization"), fun)
     260             :               .FromJust());
     261             : 
     262             :     // Optimizes a function f, which will be deoptimized by another thread.
     263             :     CompileRun(
     264             :         "function f(obj) { unlock_for_deoptimization(); return obj.x; }"
     265             :         "f({x: 1}); f({x: 1});"
     266             :         "%OptimizeFunctionOnNextCall(f);"
     267             :         "f({x: 1});");
     268             : 
     269             :     // Trigger the unlocking.
     270           5 :     ready_to_deopt = true;
     271             :     isolate->SetData(1, &ready_to_deopt);
     272             :     Local<Value> v = CompileRun("f({x: 1});");
     273             : 
     274             :     // Once the isolate has been unlocked, the thread will wait for the
     275             :     // other thread to finish its task. Once this happens, this thread
     276             :     // continues with its execution, that is, with the execution of the
     277             :     // function g, which then returns to f. The function f should have
     278             :     // also been deoptimized. Otherwise, the test will fail here.
     279           5 :     CHECK(v->IsNumber());
     280          15 :     CHECK_EQ(1, static_cast<int>(v->NumberValue(context).FromJust()));
     281             :   }
     282           5 :   isolate->Dispose();
     283           5 : }
     284             : 
     285             : // Migrating an isolate
     286           5 : class KangarooThread : public v8::base::Thread {
     287             :  public:
     288           5 :   KangarooThread(v8::Isolate* isolate, v8::Local<v8::Context> context)
     289             :       : Thread(Options("KangarooThread")),
     290             :         isolate_(isolate),
     291          10 :         context_(isolate, context) {}
     292             : 
     293           5 :   void Run() override {
     294             :     {
     295           5 :       v8::Locker locker(isolate_);
     296           5 :       v8::Isolate::Scope isolate_scope(isolate_);
     297           5 :       CHECK_EQ(isolate_, v8::Isolate::GetCurrent());
     298          10 :       v8::HandleScope scope(isolate_);
     299             :       v8::Local<v8::Context> context =
     300           5 :           v8::Local<v8::Context>::New(isolate_, context_);
     301             :       v8::Context::Scope context_scope(context);
     302             :       Local<Value> v = CompileRun("getValue()");
     303           5 :       CHECK(v->IsNumber());
     304          15 :       CHECK_EQ(30, static_cast<int>(v->NumberValue(context).FromJust()));
     305             :     }
     306             :     {
     307           5 :       v8::Locker locker(isolate_);
     308           5 :       v8::Isolate::Scope isolate_scope(isolate_);
     309          10 :       v8::HandleScope scope(isolate_);
     310             :       v8::Local<v8::Context> context =
     311           5 :           v8::Local<v8::Context>::New(isolate_, context_);
     312             :       v8::Context::Scope context_scope(context);
     313             :       Local<Value> v = CompileRun("getValue()");
     314           5 :       CHECK(v->IsNumber());
     315          15 :       CHECK_EQ(30, static_cast<int>(v->NumberValue(context).FromJust()));
     316             :     }
     317           5 :     isolate_->Dispose();
     318           5 :   }
     319             : 
     320             :  private:
     321             :   v8::Isolate* isolate_;
     322             :   v8::Persistent<v8::Context> context_;
     323             : };
     324             : 
     325             : 
     326             : // Migrates an isolate from one thread to another
     327       28342 : TEST(KangarooIsolates) {
     328             :   v8::Isolate::CreateParams create_params;
     329           5 :   create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
     330           5 :   v8::Isolate* isolate = v8::Isolate::New(create_params);
     331             :   std::unique_ptr<KangarooThread> thread1;
     332             :   {
     333             :     v8::Locker locker(isolate);
     334             :     v8::Isolate::Scope isolate_scope(isolate);
     335          10 :     v8::HandleScope handle_scope(isolate);
     336           5 :     v8::Local<v8::Context> context = v8::Context::New(isolate);
     337             :     v8::Context::Scope context_scope(context);
     338           5 :     CHECK_EQ(isolate, v8::Isolate::GetCurrent());
     339             :     CompileRun("function getValue() { return 30; }");
     340          10 :     thread1.reset(new KangarooThread(isolate, context));
     341             :   }
     342           5 :   thread1->Start();
     343           5 :   thread1->Join();
     344           5 : }
     345             : 
     346             : 
     347        7520 : static void CalcFibAndCheck(v8::Local<v8::Context> context) {
     348             :   Local<Value> v = CompileRun("function fib(n) {"
     349             :                               "  if (n <= 2) return 1;"
     350             :                               "  return fib(n-1) + fib(n-2);"
     351             :                               "}"
     352             :                               "fib(10)");
     353        7520 :   CHECK(v->IsNumber());
     354       15040 :   CHECK_EQ(55, static_cast<int>(v->NumberValue(context).FromJust()));
     355        7520 : }
     356             : 
     357             : class JoinableThread {
     358             :  public:
     359        5210 :   explicit JoinableThread(const char* name)
     360             :     : name_(name),
     361             :       semaphore_(0),
     362        5210 :       thread_(this) {
     363        5210 :   }
     364             : 
     365       10420 :   virtual ~JoinableThread() = default;
     366             : 
     367             :   void Start() {
     368        5210 :     thread_.Start();
     369             :   }
     370             : 
     371             :   void Join() {
     372        5210 :     semaphore_.Wait();
     373        5210 :     thread_.Join();
     374             :   }
     375             : 
     376             :   virtual void Run() = 0;
     377             : 
     378             :  private:
     379        5210 :   class ThreadWithSemaphore : public v8::base::Thread {
     380             :    public:
     381             :     explicit ThreadWithSemaphore(JoinableThread* joinable_thread)
     382             :         : Thread(Options(joinable_thread->name_)),
     383       10420 :           joinable_thread_(joinable_thread) {}
     384             : 
     385        5204 :     void Run() override {
     386        5204 :       joinable_thread_->Run();
     387        5210 :       joinable_thread_->semaphore_.Signal();
     388        5210 :     }
     389             : 
     390             :    private:
     391             :     JoinableThread* joinable_thread_;
     392             :   };
     393             : 
     394             :   const char* name_;
     395             :   v8::base::Semaphore semaphore_;
     396             :   ThreadWithSemaphore thread_;
     397             : 
     398             :   friend class ThreadWithSemaphore;
     399             : 
     400             :   DISALLOW_COPY_AND_ASSIGN(JoinableThread);
     401             : };
     402             : 
     403             : 
     404        2500 : class IsolateLockingThreadWithLocalContext : public JoinableThread {
     405             :  public:
     406             :   explicit IsolateLockingThreadWithLocalContext(v8::Isolate* isolate)
     407             :     : JoinableThread("IsolateLockingThread"),
     408        1000 :       isolate_(isolate) {
     409             :   }
     410             : 
     411        1000 :   void Run() override {
     412        1000 :     v8::Locker locker(isolate_);
     413        1000 :     v8::Isolate::Scope isolate_scope(isolate_);
     414        2000 :     v8::HandleScope handle_scope(isolate_);
     415        2000 :     LocalContext local_context(isolate_);
     416        1000 :     CHECK_EQ(isolate_, v8::Isolate::GetCurrent());
     417        2000 :     CalcFibAndCheck(local_context.local());
     418        1000 :   }
     419             :  private:
     420             :   v8::Isolate* isolate_;
     421             : };
     422             : 
     423          40 : static void StartJoinAndDeleteThreads(
     424             :     const std::vector<JoinableThread*>& threads) {
     425        3780 :   for (const auto& thread : threads) {
     426        3700 :     thread->Start();
     427             :   }
     428        3780 :   for (const auto& thread : threads) {
     429        3700 :     thread->Join();
     430             :   }
     431        3780 :   for (const auto& thread : threads) {
     432        3700 :     delete thread;
     433             :   }
     434          40 : }
     435             : 
     436             : 
     437             : // Run many threads all locking on the same isolate
     438       28342 : TEST(IsolateLockingStress) {
     439           5 :   i::FLAG_always_opt = false;
     440             : #if V8_TARGET_ARCH_MIPS
     441             :   const int kNThreads = 50;
     442             : #else
     443             :   const int kNThreads = 100;
     444             : #endif
     445             :   std::vector<JoinableThread*> threads;
     446           5 :   threads.reserve(kNThreads);
     447             :   v8::Isolate::CreateParams create_params;
     448           5 :   create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
     449           5 :   v8::Isolate* isolate = v8::Isolate::New(create_params);
     450         505 :   for (int i = 0; i < kNThreads; i++) {
     451        1500 :     threads.push_back(new IsolateLockingThreadWithLocalContext(isolate));
     452             :   }
     453           5 :   StartJoinAndDeleteThreads(threads);
     454           5 :   isolate->Dispose();
     455           5 : }
     456             : 
     457             : 
     458        1500 : class IsolateNestedLockingThread : public JoinableThread {
     459             :  public:
     460             :   explicit IsolateNestedLockingThread(v8::Isolate* isolate)
     461         500 :     : JoinableThread("IsolateNestedLocking"), isolate_(isolate) {
     462             :   }
     463         500 :   void Run() override {
     464         500 :     v8::Locker lock(isolate_);
     465         500 :     v8::Isolate::Scope isolate_scope(isolate_);
     466        1000 :     v8::HandleScope handle_scope(isolate_);
     467        1000 :     LocalContext local_context(isolate_);
     468             :     {
     469         500 :       v8::Locker another_lock(isolate_);
     470         500 :       CalcFibAndCheck(local_context.local());
     471             :     }
     472             :     {
     473         500 :       v8::Locker another_lock(isolate_);
     474         500 :       CalcFibAndCheck(local_context.local());
     475         500 :     }
     476         500 :   }
     477             :  private:
     478             :   v8::Isolate* isolate_;
     479             : };
     480             : 
     481             : 
     482             : // Run  many threads with nested locks
     483       28342 : TEST(IsolateNestedLocking) {
     484           5 :   i::FLAG_always_opt = false;
     485             : #if V8_TARGET_ARCH_MIPS
     486             :   const int kNThreads = 50;
     487             : #else
     488             :   const int kNThreads = 100;
     489             : #endif
     490             :   v8::Isolate::CreateParams create_params;
     491           5 :   create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
     492           5 :   v8::Isolate* isolate = v8::Isolate::New(create_params);
     493             :   std::vector<JoinableThread*> threads;
     494           5 :   threads.reserve(kNThreads);
     495         505 :   for (int i = 0; i < kNThreads; i++) {
     496        1500 :     threads.push_back(new IsolateNestedLockingThread(isolate));
     497             :   }
     498           5 :   StartJoinAndDeleteThreads(threads);
     499           5 :   isolate->Dispose();
     500           5 : }
     501             : 
     502             : 
     503        1500 : class SeparateIsolatesLocksNonexclusiveThread : public JoinableThread {
     504             :  public:
     505             :   SeparateIsolatesLocksNonexclusiveThread(v8::Isolate* isolate1,
     506             :                                           v8::Isolate* isolate2)
     507             :     : JoinableThread("SeparateIsolatesLocksNonexclusiveThread"),
     508         500 :       isolate1_(isolate1), isolate2_(isolate2) {
     509             :   }
     510             : 
     511         500 :   void Run() override {
     512         500 :     v8::Locker lock(isolate1_);
     513         500 :     v8::Isolate::Scope isolate_scope(isolate1_);
     514        1000 :     v8::HandleScope handle_scope(isolate1_);
     515        1000 :     LocalContext local_context(isolate1_);
     516             : 
     517        1000 :     IsolateLockingThreadWithLocalContext threadB(isolate2_);
     518             :     threadB.Start();
     519         500 :     CalcFibAndCheck(local_context.local());
     520         500 :     threadB.Join();
     521         500 :   }
     522             :  private:
     523             :   v8::Isolate* isolate1_;
     524             :   v8::Isolate* isolate2_;
     525             : };
     526             : 
     527             : 
     528             : // Run parallel threads that lock and access different isolates in parallel
     529       28342 : TEST(SeparateIsolatesLocksNonexclusive) {
     530           5 :   i::FLAG_always_opt = false;
     531             : #if V8_TARGET_ARCH_ARM || V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_S390
     532             :   const int kNThreads = 50;
     533             : #else
     534             :   const int kNThreads = 100;
     535             : #endif
     536             :   v8::Isolate::CreateParams create_params;
     537           5 :   create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
     538           5 :   v8::Isolate* isolate1 = v8::Isolate::New(create_params);
     539           5 :   v8::Isolate* isolate2 = v8::Isolate::New(create_params);
     540             :   std::vector<JoinableThread*> threads;
     541           5 :   threads.reserve(kNThreads);
     542         505 :   for (int i = 0; i < kNThreads; i++) {
     543             :     threads.push_back(
     544        1500 :         new SeparateIsolatesLocksNonexclusiveThread(isolate1, isolate2));
     545             :   }
     546           5 :   StartJoinAndDeleteThreads(threads);
     547           5 :   isolate2->Dispose();
     548           5 :   isolate1->Dispose();
     549           5 : }
     550             : 
     551        2015 : class LockIsolateAndCalculateFibSharedContextThread : public JoinableThread {
     552             :  public:
     553        1005 :   explicit LockIsolateAndCalculateFibSharedContextThread(
     554             :       v8::Isolate* isolate, v8::Local<v8::Context> context)
     555             :       : JoinableThread("LockIsolateAndCalculateFibThread"),
     556             :         isolate_(isolate),
     557        2010 :         context_(isolate, context) {}
     558             : 
     559        1005 :   void Run() override {
     560        1005 :     v8::Locker lock(isolate_);
     561        1005 :     v8::Isolate::Scope isolate_scope(isolate_);
     562        2010 :     v8::HandleScope handle_scope(isolate_);
     563             :     v8::Local<v8::Context> context =
     564        1005 :         v8::Local<v8::Context>::New(isolate_, context_);
     565             :     v8::Context::Scope context_scope(context);
     566        2010 :     CalcFibAndCheck(context);
     567        1005 :   }
     568             :  private:
     569             :   v8::Isolate* isolate_;
     570             :   v8::Persistent<v8::Context> context_;
     571             : };
     572             : 
     573        1500 : class LockerUnlockerThread : public JoinableThread {
     574             :  public:
     575             :   explicit LockerUnlockerThread(v8::Isolate* isolate)
     576             :     : JoinableThread("LockerUnlockerThread"),
     577         500 :       isolate_(isolate) {
     578             :   }
     579             : 
     580         499 :   void Run() override {
     581         499 :     isolate_->DiscardThreadSpecificMetadata();  // No-op
     582             :     {
     583         497 :       v8::Locker lock(isolate_);
     584         500 :       v8::Isolate::Scope isolate_scope(isolate_);
     585        1000 :       v8::HandleScope handle_scope(isolate_);
     586         500 :       v8::Local<v8::Context> context = v8::Context::New(isolate_);
     587             :       {
     588             :         v8::Context::Scope context_scope(context);
     589         500 :         CalcFibAndCheck(context);
     590             :       }
     591             :       {
     592         500 :         LockIsolateAndCalculateFibSharedContextThread thread(isolate_, context);
     593         500 :         isolate_->Exit();
     594        1000 :         v8::Unlocker unlocker(isolate_);
     595             :         thread.Start();
     596         500 :         thread.Join();
     597             :       }
     598         500 :       isolate_->Enter();
     599             :       {
     600             :         v8::Context::Scope context_scope(context);
     601         500 :         CalcFibAndCheck(context);
     602         500 :       }
     603             :     }
     604         500 :     isolate_->DiscardThreadSpecificMetadata();
     605         500 :     isolate_->DiscardThreadSpecificMetadata();  // No-op
     606         500 :   }
     607             : 
     608             :  private:
     609             :   v8::Isolate* isolate_;
     610             : };
     611             : 
     612             : 
     613             : // Use unlocker inside of a Locker, multiple threads.
     614       28342 : TEST(LockerUnlocker) {
     615           5 :   i::FLAG_always_opt = false;
     616             : #if V8_TARGET_ARCH_ARM || V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_S390
     617             :   const int kNThreads = 50;
     618             : #else
     619             :   const int kNThreads = 100;
     620             : #endif
     621             :   std::vector<JoinableThread*> threads;
     622           5 :   threads.reserve(kNThreads);
     623             :   v8::Isolate::CreateParams create_params;
     624           5 :   create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
     625           5 :   v8::Isolate* isolate = v8::Isolate::New(create_params);
     626         505 :   for (int i = 0; i < kNThreads; i++) {
     627        1500 :     threads.push_back(new LockerUnlockerThread(isolate));
     628             :   }
     629           5 :   StartJoinAndDeleteThreads(threads);
     630           5 :   isolate->Dispose();
     631           5 : }
     632             : 
     633        1500 : class LockTwiceAndUnlockThread : public JoinableThread {
     634             :  public:
     635             :   explicit LockTwiceAndUnlockThread(v8::Isolate* isolate)
     636             :     : JoinableThread("LockTwiceAndUnlockThread"),
     637         500 :       isolate_(isolate) {
     638             :   }
     639             : 
     640         496 :   void Run() override {
     641         496 :     v8::Locker lock(isolate_);
     642         500 :     v8::Isolate::Scope isolate_scope(isolate_);
     643        1000 :     v8::HandleScope handle_scope(isolate_);
     644         500 :     v8::Local<v8::Context> context = v8::Context::New(isolate_);
     645             :     {
     646             :       v8::Context::Scope context_scope(context);
     647         500 :       CalcFibAndCheck(context);
     648             :     }
     649             :     {
     650         500 :       v8::Locker second_lock(isolate_);
     651             :       {
     652         500 :         LockIsolateAndCalculateFibSharedContextThread thread(isolate_, context);
     653         500 :         isolate_->Exit();
     654        1000 :         v8::Unlocker unlocker(isolate_);
     655             :         thread.Start();
     656         500 :         thread.Join();
     657         500 :       }
     658             :     }
     659         500 :     isolate_->Enter();
     660             :     {
     661             :       v8::Context::Scope context_scope(context);
     662         500 :       CalcFibAndCheck(context);
     663         500 :     }
     664         500 :   }
     665             : 
     666             :  private:
     667             :   v8::Isolate* isolate_;
     668             : };
     669             : 
     670             : 
     671             : // Use Unlocker inside two Lockers.
     672       28342 : TEST(LockTwiceAndUnlock) {
     673           5 :   i::FLAG_always_opt = false;
     674             : #if V8_TARGET_ARCH_ARM || V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_S390
     675             :   const int kNThreads = 50;
     676             : #else
     677             :   const int kNThreads = 100;
     678             : #endif
     679             :   std::vector<JoinableThread*> threads;
     680           5 :   threads.reserve(kNThreads);
     681             :   v8::Isolate::CreateParams create_params;
     682           5 :   create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
     683           5 :   v8::Isolate* isolate = v8::Isolate::New(create_params);
     684         505 :   for (int i = 0; i < kNThreads; i++) {
     685        1500 :     threads.push_back(new LockTwiceAndUnlockThread(isolate));
     686             :   }
     687           5 :   StartJoinAndDeleteThreads(threads);
     688           5 :   isolate->Dispose();
     689           5 : }
     690             : 
     691          10 : class LockAndUnlockDifferentIsolatesThread : public JoinableThread {
     692             :  public:
     693             :   LockAndUnlockDifferentIsolatesThread(v8::Isolate* isolate1,
     694             :                                        v8::Isolate* isolate2)
     695             :     : JoinableThread("LockAndUnlockDifferentIsolatesThread"),
     696             :       isolate1_(isolate1),
     697           5 :       isolate2_(isolate2) {
     698             :   }
     699             : 
     700           5 :   void Run() override {
     701             :     std::unique_ptr<LockIsolateAndCalculateFibSharedContextThread> thread;
     702          10 :     v8::Locker lock1(isolate1_);
     703           5 :     CHECK(v8::Locker::IsLocked(isolate1_));
     704           5 :     CHECK(!v8::Locker::IsLocked(isolate2_));
     705             :     {
     706           5 :       v8::Isolate::Scope isolate_scope(isolate1_);
     707          10 :       v8::HandleScope handle_scope(isolate1_);
     708           5 :       v8::Local<v8::Context> context1 = v8::Context::New(isolate1_);
     709             :       {
     710             :         v8::Context::Scope context_scope(context1);
     711           5 :         CalcFibAndCheck(context1);
     712             :       }
     713             :       thread.reset(new LockIsolateAndCalculateFibSharedContextThread(isolate1_,
     714           5 :                                                                      context1));
     715             :     }
     716          10 :     v8::Locker lock2(isolate2_);
     717           5 :     CHECK(v8::Locker::IsLocked(isolate1_));
     718           5 :     CHECK(v8::Locker::IsLocked(isolate2_));
     719             :     {
     720           5 :       v8::Isolate::Scope isolate_scope(isolate2_);
     721          10 :       v8::HandleScope handle_scope(isolate2_);
     722           5 :       v8::Local<v8::Context> context2 = v8::Context::New(isolate2_);
     723             :       {
     724             :         v8::Context::Scope context_scope(context2);
     725           5 :         CalcFibAndCheck(context2);
     726             :       }
     727          10 :       v8::Unlocker unlock1(isolate1_);
     728           5 :       CHECK(!v8::Locker::IsLocked(isolate1_));
     729           5 :       CHECK(v8::Locker::IsLocked(isolate2_));
     730             :       v8::Context::Scope context_scope(context2);
     731             :       thread->Start();
     732           5 :       CalcFibAndCheck(context2);
     733             :       thread->Join();
     734             :     }
     735           5 :   }
     736             : 
     737             :  private:
     738             :   v8::Isolate* isolate1_;
     739             :   v8::Isolate* isolate2_;
     740             : };
     741             : 
     742             : 
     743             : // Lock two isolates and unlock one of them.
     744       28342 : TEST(LockAndUnlockDifferentIsolates) {
     745             :   v8::Isolate::CreateParams create_params;
     746           5 :   create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
     747           5 :   v8::Isolate* isolate1 = v8::Isolate::New(create_params);
     748           5 :   v8::Isolate* isolate2 = v8::Isolate::New(create_params);
     749             :   LockAndUnlockDifferentIsolatesThread thread(isolate1, isolate2);
     750             :   thread.Start();
     751             :   thread.Join();
     752           5 :   isolate2->Dispose();
     753           5 :   isolate1->Dispose();
     754           5 : }
     755             : 
     756        1500 : class LockUnlockLockThread : public JoinableThread {
     757             :  public:
     758         500 :   LockUnlockLockThread(v8::Isolate* isolate, v8::Local<v8::Context> context)
     759             :       : JoinableThread("LockUnlockLockThread"),
     760             :         isolate_(isolate),
     761        1000 :         context_(isolate, context) {}
     762             : 
     763         500 :   void Run() override {
     764         500 :     v8::Locker lock1(isolate_);
     765         500 :     CHECK(v8::Locker::IsLocked(isolate_));
     766         500 :     CHECK(!v8::Locker::IsLocked(CcTest::isolate()));
     767             :     {
     768         500 :       v8::Isolate::Scope isolate_scope(isolate_);
     769        1000 :       v8::HandleScope handle_scope(isolate_);
     770             :       v8::Local<v8::Context> context =
     771         500 :           v8::Local<v8::Context>::New(isolate_, context_);
     772             :       v8::Context::Scope context_scope(context);
     773         500 :       CalcFibAndCheck(context);
     774             :     }
     775             :     {
     776         500 :       v8::Unlocker unlock1(isolate_);
     777         500 :       CHECK(!v8::Locker::IsLocked(isolate_));
     778         500 :       CHECK(!v8::Locker::IsLocked(CcTest::isolate()));
     779             :       {
     780         500 :         v8::Locker lock2(isolate_);
     781         500 :         v8::Isolate::Scope isolate_scope(isolate_);
     782        1000 :         v8::HandleScope handle_scope(isolate_);
     783         500 :         CHECK(v8::Locker::IsLocked(isolate_));
     784         500 :         CHECK(!v8::Locker::IsLocked(CcTest::isolate()));
     785             :         v8::Local<v8::Context> context =
     786         500 :             v8::Local<v8::Context>::New(isolate_, context_);
     787             :         v8::Context::Scope context_scope(context);
     788        1000 :         CalcFibAndCheck(context);
     789         499 :       }
     790         500 :     }
     791         500 :   }
     792             : 
     793             :  private:
     794             :   v8::Isolate* isolate_;
     795             :   v8::Persistent<v8::Context> context_;
     796             : };
     797             : 
     798             : 
     799             : // Locker inside an Unlocker inside a Locker.
     800       28342 : TEST(LockUnlockLockMultithreaded) {
     801             : #if V8_TARGET_ARCH_MIPS
     802             :   const int kNThreads = 50;
     803             : #else
     804             :   const int kNThreads = 100;
     805             : #endif
     806             :   v8::Isolate::CreateParams create_params;
     807           5 :   create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
     808           5 :   v8::Isolate* isolate = v8::Isolate::New(create_params);
     809             :   std::vector<JoinableThread*> threads;
     810           5 :   threads.reserve(kNThreads);
     811             :   {
     812             :     v8::Locker locker_(isolate);
     813             :     v8::Isolate::Scope isolate_scope(isolate);
     814          10 :     v8::HandleScope handle_scope(isolate);
     815           5 :     v8::Local<v8::Context> context = v8::Context::New(isolate);
     816         505 :     for (int i = 0; i < kNThreads; i++) {
     817        1000 :       threads.push_back(new LockUnlockLockThread(isolate, context));
     818           5 :     }
     819             :   }
     820           5 :   StartJoinAndDeleteThreads(threads);
     821           5 :   isolate->Dispose();
     822           5 : }
     823             : 
     824        1500 : class LockUnlockLockDefaultIsolateThread : public JoinableThread {
     825             :  public:
     826         500 :   explicit LockUnlockLockDefaultIsolateThread(v8::Local<v8::Context> context)
     827             :       : JoinableThread("LockUnlockLockDefaultIsolateThread"),
     828        1000 :         context_(CcTest::isolate(), context) {}
     829             : 
     830         500 :   void Run() override {
     831         500 :     v8::Locker lock1(CcTest::isolate());
     832             :     {
     833         500 :       v8::Isolate::Scope isolate_scope(CcTest::isolate());
     834        1000 :       v8::HandleScope handle_scope(CcTest::isolate());
     835             :       v8::Local<v8::Context> context =
     836         500 :           v8::Local<v8::Context>::New(CcTest::isolate(), context_);
     837             :       v8::Context::Scope context_scope(context);
     838         500 :       CalcFibAndCheck(context);
     839             :     }
     840             :     {
     841         500 :       v8::Unlocker unlock1(CcTest::isolate());
     842             :       {
     843         500 :         v8::Locker lock2(CcTest::isolate());
     844         500 :         v8::Isolate::Scope isolate_scope(CcTest::isolate());
     845        1000 :         v8::HandleScope handle_scope(CcTest::isolate());
     846             :         v8::Local<v8::Context> context =
     847         500 :             v8::Local<v8::Context>::New(CcTest::isolate(), context_);
     848             :         v8::Context::Scope context_scope(context);
     849        1000 :         CalcFibAndCheck(context);
     850         500 :       }
     851         500 :     }
     852         500 :   }
     853             : 
     854             :  private:
     855             :   v8::Persistent<v8::Context> context_;
     856             : };
     857             : 
     858             : 
     859             : // Locker inside an Unlocker inside a Locker for default isolate.
     860       28342 : TEST(LockUnlockLockDefaultIsolateMultithreaded) {
     861             : #if V8_TARGET_ARCH_MIPS
     862             :   const int kNThreads = 50;
     863             : #else
     864             :   const int kNThreads = 100;
     865             : #endif
     866             :   Local<v8::Context> context;
     867             :   std::vector<JoinableThread*> threads;
     868           5 :   threads.reserve(kNThreads);
     869             :   {
     870           5 :     v8::Locker locker_(CcTest::isolate());
     871           5 :     v8::Isolate::Scope isolate_scope(CcTest::isolate());
     872          10 :     v8::HandleScope handle_scope(CcTest::isolate());
     873           5 :     context = v8::Context::New(CcTest::isolate());
     874         505 :     for (int i = 0; i < kNThreads; i++) {
     875        1000 :       threads.push_back(new LockUnlockLockDefaultIsolateThread(context));
     876           5 :     }
     877             :   }
     878           5 :   StartJoinAndDeleteThreads(threads);
     879           5 : }
     880             : 
     881             : 
     882       28342 : TEST(Regress1433) {
     883          55 :   for (int i = 0; i < 10; i++) {
     884             :     v8::Isolate::CreateParams create_params;
     885          50 :     create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
     886          50 :     v8::Isolate* isolate = v8::Isolate::New(create_params);
     887             :     {
     888             :       v8::Locker lock(isolate);
     889             :       v8::Isolate::Scope isolate_scope(isolate);
     890         100 :       v8::HandleScope handle_scope(isolate);
     891          50 :       v8::Local<v8::Context> context = v8::Context::New(isolate);
     892             :       v8::Context::Scope context_scope(context);
     893          50 :       v8::Local<v8::String> source = v8_str("1+1");
     894             :       v8::Local<v8::Script> script =
     895          50 :           v8::Script::Compile(context, source).ToLocalChecked();
     896         100 :       v8::Local<v8::Value> result = script->Run(context).ToLocalChecked();
     897         100 :       v8::String::Utf8Value utf8(isolate, result);
     898             :     }
     899          50 :     isolate->Dispose();
     900             :   }
     901           5 : }
     902             : 
     903             : 
     904             : static const char* kSimpleExtensionSource =
     905             :   "(function Foo() {"
     906             :   "  return 4;"
     907             :   "})() ";
     908             : 
     909         600 : class IsolateGenesisThread : public JoinableThread {
     910             :  public:
     911             :   IsolateGenesisThread(int count, const char* extension_names[])
     912             :     : JoinableThread("IsolateGenesisThread"),
     913             :       count_(count),
     914         200 :       extension_names_(extension_names)
     915             :   {}
     916             : 
     917         198 :   void Run() override {
     918             :     v8::Isolate::CreateParams create_params;
     919         199 :     create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
     920         199 :     v8::Isolate* isolate = v8::Isolate::New(create_params);
     921             :     {
     922             :       v8::Isolate::Scope isolate_scope(isolate);
     923         200 :       v8::ExtensionConfiguration extensions(count_, extension_names_);
     924         400 :       v8::HandleScope handle_scope(isolate);
     925         200 :       v8::Context::New(isolate, &extensions);
     926             :     }
     927         200 :     isolate->Dispose();
     928         200 :   }
     929             : 
     930             :  private:
     931             :   int count_;
     932             :   const char** extension_names_;
     933             : };
     934             : 
     935             : 
     936             : // Test installing extensions in separate isolates concurrently.
     937             : // http://code.google.com/p/v8/issues/detail?id=1821
     938       28342 : TEST(ExtensionsRegistration) {
     939             : #if V8_TARGET_ARCH_ARM || V8_TARGET_ARCH_MIPS
     940             :   const int kNThreads = 10;
     941             : #elif V8_TARGET_ARCH_X64 && V8_TARGET_ARCH_32_BIT
     942             :   const int kNThreads = 4;
     943             : #elif V8_TARGET_ARCH_S390 && V8_TARGET_ARCH_32_BIT
     944             :   const int kNThreads = 10;
     945             : #else
     946             :   const int kNThreads = 40;
     947             : #endif
     948             :   v8::RegisterExtension(new v8::Extension("test0",
     949           5 :                                           kSimpleExtensionSource));
     950             :   v8::RegisterExtension(new v8::Extension("test1",
     951           5 :                                           kSimpleExtensionSource));
     952             :   v8::RegisterExtension(new v8::Extension("test2",
     953           5 :                                           kSimpleExtensionSource));
     954             :   v8::RegisterExtension(new v8::Extension("test3",
     955           5 :                                           kSimpleExtensionSource));
     956             :   v8::RegisterExtension(new v8::Extension("test4",
     957           5 :                                           kSimpleExtensionSource));
     958             :   v8::RegisterExtension(new v8::Extension("test5",
     959           5 :                                           kSimpleExtensionSource));
     960             :   v8::RegisterExtension(new v8::Extension("test6",
     961           5 :                                           kSimpleExtensionSource));
     962             :   v8::RegisterExtension(new v8::Extension("test7",
     963           5 :                                           kSimpleExtensionSource));
     964             :   const char* extension_names[] = { "test0", "test1",
     965             :                                     "test2", "test3", "test4",
     966           5 :                                     "test5", "test6", "test7" };
     967             :   std::vector<JoinableThread*> threads;
     968           5 :   threads.reserve(kNThreads);
     969         205 :   for (int i = 0; i < kNThreads; i++) {
     970         600 :     threads.push_back(new IsolateGenesisThread(8, extension_names));
     971             :   }
     972           5 :   StartJoinAndDeleteThreads(threads);
     973           5 : }
     974             : 
     975             : }  // namespace test_lockers
     976             : }  // namespace internal
     977       85011 : }  // namespace v8

Generated by: LCOV version 1.10