LCOV - code coverage report
Current view: top level - test/unittests/compiler-dispatcher - compiler-dispatcher-unittest.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 404 427 94.6 %
Date: 2019-01-20 Functions: 54 84 64.3 %

          Line data    Source code
       1             : // Copyright 2016 the V8 project authors. All rights reserved.
       2             : // Use of this source code is governed by a BSD-style license that can be
       3             : // found in the LICENSE file.
       4             : 
       5             : #include "src/compiler-dispatcher/compiler-dispatcher.h"
       6             : 
       7             : #include <sstream>
       8             : 
       9             : #include "include/v8-platform.h"
      10             : #include "src/api-inl.h"
      11             : #include "src/ast/ast-value-factory.h"
      12             : #include "src/ast/ast.h"
      13             : #include "src/ast/scopes.h"
      14             : #include "src/base/platform/semaphore.h"
      15             : #include "src/base/template-utils.h"
      16             : #include "src/compiler.h"
      17             : #include "src/flags.h"
      18             : #include "src/handles.h"
      19             : #include "src/objects-inl.h"
      20             : #include "src/parsing/parse-info.h"
      21             : #include "src/parsing/parsing.h"
      22             : #include "src/v8.h"
      23             : #include "test/unittests/test-helpers.h"
      24             : #include "test/unittests/test-utils.h"
      25             : #include "testing/gtest/include/gtest/gtest.h"
      26             : 
      27             : namespace v8 {
      28             : namespace internal {
      29             : 
      30             : class CompilerDispatcherTestFlags {
      31             :  public:
      32          15 :   static void SetFlagsForTest() {
      33          15 :     CHECK_NULL(save_flags_);
      34          15 :     save_flags_ = new SaveFlags();
      35          15 :     FLAG_single_threaded = true;
      36          15 :     FlagList::EnforceFlagImplications();
      37          15 :     FLAG_compiler_dispatcher = true;
      38          15 :   }
      39             : 
      40          15 :   static void RestoreFlags() {
      41          15 :     CHECK_NOT_NULL(save_flags_);
      42          15 :     delete save_flags_;
      43          15 :     save_flags_ = nullptr;
      44          15 :   }
      45             : 
      46             :  private:
      47             :   static SaveFlags* save_flags_;
      48             : 
      49             :   DISALLOW_IMPLICIT_CONSTRUCTORS(CompilerDispatcherTestFlags);
      50             : };
      51             : 
      52             : SaveFlags* CompilerDispatcherTestFlags::save_flags_ = nullptr;
      53             : 
      54             : class CompilerDispatcherTest : public TestWithNativeContext {
      55             :  public:
      56          15 :   CompilerDispatcherTest() = default;
      57          15 :   ~CompilerDispatcherTest() override = default;
      58             : 
      59          15 :   static void SetUpTestCase() {
      60          15 :     CompilerDispatcherTestFlags::SetFlagsForTest();
      61             :     TestWithNativeContext ::SetUpTestCase();
      62          15 :   }
      63             : 
      64          15 :   static void TearDownTestCase() {
      65             :     TestWithNativeContext ::TearDownTestCase();
      66          15 :     CompilerDispatcherTestFlags::RestoreFlags();
      67          15 :   }
      68             : 
      69          18 :   static base::Optional<CompilerDispatcher::JobId> EnqueueUnoptimizedCompileJob(
      70             :       CompilerDispatcher* dispatcher, Isolate* isolate,
      71             :       Handle<SharedFunctionInfo> shared) {
      72             :     std::unique_ptr<ParseInfo> outer_parse_info =
      73          18 :         test::OuterParseInfoForShared(isolate, shared);
      74             :     AstValueFactory* ast_value_factory =
      75          18 :         outer_parse_info->GetOrCreateAstValueFactory();
      76             :     AstNodeFactory ast_node_factory(ast_value_factory,
      77          18 :                                     outer_parse_info->zone());
      78             : 
      79             :     const AstRawString* function_name =
      80          18 :         ast_value_factory->GetOneByteString("f");
      81             :     DeclarationScope* script_scope = new (outer_parse_info->zone())
      82          18 :         DeclarationScope(outer_parse_info->zone(), ast_value_factory);
      83             :     DeclarationScope* function_scope =
      84             :         new (outer_parse_info->zone()) DeclarationScope(
      85          18 :             outer_parse_info->zone(), script_scope, FUNCTION_SCOPE);
      86          36 :     function_scope->set_start_position(shared->StartPosition());
      87          36 :     function_scope->set_end_position(shared->EndPosition());
      88             :     std::vector<void*> pointer_buffer;
      89             :     ScopedPtrList<Statement> statements(&pointer_buffer);
      90             :     const FunctionLiteral* function_literal =
      91             :         ast_node_factory.NewFunctionLiteral(
      92             :             function_name, function_scope, statements, -1, -1, -1,
      93             :             FunctionLiteral::kNoDuplicateParameters,
      94             :             FunctionLiteral::kAnonymousExpression,
      95             :             FunctionLiteral::kShouldEagerCompile, shared->StartPosition(), true,
      96          36 :             shared->FunctionLiteralId(isolate), nullptr);
      97             : 
      98             :     return dispatcher->Enqueue(outer_parse_info.get(), function_name,
      99          36 :                                function_literal);
     100             :   }
     101             : 
     102             :  private:
     103             :   DISALLOW_COPY_AND_ASSIGN(CompilerDispatcherTest);
     104             : };
     105             : 
     106             : namespace {
     107             : 
     108             : class MockPlatform : public v8::Platform {
     109             :  public:
     110          13 :   MockPlatform()
     111             :       : time_(0.0),
     112             :         time_step_(0.0),
     113             :         idle_task_(nullptr),
     114             :         sem_(0),
     115          26 :         tracing_controller_(V8::GetCurrentPlatform()->GetTracingController()) {}
     116          26 :   ~MockPlatform() override {
     117          13 :     base::MutexGuard lock(&mutex_);
     118          13 :     EXPECT_TRUE(foreground_tasks_.empty());
     119          13 :     EXPECT_TRUE(worker_tasks_.empty());
     120          26 :     EXPECT_TRUE(idle_task_ == nullptr);
     121          13 :   }
     122             : 
     123          15 :   int NumberOfWorkerThreads() override { return 1; }
     124             : 
     125          13 :   std::shared_ptr<TaskRunner> GetForegroundTaskRunner(
     126             :       v8::Isolate* isolate) override {
     127          26 :     return std::make_shared<MockForegroundTaskRunner>(this);
     128             :   }
     129             : 
     130          12 :   void CallOnWorkerThread(std::unique_ptr<Task> task) override {
     131          12 :     base::MutexGuard lock(&mutex_);
     132          12 :     worker_tasks_.push_back(std::move(task));
     133          12 :   }
     134             : 
     135           0 :   void CallDelayedOnWorkerThread(std::unique_ptr<Task> task,
     136             :                                  double delay_in_seconds) override {
     137           0 :     UNREACHABLE();
     138             :   }
     139             : 
     140           0 :   void CallOnForegroundThread(v8::Isolate* isolate, Task* task) override {
     141           0 :     base::MutexGuard lock(&mutex_);
     142           0 :     foreground_tasks_.push_back(std::unique_ptr<Task>(task));
     143           0 :   }
     144             : 
     145           0 :   void CallDelayedOnForegroundThread(v8::Isolate* isolate, Task* task,
     146             :                                      double delay_in_seconds) override {
     147           0 :     UNREACHABLE();
     148             :   }
     149             : 
     150           0 :   void CallIdleOnForegroundThread(v8::Isolate* isolate,
     151             :                                   IdleTask* task) override {
     152           0 :     base::MutexGuard lock(&mutex_);
     153           0 :     ASSERT_TRUE(idle_task_ == nullptr);
     154           0 :     idle_task_ = task;
     155             :   }
     156             : 
     157           0 :   bool IdleTasksEnabled(v8::Isolate* isolate) override { return true; }
     158             : 
     159          19 :   double MonotonicallyIncreasingTime() override {
     160          19 :     time_ += time_step_;
     161          19 :     return time_;
     162             :   }
     163             : 
     164           0 :   double CurrentClockTimeMillis() override {
     165           0 :     return time_ * base::Time::kMillisecondsPerSecond;
     166             :   }
     167             : 
     168           0 :   v8::TracingController* GetTracingController() override {
     169           0 :     return tracing_controller_;
     170             :   }
     171             : 
     172           9 :   void RunIdleTask(double deadline_in_seconds, double time_step) {
     173           9 :     time_step_ = time_step;
     174             :     IdleTask* task;
     175             :     {
     176           9 :       base::MutexGuard lock(&mutex_);
     177           9 :       task = idle_task_;
     178          27 :       ASSERT_TRUE(idle_task_ != nullptr);
     179           9 :       idle_task_ = nullptr;
     180             :     }
     181           9 :     task->Run(deadline_in_seconds);
     182           9 :     delete task;
     183             :   }
     184             : 
     185          22 :   bool IdleTaskPending() {
     186          22 :     base::MutexGuard lock(&mutex_);
     187          44 :     return idle_task_;
     188             :   }
     189             : 
     190          14 :   bool WorkerTasksPending() {
     191          14 :     base::MutexGuard lock(&mutex_);
     192          28 :     return !worker_tasks_.empty();
     193             :   }
     194             : 
     195             :   bool ForegroundTasksPending() {
     196             :     base::MutexGuard lock(&mutex_);
     197             :     return !foreground_tasks_.empty();
     198             :   }
     199             : 
     200           6 :   void RunWorkerTasksAndBlock(Platform* platform) {
     201             :     std::vector<std::unique_ptr<Task>> tasks;
     202             :     {
     203           6 :       base::MutexGuard lock(&mutex_);
     204             :       tasks.swap(worker_tasks_);
     205             :     }
     206             :     platform->CallOnWorkerThread(
     207          24 :         base::make_unique<TaskWrapper>(this, std::move(tasks), true));
     208           6 :     sem_.Wait();
     209           6 :   }
     210             : 
     211           2 :   void RunWorkerTasks(Platform* platform) {
     212             :     std::vector<std::unique_ptr<Task>> tasks;
     213             :     {
     214           2 :       base::MutexGuard lock(&mutex_);
     215             :       tasks.swap(worker_tasks_);
     216             :     }
     217             :     platform->CallOnWorkerThread(
     218           8 :         base::make_unique<TaskWrapper>(this, std::move(tasks), false));
     219           2 :   }
     220             : 
     221             :   void RunForegroundTasks() {
     222             :     std::vector<std::unique_ptr<Task>> tasks;
     223             :     {
     224             :       base::MutexGuard lock(&mutex_);
     225             :       tasks.swap(foreground_tasks_);
     226             :     }
     227             :     for (auto& task : tasks) {
     228             :       task->Run();
     229             :       // Reset |task| before running the next one.
     230             :       task.reset();
     231             :     }
     232             :   }
     233             : 
     234           5 :   void ClearWorkerTasks() {
     235             :     std::vector<std::unique_ptr<Task>> tasks;
     236             :     {
     237           5 :       base::MutexGuard lock(&mutex_);
     238             :       tasks.swap(worker_tasks_);
     239           5 :     }
     240           5 :   }
     241             : 
     242             :   void ClearForegroundTasks() {
     243             :     std::vector<std::unique_ptr<Task>> tasks;
     244             :     {
     245             :       base::MutexGuard lock(&mutex_);
     246             :       tasks.swap(foreground_tasks_);
     247             :     }
     248             :   }
     249             : 
     250           1 :   void ClearIdleTask() {
     251           1 :     base::MutexGuard lock(&mutex_);
     252           3 :     ASSERT_TRUE(idle_task_ != nullptr);
     253           1 :     delete idle_task_;
     254           1 :     idle_task_ = nullptr;
     255             :   }
     256             : 
     257             :  private:
     258             :   class TaskWrapper : public Task {
     259             :    public:
     260             :     TaskWrapper(MockPlatform* platform,
     261             :                 std::vector<std::unique_ptr<Task>> tasks, bool signal)
     262          16 :         : platform_(platform), tasks_(std::move(tasks)), signal_(signal) {}
     263          16 :     ~TaskWrapper() override = default;
     264             : 
     265           8 :     void Run() override {
     266          24 :       for (auto& task : tasks_) {
     267           8 :         task->Run();
     268             :         // Reset |task| before running the next one.
     269             :         task.reset();
     270             :       }
     271           8 :       if (signal_) platform_->sem_.Signal();
     272           8 :     }
     273             : 
     274             :    private:
     275             :     MockPlatform* platform_;
     276             :     std::vector<std::unique_ptr<Task>> tasks_;
     277             :     bool signal_;
     278             : 
     279             :     DISALLOW_COPY_AND_ASSIGN(TaskWrapper);
     280             :   };
     281             : 
     282          13 :   class MockForegroundTaskRunner final : public TaskRunner {
     283             :    public:
     284             :     explicit MockForegroundTaskRunner(MockPlatform* platform)
     285          13 :         : platform_(platform) {}
     286             : 
     287           0 :     void PostTask(std::unique_ptr<v8::Task> task) override {
     288           0 :       base::MutexGuard lock(&platform_->mutex_);
     289           0 :       platform_->foreground_tasks_.push_back(std::move(task));
     290           0 :     }
     291             : 
     292           0 :     void PostDelayedTask(std::unique_ptr<Task> task,
     293             :                          double delay_in_seconds) override {
     294           0 :       UNREACHABLE();
     295             :     };
     296             : 
     297          10 :     void PostIdleTask(std::unique_ptr<IdleTask> task) override {
     298             :       DCHECK(IdleTasksEnabled());
     299          10 :       base::MutexGuard lock(&platform_->mutex_);
     300          30 :       ASSERT_TRUE(platform_->idle_task_ == nullptr);
     301          20 :       platform_->idle_task_ = task.release();
     302             :     }
     303             : 
     304          13 :     bool IdleTasksEnabled() override { return true; };
     305             : 
     306             :    private:
     307             :     MockPlatform* platform_;
     308             :   };
     309             : 
     310             :   double time_;
     311             :   double time_step_;
     312             : 
     313             :   // Protects all *_tasks_.
     314             :   base::Mutex mutex_;
     315             : 
     316             :   IdleTask* idle_task_;
     317             :   std::vector<std::unique_ptr<Task>> worker_tasks_;
     318             :   std::vector<std::unique_ptr<Task>> foreground_tasks_;
     319             : 
     320             :   base::Semaphore sem_;
     321             : 
     322             :   v8::TracingController* tracing_controller_;
     323             : 
     324             :   DISALLOW_COPY_AND_ASSIGN(MockPlatform);
     325             : };
     326             : 
     327             : }  // namespace
     328             : 
     329       15129 : TEST_F(CompilerDispatcherTest, Construct) {
     330           1 :   MockPlatform platform;
     331           3 :   CompilerDispatcher dispatcher(i_isolate(), &platform, FLAG_stack_size);
     332           2 :   dispatcher.AbortAll();
     333           1 : }
     334             : 
     335       15129 : TEST_F(CompilerDispatcherTest, IsEnqueued) {
     336           1 :   MockPlatform platform;
     337           3 :   CompilerDispatcher dispatcher(i_isolate(), &platform, FLAG_stack_size);
     338             : 
     339             :   Handle<SharedFunctionInfo> shared =
     340           1 :       test::CreateSharedFunctionInfo(i_isolate(), nullptr);
     341           2 :   ASSERT_FALSE(shared->is_compiled());
     342           2 :   ASSERT_FALSE(dispatcher.IsEnqueued(shared));
     343             : 
     344             :   base::Optional<CompilerDispatcher::JobId> job_id =
     345           1 :       EnqueueUnoptimizedCompileJob(&dispatcher, i_isolate(), shared);
     346             : 
     347           1 :   ASSERT_TRUE(job_id);
     348           2 :   ASSERT_TRUE(dispatcher.IsEnqueued(*job_id));
     349           2 :   ASSERT_FALSE(dispatcher.IsEnqueued(shared));  // SFI not yet registered.
     350             : 
     351           1 :   dispatcher.RegisterSharedFunctionInfo(*job_id, *shared);
     352           2 :   ASSERT_TRUE(dispatcher.IsEnqueued(*job_id));
     353           2 :   ASSERT_TRUE(dispatcher.IsEnqueued(shared));
     354             : 
     355           1 :   dispatcher.AbortAll();
     356           2 :   ASSERT_FALSE(dispatcher.IsEnqueued(*job_id));
     357           2 :   ASSERT_FALSE(dispatcher.IsEnqueued(shared));
     358             : 
     359           2 :   ASSERT_FALSE(platform.IdleTaskPending());
     360           2 :   ASSERT_TRUE(platform.WorkerTasksPending());
     361           2 :   platform.ClearWorkerTasks();
     362             : }
     363             : 
     364       15129 : TEST_F(CompilerDispatcherTest, FinishNow) {
     365           1 :   MockPlatform platform;
     366           3 :   CompilerDispatcher dispatcher(i_isolate(), &platform, FLAG_stack_size);
     367             : 
     368             :   Handle<SharedFunctionInfo> shared =
     369           1 :       test::CreateSharedFunctionInfo(i_isolate(), nullptr);
     370           2 :   ASSERT_FALSE(shared->is_compiled());
     371             : 
     372             :   base::Optional<CompilerDispatcher::JobId> job_id =
     373           1 :       EnqueueUnoptimizedCompileJob(&dispatcher, i_isolate(), shared);
     374           1 :   dispatcher.RegisterSharedFunctionInfo(*job_id, *shared);
     375             : 
     376           2 :   ASSERT_TRUE(dispatcher.FinishNow(shared));
     377             :   // Finishing removes the SFI from the queue.
     378           2 :   ASSERT_FALSE(dispatcher.IsEnqueued(*job_id));
     379           2 :   ASSERT_FALSE(dispatcher.IsEnqueued(shared));
     380           2 :   ASSERT_TRUE(shared->is_compiled());
     381             : 
     382           1 :   platform.ClearWorkerTasks();
     383           2 :   ASSERT_FALSE(platform.IdleTaskPending());
     384           2 :   dispatcher.AbortAll();
     385             : }
     386             : 
     387       15129 : TEST_F(CompilerDispatcherTest, CompileAndFinalize) {
     388           1 :   MockPlatform platform;
     389           3 :   CompilerDispatcher dispatcher(i_isolate(), &platform, FLAG_stack_size);
     390             : 
     391             :   Handle<SharedFunctionInfo> shared =
     392           1 :       test::CreateSharedFunctionInfo(i_isolate(), nullptr);
     393           2 :   ASSERT_FALSE(shared->is_compiled());
     394           2 :   ASSERT_FALSE(platform.IdleTaskPending());
     395             : 
     396             :   base::Optional<CompilerDispatcher::JobId> job_id =
     397           1 :       EnqueueUnoptimizedCompileJob(&dispatcher, i_isolate(), shared);
     398           2 :   ASSERT_TRUE(platform.WorkerTasksPending());
     399             : 
     400             :   // Run compile steps.
     401           1 :   platform.RunWorkerTasksAndBlock(V8::GetCurrentPlatform());
     402             : 
     403             :   // Since we haven't yet registered the SFI for the job, it should still be
     404             :   // enqueued and waiting.
     405           2 :   ASSERT_TRUE(dispatcher.IsEnqueued(*job_id));
     406           2 :   ASSERT_FALSE(shared->is_compiled());
     407           2 :   ASSERT_FALSE(platform.IdleTaskPending());
     408             : 
     409             :   // Register SFI, which should schedule another idle task to finalize the
     410             :   // compilation.
     411           1 :   dispatcher.RegisterSharedFunctionInfo(*job_id, *shared);
     412           2 :   ASSERT_TRUE(platform.IdleTaskPending());
     413           1 :   platform.RunIdleTask(1000.0, 0.0);
     414             : 
     415           2 :   ASSERT_FALSE(dispatcher.IsEnqueued(shared));
     416           2 :   ASSERT_TRUE(shared->is_compiled());
     417           2 :   ASSERT_FALSE(platform.WorkerTasksPending());
     418           2 :   ASSERT_FALSE(platform.IdleTaskPending());
     419           2 :   dispatcher.AbortAll();
     420             : }
     421             : 
     422       15129 : TEST_F(CompilerDispatcherTest, IdleTaskNoIdleTime) {
     423           1 :   MockPlatform platform;
     424           3 :   CompilerDispatcher dispatcher(i_isolate(), &platform, FLAG_stack_size);
     425             : 
     426             :   Handle<SharedFunctionInfo> shared =
     427           1 :       test::CreateSharedFunctionInfo(i_isolate(), nullptr);
     428           2 :   ASSERT_FALSE(shared->is_compiled());
     429           2 :   ASSERT_FALSE(platform.IdleTaskPending());
     430             : 
     431             :   base::Optional<CompilerDispatcher::JobId> job_id =
     432           1 :       EnqueueUnoptimizedCompileJob(&dispatcher, i_isolate(), shared);
     433           1 :   dispatcher.RegisterSharedFunctionInfo(*job_id, *shared);
     434             : 
     435             :   // Run compile steps.
     436           1 :   platform.RunWorkerTasksAndBlock(V8::GetCurrentPlatform());
     437             : 
     438             :   // Job should be ready to finalize.
     439           3 :   ASSERT_EQ(dispatcher.jobs_.size(), 1u);
     440           1 :   ASSERT_TRUE(dispatcher.jobs_.begin()->second->has_run);
     441           2 :   ASSERT_TRUE(platform.IdleTaskPending());
     442             : 
     443             :   // Grant no idle time and have time advance beyond it in one step.
     444           1 :   platform.RunIdleTask(0.0, 1.0);
     445             : 
     446           2 :   ASSERT_TRUE(dispatcher.IsEnqueued(shared));
     447           2 :   ASSERT_FALSE(shared->is_compiled());
     448           2 :   ASSERT_TRUE(platform.IdleTaskPending());
     449             : 
     450             :   // Job should be ready to finalize.
     451           3 :   ASSERT_EQ(dispatcher.jobs_.size(), 1u);
     452           1 :   ASSERT_TRUE(dispatcher.jobs_.begin()->second->has_run);
     453             : 
     454             :   // Now grant a lot of idle time and freeze time.
     455           1 :   platform.RunIdleTask(1000.0, 0.0);
     456             : 
     457           2 :   ASSERT_FALSE(dispatcher.IsEnqueued(shared));
     458           2 :   ASSERT_TRUE(shared->is_compiled());
     459           2 :   ASSERT_FALSE(platform.IdleTaskPending());
     460           2 :   ASSERT_FALSE(platform.WorkerTasksPending());
     461           2 :   dispatcher.AbortAll();
     462             : }
     463             : 
     464       15129 : TEST_F(CompilerDispatcherTest, IdleTaskSmallIdleTime) {
     465           1 :   MockPlatform platform;
     466           3 :   CompilerDispatcher dispatcher(i_isolate(), &platform, FLAG_stack_size);
     467             : 
     468             :   Handle<SharedFunctionInfo> shared_1 =
     469           1 :       test::CreateSharedFunctionInfo(i_isolate(), nullptr);
     470           2 :   ASSERT_FALSE(shared_1->is_compiled());
     471             :   Handle<SharedFunctionInfo> shared_2 =
     472           1 :       test::CreateSharedFunctionInfo(i_isolate(), nullptr);
     473           2 :   ASSERT_FALSE(shared_2->is_compiled());
     474             : 
     475             :   base::Optional<CompilerDispatcher::JobId> job_id_1 =
     476           1 :       EnqueueUnoptimizedCompileJob(&dispatcher, i_isolate(), shared_1);
     477             :   base::Optional<CompilerDispatcher::JobId> job_id_2 =
     478           1 :       EnqueueUnoptimizedCompileJob(&dispatcher, i_isolate(), shared_2);
     479             : 
     480           1 :   dispatcher.RegisterSharedFunctionInfo(*job_id_1, *shared_1);
     481           1 :   dispatcher.RegisterSharedFunctionInfo(*job_id_2, *shared_2);
     482             : 
     483             :   // Run compile steps.
     484           1 :   platform.RunWorkerTasksAndBlock(V8::GetCurrentPlatform());
     485             : 
     486             :   // Both jobs should be ready to finalize.
     487           3 :   ASSERT_EQ(dispatcher.jobs_.size(), 2u);
     488           1 :   ASSERT_TRUE(dispatcher.jobs_.begin()->second->has_run);
     489           1 :   ASSERT_TRUE((++dispatcher.jobs_.begin())->second->has_run);
     490           2 :   ASSERT_TRUE(platform.IdleTaskPending());
     491             : 
     492             :   // Grant a small anount of idle time and have time advance beyond it in one
     493             :   // step.
     494           1 :   platform.RunIdleTask(2.0, 1.0);
     495             : 
     496             :   // Only one of the jobs should be finalized.
     497           3 :   ASSERT_EQ(dispatcher.jobs_.size(), 1u);
     498           1 :   ASSERT_TRUE(dispatcher.jobs_.begin()->second->has_run);
     499           1 :   ASSERT_NE(dispatcher.IsEnqueued(shared_1), dispatcher.IsEnqueued(shared_2));
     500           2 :   ASSERT_NE(shared_1->is_compiled(), shared_2->is_compiled());
     501           2 :   ASSERT_TRUE(platform.IdleTaskPending());
     502             : 
     503             :   // Now grant a lot of idle time and freeze time.
     504           1 :   platform.RunIdleTask(1000.0, 0.0);
     505             : 
     506           2 :   ASSERT_FALSE(dispatcher.IsEnqueued(shared_1) ||
     507             :                dispatcher.IsEnqueued(shared_2));
     508           3 :   ASSERT_TRUE(shared_1->is_compiled() && shared_2->is_compiled());
     509           2 :   ASSERT_FALSE(platform.IdleTaskPending());
     510           2 :   ASSERT_FALSE(platform.WorkerTasksPending());
     511           2 :   dispatcher.AbortAll();
     512             : }
     513             : 
     514       15129 : TEST_F(CompilerDispatcherTest, IdleTaskException) {
     515           1 :   MockPlatform platform;
     516           2 :   CompilerDispatcher dispatcher(i_isolate(), &platform, 50);
     517             : 
     518           1 :   std::string raw_script("(x) { var a = ");
     519        1001 :   for (int i = 0; i < 1000; i++) {
     520             :     // Alternate + and - to avoid n-ary operation nodes.
     521             :     raw_script += "'x' + 'x' - ";
     522             :   }
     523             :   raw_script += " 'x'; };";
     524             :   test::ScriptResource* script =
     525           1 :       new test::ScriptResource(raw_script.c_str(), strlen(raw_script.c_str()));
     526             :   Handle<SharedFunctionInfo> shared =
     527           1 :       test::CreateSharedFunctionInfo(i_isolate(), script);
     528           2 :   ASSERT_FALSE(shared->is_compiled());
     529             : 
     530             :   base::Optional<CompilerDispatcher::JobId> job_id =
     531           1 :       EnqueueUnoptimizedCompileJob(&dispatcher, i_isolate(), shared);
     532           1 :   dispatcher.RegisterSharedFunctionInfo(*job_id, *shared);
     533             : 
     534             :   // Run compile steps and finalize.
     535           1 :   platform.RunWorkerTasksAndBlock(V8::GetCurrentPlatform());
     536           1 :   platform.RunIdleTask(1000.0, 0.0);
     537             : 
     538           2 :   ASSERT_FALSE(dispatcher.IsEnqueued(shared));
     539           2 :   ASSERT_FALSE(shared->is_compiled());
     540           2 :   ASSERT_FALSE(i_isolate()->has_pending_exception());
     541           1 :   platform.ClearWorkerTasks();
     542           2 :   dispatcher.AbortAll();
     543             : }
     544             : 
     545       15129 : TEST_F(CompilerDispatcherTest, FinishNowWithWorkerTask) {
     546           1 :   MockPlatform platform;
     547           3 :   CompilerDispatcher dispatcher(i_isolate(), &platform, FLAG_stack_size);
     548             : 
     549             :   Handle<SharedFunctionInfo> shared =
     550           1 :       test::CreateSharedFunctionInfo(i_isolate(), nullptr);
     551           2 :   ASSERT_FALSE(shared->is_compiled());
     552             : 
     553             :   base::Optional<CompilerDispatcher::JobId> job_id =
     554           1 :       EnqueueUnoptimizedCompileJob(&dispatcher, i_isolate(), shared);
     555           1 :   dispatcher.RegisterSharedFunctionInfo(*job_id, *shared);
     556             : 
     557           3 :   ASSERT_EQ(dispatcher.jobs_.size(), 1u);
     558           2 :   ASSERT_FALSE(dispatcher.jobs_.begin()->second->has_run);
     559             : 
     560           2 :   ASSERT_TRUE(dispatcher.IsEnqueued(shared));
     561           2 :   ASSERT_FALSE(shared->is_compiled());
     562           3 :   ASSERT_EQ(dispatcher.jobs_.size(), 1u);
     563           2 :   ASSERT_FALSE(dispatcher.jobs_.begin()->second->has_run);
     564           2 :   ASSERT_TRUE(platform.WorkerTasksPending());
     565             : 
     566             :   // This does not block, but races with the FinishNow() call below.
     567           1 :   platform.RunWorkerTasks(V8::GetCurrentPlatform());
     568             : 
     569           2 :   ASSERT_TRUE(dispatcher.FinishNow(shared));
     570             :   // Finishing removes the SFI from the queue.
     571           2 :   ASSERT_FALSE(dispatcher.IsEnqueued(shared));
     572           2 :   ASSERT_TRUE(shared->is_compiled());
     573           1 :   if (platform.IdleTaskPending()) platform.ClearIdleTask();
     574           2 :   ASSERT_FALSE(platform.WorkerTasksPending());
     575           2 :   dispatcher.AbortAll();
     576             : }
     577             : 
     578       15129 : TEST_F(CompilerDispatcherTest, IdleTaskMultipleJobs) {
     579           1 :   MockPlatform platform;
     580           3 :   CompilerDispatcher dispatcher(i_isolate(), &platform, FLAG_stack_size);
     581             : 
     582             :   Handle<SharedFunctionInfo> shared_1 =
     583           1 :       test::CreateSharedFunctionInfo(i_isolate(), nullptr);
     584           2 :   ASSERT_FALSE(shared_1->is_compiled());
     585             :   Handle<SharedFunctionInfo> shared_2 =
     586           1 :       test::CreateSharedFunctionInfo(i_isolate(), nullptr);
     587           2 :   ASSERT_FALSE(shared_2->is_compiled());
     588             : 
     589             :   base::Optional<CompilerDispatcher::JobId> job_id_1 =
     590           1 :       EnqueueUnoptimizedCompileJob(&dispatcher, i_isolate(), shared_1);
     591             :   base::Optional<CompilerDispatcher::JobId> job_id_2 =
     592           1 :       EnqueueUnoptimizedCompileJob(&dispatcher, i_isolate(), shared_2);
     593             : 
     594           1 :   dispatcher.RegisterSharedFunctionInfo(*job_id_1, *shared_1);
     595           1 :   dispatcher.RegisterSharedFunctionInfo(*job_id_2, *shared_2);
     596             : 
     597           2 :   ASSERT_TRUE(dispatcher.IsEnqueued(shared_1));
     598           2 :   ASSERT_TRUE(dispatcher.IsEnqueued(shared_2));
     599             : 
     600             :   // Run compile steps and finalize.
     601           1 :   platform.RunWorkerTasksAndBlock(V8::GetCurrentPlatform());
     602           1 :   platform.RunIdleTask(1000.0, 0.0);
     603             : 
     604           2 :   ASSERT_FALSE(dispatcher.IsEnqueued(shared_1));
     605           2 :   ASSERT_FALSE(dispatcher.IsEnqueued(shared_2));
     606           2 :   ASSERT_TRUE(shared_1->is_compiled());
     607           2 :   ASSERT_TRUE(shared_2->is_compiled());
     608           2 :   ASSERT_FALSE(platform.IdleTaskPending());
     609           2 :   ASSERT_FALSE(platform.WorkerTasksPending());
     610           2 :   dispatcher.AbortAll();
     611             : }
     612             : 
     613       15129 : TEST_F(CompilerDispatcherTest, FinishNowException) {
     614           1 :   MockPlatform platform;
     615           2 :   CompilerDispatcher dispatcher(i_isolate(), &platform, 50);
     616             : 
     617           1 :   std::string raw_script("(x) { var a = ");
     618        1001 :   for (int i = 0; i < 1000; i++) {
     619             :     // Alternate + and - to avoid n-ary operation nodes.
     620             :     raw_script += "'x' + 'x' - ";
     621             :   }
     622             :   raw_script += " 'x'; };";
     623             :   test::ScriptResource* script =
     624           1 :       new test::ScriptResource(raw_script.c_str(), strlen(raw_script.c_str()));
     625             :   Handle<SharedFunctionInfo> shared =
     626           1 :       test::CreateSharedFunctionInfo(i_isolate(), script);
     627           2 :   ASSERT_FALSE(shared->is_compiled());
     628             : 
     629             :   base::Optional<CompilerDispatcher::JobId> job_id =
     630           1 :       EnqueueUnoptimizedCompileJob(&dispatcher, i_isolate(), shared);
     631           1 :   dispatcher.RegisterSharedFunctionInfo(*job_id, *shared);
     632             : 
     633           2 :   ASSERT_FALSE(dispatcher.FinishNow(shared));
     634             : 
     635           2 :   ASSERT_FALSE(dispatcher.IsEnqueued(shared));
     636           2 :   ASSERT_FALSE(shared->is_compiled());
     637           2 :   ASSERT_TRUE(i_isolate()->has_pending_exception());
     638             : 
     639           1 :   i_isolate()->clear_pending_exception();
     640           2 :   ASSERT_FALSE(platform.IdleTaskPending());
     641           1 :   platform.ClearWorkerTasks();
     642           2 :   dispatcher.AbortAll();
     643             : }
     644             : 
     645       15129 : TEST_F(CompilerDispatcherTest, AbortJobNotStarted) {
     646           1 :   MockPlatform platform;
     647           3 :   CompilerDispatcher dispatcher(i_isolate(), &platform, FLAG_stack_size);
     648             : 
     649             :   Handle<SharedFunctionInfo> shared =
     650           1 :       test::CreateSharedFunctionInfo(i_isolate(), nullptr);
     651           2 :   ASSERT_FALSE(shared->is_compiled());
     652             : 
     653             :   base::Optional<CompilerDispatcher::JobId> job_id =
     654           1 :       EnqueueUnoptimizedCompileJob(&dispatcher, i_isolate(), shared);
     655             : 
     656           3 :   ASSERT_EQ(dispatcher.jobs_.size(), 1u);
     657           2 :   ASSERT_FALSE(dispatcher.jobs_.begin()->second->has_run);
     658             : 
     659           2 :   ASSERT_TRUE(dispatcher.IsEnqueued(*job_id));
     660           2 :   ASSERT_FALSE(shared->is_compiled());
     661           3 :   ASSERT_EQ(dispatcher.jobs_.size(), 1u);
     662           2 :   ASSERT_FALSE(dispatcher.jobs_.begin()->second->has_run);
     663           2 :   ASSERT_TRUE(platform.WorkerTasksPending());
     664             : 
     665           1 :   dispatcher.AbortJob(*job_id);
     666             : 
     667             :   // Aborting removes the job from the queue.
     668           2 :   ASSERT_FALSE(dispatcher.IsEnqueued(*job_id));
     669           2 :   ASSERT_FALSE(shared->is_compiled());
     670           2 :   ASSERT_FALSE(platform.IdleTaskPending());
     671           1 :   platform.ClearWorkerTasks();
     672           2 :   dispatcher.AbortAll();
     673             : }
     674             : 
     675       15129 : TEST_F(CompilerDispatcherTest, AbortJobAlreadyStarted) {
     676           1 :   MockPlatform platform;
     677           3 :   CompilerDispatcher dispatcher(i_isolate(), &platform, FLAG_stack_size);
     678             : 
     679             :   Handle<SharedFunctionInfo> shared =
     680           1 :       test::CreateSharedFunctionInfo(i_isolate(), nullptr);
     681           2 :   ASSERT_FALSE(shared->is_compiled());
     682             : 
     683             :   base::Optional<CompilerDispatcher::JobId> job_id =
     684           1 :       EnqueueUnoptimizedCompileJob(&dispatcher, i_isolate(), shared);
     685             : 
     686           3 :   ASSERT_EQ(dispatcher.jobs_.size(), 1u);
     687           2 :   ASSERT_FALSE(dispatcher.jobs_.begin()->second->has_run);
     688             : 
     689           2 :   ASSERT_TRUE(dispatcher.IsEnqueued(*job_id));
     690           2 :   ASSERT_FALSE(shared->is_compiled());
     691           3 :   ASSERT_EQ(dispatcher.jobs_.size(), 1u);
     692           2 :   ASSERT_FALSE(dispatcher.jobs_.begin()->second->has_run);
     693           2 :   ASSERT_TRUE(platform.WorkerTasksPending());
     694             : 
     695             :   // Have dispatcher block on the background thread when running the job.
     696             :   {
     697             :     base::LockGuard<base::Mutex> lock(&dispatcher.mutex_);
     698             :     dispatcher.block_for_testing_.SetValue(true);
     699             :   }
     700             : 
     701             :   // Start background thread and wait until it is about to run the job.
     702           1 :   platform.RunWorkerTasks(V8::GetCurrentPlatform());
     703       17574 :   while (dispatcher.block_for_testing_.Value()) {
     704             :   }
     705             : 
     706             :   // Now abort while dispatcher is in the middle of running the job.
     707           1 :   dispatcher.AbortJob(*job_id);
     708             : 
     709             :   // Unblock background thread, and wait for job to complete.
     710             :   {
     711             :     base::LockGuard<base::Mutex> lock(&dispatcher.mutex_);
     712             :     dispatcher.main_thread_blocking_on_job_ =
     713           1 :         dispatcher.jobs_.begin()->second.get();
     714           1 :     dispatcher.semaphore_for_testing_.Signal();
     715           3 :     while (dispatcher.main_thread_blocking_on_job_ != nullptr) {
     716           1 :       dispatcher.main_thread_blocking_signal_.Wait(&dispatcher.mutex_);
     717             :     }
     718             :   }
     719             : 
     720             :   // Job should have finished running and then been aborted.
     721           2 :   ASSERT_TRUE(dispatcher.IsEnqueued(*job_id));
     722           2 :   ASSERT_FALSE(shared->is_compiled());
     723           3 :   ASSERT_EQ(dispatcher.jobs_.size(), 1u);
     724           1 :   ASSERT_TRUE(dispatcher.jobs_.begin()->second->has_run);
     725           1 :   ASSERT_TRUE(dispatcher.jobs_.begin()->second->aborted);
     726           2 :   ASSERT_FALSE(platform.WorkerTasksPending());
     727           2 :   ASSERT_TRUE(platform.IdleTaskPending());
     728             : 
     729             :   // Runt the pending idle task
     730           1 :   platform.RunIdleTask(1000.0, 0.0);
     731             : 
     732             :   // Aborting removes the SFI from the queue.
     733           2 :   ASSERT_FALSE(dispatcher.IsEnqueued(*job_id));
     734           2 :   ASSERT_FALSE(shared->is_compiled());
     735           2 :   ASSERT_FALSE(platform.IdleTaskPending());
     736           2 :   ASSERT_FALSE(platform.WorkerTasksPending());
     737           2 :   dispatcher.AbortAll();
     738             : }
     739             : 
     740       15129 : TEST_F(CompilerDispatcherTest, CompileLazyFinishesDispatcherJob) {
     741             :   // Use the real dispatcher so that CompileLazy checks the same one for
     742             :   // enqueued functions.
     743           1 :   CompilerDispatcher* dispatcher = i_isolate()->compiler_dispatcher();
     744             : 
     745           1 :   const char raw_script[] = "function lazy() { return 42; }; lazy;";
     746             :   test::ScriptResource* script =
     747           1 :       new test::ScriptResource(raw_script, strlen(raw_script));
     748           1 :   Handle<JSFunction> f = RunJS<JSFunction>(script);
     749           2 :   Handle<SharedFunctionInfo> shared(f->shared(), i_isolate());
     750           2 :   ASSERT_FALSE(shared->is_compiled());
     751             : 
     752             :   base::Optional<CompilerDispatcher::JobId> job_id =
     753           1 :       EnqueueUnoptimizedCompileJob(dispatcher, i_isolate(), shared);
     754           1 :   dispatcher->RegisterSharedFunctionInfo(*job_id, *shared);
     755             : 
     756             :   // Now force the function to run and ensure CompileLazy finished and dequeues
     757             :   // it from the dispatcher.
     758           1 :   RunJS("lazy();");
     759           2 :   ASSERT_TRUE(shared->is_compiled());
     760           2 :   ASSERT_FALSE(dispatcher->IsEnqueued(shared));
     761             : }
     762             : 
     763       15129 : TEST_F(CompilerDispatcherTest, CompileLazy2FinishesDispatcherJob) {
     764             :   // Use the real dispatcher so that CompileLazy checks the same one for
     765             :   // enqueued functions.
     766           1 :   CompilerDispatcher* dispatcher = i_isolate()->compiler_dispatcher();
     767             : 
     768           1 :   const char raw_source_2[] = "function lazy2() { return 42; }; lazy2;";
     769             :   test::ScriptResource* source_2 =
     770           1 :       new test::ScriptResource(raw_source_2, strlen(raw_source_2));
     771           1 :   Handle<JSFunction> lazy2 = RunJS<JSFunction>(source_2);
     772           2 :   Handle<SharedFunctionInfo> shared_2(lazy2->shared(), i_isolate());
     773           2 :   ASSERT_FALSE(shared_2->is_compiled());
     774             : 
     775           1 :   const char raw_source_1[] = "function lazy1() { return lazy2(); }; lazy1;";
     776             :   test::ScriptResource* source_1 =
     777           1 :       new test::ScriptResource(raw_source_1, strlen(raw_source_1));
     778           1 :   Handle<JSFunction> lazy1 = RunJS<JSFunction>(source_1);
     779           2 :   Handle<SharedFunctionInfo> shared_1(lazy1->shared(), i_isolate());
     780           2 :   ASSERT_FALSE(shared_1->is_compiled());
     781             : 
     782             :   base::Optional<CompilerDispatcher::JobId> job_id_1 =
     783           1 :       EnqueueUnoptimizedCompileJob(dispatcher, i_isolate(), shared_1);
     784           1 :   dispatcher->RegisterSharedFunctionInfo(*job_id_1, *shared_1);
     785             : 
     786             :   base::Optional<CompilerDispatcher::JobId> job_id_2 =
     787           1 :       EnqueueUnoptimizedCompileJob(dispatcher, i_isolate(), shared_2);
     788           1 :   dispatcher->RegisterSharedFunctionInfo(*job_id_2, *shared_2);
     789             : 
     790           2 :   ASSERT_TRUE(dispatcher->IsEnqueued(shared_1));
     791           2 :   ASSERT_TRUE(dispatcher->IsEnqueued(shared_2));
     792             : 
     793           1 :   RunJS("lazy1();");
     794           2 :   ASSERT_TRUE(shared_1->is_compiled());
     795           2 :   ASSERT_TRUE(shared_2->is_compiled());
     796           2 :   ASSERT_FALSE(dispatcher->IsEnqueued(shared_1));
     797           2 :   ASSERT_FALSE(dispatcher->IsEnqueued(shared_2));
     798             : }
     799             : 
     800       15129 : TEST_F(CompilerDispatcherTest, CompileMultipleOnBackgroundThread) {
     801           1 :   MockPlatform platform;
     802           3 :   CompilerDispatcher dispatcher(i_isolate(), &platform, FLAG_stack_size);
     803             : 
     804             :   Handle<SharedFunctionInfo> shared_1 =
     805           1 :       test::CreateSharedFunctionInfo(i_isolate(), nullptr);
     806           2 :   ASSERT_FALSE(shared_1->is_compiled());
     807             : 
     808             :   Handle<SharedFunctionInfo> shared_2 =
     809           1 :       test::CreateSharedFunctionInfo(i_isolate(), nullptr);
     810           2 :   ASSERT_FALSE(shared_2->is_compiled());
     811             : 
     812             :   base::Optional<CompilerDispatcher::JobId> job_id_1 =
     813           1 :       EnqueueUnoptimizedCompileJob(&dispatcher, i_isolate(), shared_1);
     814           1 :   dispatcher.RegisterSharedFunctionInfo(*job_id_1, *shared_1);
     815             : 
     816             :   base::Optional<CompilerDispatcher::JobId> job_id_2 =
     817           1 :       EnqueueUnoptimizedCompileJob(&dispatcher, i_isolate(), shared_2);
     818           1 :   dispatcher.RegisterSharedFunctionInfo(*job_id_2, *shared_2);
     819             : 
     820           3 :   ASSERT_EQ(dispatcher.jobs_.size(), 2u);
     821           2 :   ASSERT_FALSE(dispatcher.jobs_.begin()->second->has_run);
     822           2 :   ASSERT_FALSE((++dispatcher.jobs_.begin())->second->has_run);
     823             : 
     824           2 :   ASSERT_TRUE(dispatcher.IsEnqueued(shared_1));
     825           2 :   ASSERT_TRUE(dispatcher.IsEnqueued(shared_2));
     826           2 :   ASSERT_FALSE(shared_1->is_compiled());
     827           2 :   ASSERT_FALSE(shared_2->is_compiled());
     828           2 :   ASSERT_FALSE(platform.IdleTaskPending());
     829           2 :   ASSERT_TRUE(platform.WorkerTasksPending());
     830             : 
     831           1 :   platform.RunWorkerTasksAndBlock(V8::GetCurrentPlatform());
     832             : 
     833           2 :   ASSERT_TRUE(platform.IdleTaskPending());
     834           2 :   ASSERT_FALSE(platform.WorkerTasksPending());
     835           3 :   ASSERT_EQ(dispatcher.jobs_.size(), 2u);
     836           1 :   ASSERT_TRUE(dispatcher.jobs_.begin()->second->has_run);
     837           1 :   ASSERT_TRUE((++dispatcher.jobs_.begin())->second->has_run);
     838             : 
     839             :   // Now grant a lot of idle time and freeze time.
     840           1 :   platform.RunIdleTask(1000.0, 0.0);
     841             : 
     842           2 :   ASSERT_FALSE(dispatcher.IsEnqueued(shared_1));
     843           2 :   ASSERT_FALSE(dispatcher.IsEnqueued(shared_2));
     844           2 :   ASSERT_TRUE(shared_1->is_compiled());
     845           2 :   ASSERT_TRUE(shared_2->is_compiled());
     846           2 :   ASSERT_FALSE(platform.IdleTaskPending());
     847           2 :   dispatcher.AbortAll();
     848             : }
     849             : 
     850             : }  // namespace internal
     851        9075 : }  // namespace v8

Generated by: LCOV version 1.10