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 #ifndef V8_COMPILER_DISPATCHER_LAZY_COMPILE_DISPATCHER_H_
6 #define V8_COMPILER_DISPATCHER_LAZY_COMPILE_DISPATCHER_H_
7 
8 #include <cstdint>
9 #include <map>
10 #include <memory>
11 #include <unordered_set>
12 #include <utility>
13 
14 #include "src/base/atomic-utils.h"
15 #include "src/base/macros.h"
16 #include "src/base/optional.h"
17 #include "src/base/platform/condition-variable.h"
18 #include "src/base/platform/mutex.h"
19 #include "src/base/platform/semaphore.h"
20 #include "src/common/globals.h"
21 #include "src/handles/maybe-handles.h"
22 #include "src/utils/identity-map.h"
23 #include "testing/gtest/include/gtest/gtest_prod.h"  // nogncheck
24 
25 namespace v8 {
26 
27 class Platform;
28 enum class MemoryPressureLevel;
29 
30 namespace internal {
31 
32 class AstRawString;
33 class AstValueFactory;
34 class BackgroundCompileTask;
35 class CancelableTaskManager;
36 class UnoptimizedCompileJob;
37 class FunctionLiteral;
38 class Isolate;
39 class ParseInfo;
40 class SharedFunctionInfo;
41 class TimedHistogram;
42 class WorkerThreadRuntimeCallStats;
43 class Zone;
44 
45 template <typename T>
46 class Handle;
47 
48 // The LazyCompileDispatcher uses a combination of idle tasks and background
49 // tasks to parse and compile lazily parsed functions.
50 //
51 // As both parsing and compilation currently requires a preparation and
52 // finalization step that happens on the main thread, every task has to be
53 // advanced during idle time first. Depending on the properties of the task, it
54 // can then be parsed or compiled on either background threads, or during idle
55 // time. Last, it has to be finalized during idle time again.
56 //
57 // LazyCompileDispatcher::jobs_ maintains the list of all
58 // LazyCompilerDispatcherJobs the LazyCompileDispatcher knows about.
59 //
60 // LazyCompileDispatcher::pending_background_jobs_ contains the set of
61 // LazyCompilerDispatcherJobs that can be processed on a background thread.
62 //
63 // LazyCompileDispatcher::running_background_jobs_ contains the set of
64 // LazyCompilerDispatcherJobs that are currently being processed on a background
65 // thread.
66 //
67 // LazyCompileDispatcher::DoIdleWork tries to advance as many jobs out of jobs_
68 // as possible during idle time. If a job can't be advanced, but is suitable for
69 // background processing, it fires off background threads.
70 //
71 // LazyCompileDispatcher::DoBackgroundWork advances one of the pending jobs,
72 // and then spins of another idle task to potentially do the final step on the
73 // main thread.
74 class V8_EXPORT_PRIVATE LazyCompileDispatcher {
75  public:
76   using JobId = uintptr_t;
77 
78   LazyCompileDispatcher(Isolate* isolate, Platform* platform,
79                         size_t max_stack_size);
80   LazyCompileDispatcher(const LazyCompileDispatcher&) = delete;
81   LazyCompileDispatcher& operator=(const LazyCompileDispatcher&) = delete;
82   ~LazyCompileDispatcher();
83 
84   // Returns true if the compiler dispatcher is enabled.
85   bool IsEnabled() const;
86 
87   base::Optional<JobId> Enqueue(const ParseInfo* outer_parse_info,
88                                 const AstRawString* function_name,
89                                 const FunctionLiteral* function_literal);
90 
91   // Registers the given |function| with the compilation job |job_id|.
92   void RegisterSharedFunctionInfo(JobId job_id, SharedFunctionInfo function);
93 
94   // Returns true if there is a pending job with the given id.
95   bool IsEnqueued(JobId job_id) const;
96 
97   // Returns true if there is a pending job registered for the given function.
98   bool IsEnqueued(Handle<SharedFunctionInfo> function) const;
99 
100   // Blocks until the given function is compiled (and does so as fast as
101   // possible). Returns true if the compile job was successful.
102   bool FinishNow(Handle<SharedFunctionInfo> function);
103 
104   // Aborts compilation job |job_id|.
105   void AbortJob(JobId job_id);
106 
107   // Aborts all jobs, blocking until all jobs are aborted.
108   void AbortAll();
109 
110  private:
111   FRIEND_TEST(LazyCompilerDispatcherTest, IdleTaskNoIdleTime);
112   FRIEND_TEST(LazyCompilerDispatcherTest, IdleTaskSmallIdleTime);
113   FRIEND_TEST(LazyCompilerDispatcherTest, FinishNowWithWorkerTask);
114   FRIEND_TEST(LazyCompilerDispatcherTest, AbortJobNotStarted);
115   FRIEND_TEST(LazyCompilerDispatcherTest, AbortJobAlreadyStarted);
116   FRIEND_TEST(LazyCompilerDispatcherTest, AsyncAbortAllPendingWorkerTask);
117   FRIEND_TEST(LazyCompilerDispatcherTest, AsyncAbortAllRunningWorkerTask);
118   FRIEND_TEST(LazyCompilerDispatcherTest, CompileMultipleOnBackgroundThread);
119 
120   struct Job {
121     explicit Job(BackgroundCompileTask* task_arg);
122     ~Job();
123 
IsReadyToFinalizeJob124     bool IsReadyToFinalize(const base::MutexGuard&) {
125       return has_run && (!function.is_null() || aborted);
126     }
127 
IsReadyToFinalizeJob128     bool IsReadyToFinalize(base::Mutex* mutex) {
129       base::MutexGuard lock(mutex);
130       return IsReadyToFinalize(lock);
131     }
132 
133     std::unique_ptr<BackgroundCompileTask> task;
134     MaybeHandle<SharedFunctionInfo> function;
135     bool has_run;
136     bool aborted;
137   };
138 
139   using JobMap = std::map<JobId, std::unique_ptr<Job>>;
140   using SharedToJobIdMap = IdentityMap<JobId, FreeStoreAllocationPolicy>;
141 
142   void WaitForJobIfRunningOnBackground(Job* job);
143   JobMap::const_iterator GetJobFor(Handle<SharedFunctionInfo> shared) const;
144   void ScheduleMoreWorkerTasksIfNeeded();
145   void ScheduleIdleTaskFromAnyThread(const base::MutexGuard&);
146   void DoBackgroundWork();
147   void DoIdleWork(double deadline_in_seconds);
148   // Returns iterator to the inserted job.
149   JobMap::const_iterator InsertJob(std::unique_ptr<Job> job);
150   // Returns iterator following the removed job.
151   JobMap::const_iterator RemoveJob(JobMap::const_iterator job);
152 
153   Isolate* isolate_;
154   WorkerThreadRuntimeCallStats* worker_thread_runtime_call_stats_;
155   TimedHistogram* background_compile_timer_;
156   std::shared_ptr<v8::TaskRunner> taskrunner_;
157   Platform* platform_;
158   size_t max_stack_size_;
159 
160   // Copy of FLAG_trace_compiler_dispatcher to allow for access from any thread.
161   bool trace_compiler_dispatcher_;
162 
163   std::unique_ptr<CancelableTaskManager> task_manager_;
164 
165   // Id for next job to be added
166   JobId next_job_id_;
167 
168   // Mapping from job_id to job.
169   JobMap jobs_;
170 
171   // Mapping from SharedFunctionInfo to the corresponding unoptimized
172   // compilation's JobId;
173   SharedToJobIdMap shared_to_unoptimized_job_id_;
174 
175   // The following members can be accessed from any thread. Methods need to hold
176   // the mutex |mutex_| while accessing them.
177   base::Mutex mutex_;
178 
179   // True if an idle task is scheduled to be run.
180   bool idle_task_scheduled_;
181 
182   // Number of scheduled or running WorkerTask objects.
183   int num_worker_tasks_;
184 
185   // The set of jobs that can be run on a background thread.
186   std::unordered_set<Job*> pending_background_jobs_;
187 
188   // The set of jobs currently being run on background threads.
189   std::unordered_set<Job*> running_background_jobs_;
190 
191   // If not nullptr, then the main thread waits for the task processing
192   // this job, and blocks on the ConditionVariable main_thread_blocking_signal_.
193   Job* main_thread_blocking_on_job_;
194   base::ConditionVariable main_thread_blocking_signal_;
195 
196   // Test support.
197   base::AtomicValue<bool> block_for_testing_;
198   base::Semaphore semaphore_for_testing_;
199 };
200 
201 }  // namespace internal
202 }  // namespace v8
203 
204 #endif  // V8_COMPILER_DISPATCHER_LAZY_COMPILE_DISPATCHER_H_
205