• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <memory>
10 #include <unordered_set>
11 #include <utility>
12 
13 #include "src/base/atomic-utils.h"
14 #include "src/base/macros.h"
15 #include "src/base/optional.h"
16 #include "src/base/platform/condition-variable.h"
17 #include "src/base/platform/mutex.h"
18 #include "src/base/platform/semaphore.h"
19 #include "src/common/globals.h"
20 #include "src/handles/maybe-handles.h"
21 #include "src/utils/identity-map.h"
22 #include "src/utils/locked-queue.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 UnoptimizedCompileState;
38 class FunctionLiteral;
39 class Isolate;
40 class ParseInfo;
41 class ProducedPreparseData;
42 class SharedFunctionInfo;
43 class TimedHistogram;
44 class Utf16CharacterStream;
45 class WorkerThreadRuntimeCallStats;
46 class Zone;
47 
48 template <typename T>
49 class Handle;
50 
51 // The LazyCompileDispatcher uses a combination of idle tasks and background
52 // tasks to parse and compile lazily parsed functions.
53 //
54 // As both parsing and compilation currently requires a preparation and
55 // finalization step that happens on the main thread, every task has to be
56 // advanced during idle time first. Depending on the properties of the task, it
57 // can then be parsed or compiled on either background threads, or during idle
58 // time. Last, it has to be finalized during idle time again.
59 //
60 // LazyCompileDispatcher::jobs_ maintains the list of all
61 // LazyCompilerDispatcherJobs the LazyCompileDispatcher knows about.
62 //
63 // LazyCompileDispatcher::pending_background_jobs_ contains the set of
64 // LazyCompilerDispatcherJobs that can be processed on a background thread.
65 //
66 // LazyCompileDispatcher::running_background_jobs_ contains the set of
67 // LazyCompilerDispatcherJobs that are currently being processed on a background
68 // thread.
69 //
70 // LazyCompileDispatcher::DoIdleWork tries to advance as many jobs out of jobs_
71 // as possible during idle time. If a job can't be advanced, but is suitable for
72 // background processing, it fires off background threads.
73 //
74 // LazyCompileDispatcher::DoBackgroundWork advances one of the pending jobs,
75 // and then spins of another idle task to potentially do the final step on the
76 // main thread.
77 class V8_EXPORT_PRIVATE LazyCompileDispatcher {
78  public:
79   using JobId = uintptr_t;
80 
81   LazyCompileDispatcher(Isolate* isolate, Platform* platform,
82                         size_t max_stack_size);
83   LazyCompileDispatcher(const LazyCompileDispatcher&) = delete;
84   LazyCompileDispatcher& operator=(const LazyCompileDispatcher&) = delete;
85   ~LazyCompileDispatcher();
86 
87   void Enqueue(LocalIsolate* isolate, Handle<SharedFunctionInfo> shared_info,
88                std::unique_ptr<Utf16CharacterStream> character_stream);
89 
90   // Returns true if there is a pending job registered for the given function.
91   bool IsEnqueued(Handle<SharedFunctionInfo> function) const;
92 
93   // Blocks until the given function is compiled (and does so as fast as
94   // possible). Returns true if the compile job was successful.
95   bool FinishNow(Handle<SharedFunctionInfo> function);
96 
97   // Aborts compilation job for the given function.
98   void AbortJob(Handle<SharedFunctionInfo> function);
99 
100   // Aborts all jobs, blocking until all jobs are aborted.
101   void AbortAll();
102 
103  private:
104   FRIEND_TEST(LazyCompileDispatcherTest, IdleTaskNoIdleTime);
105   FRIEND_TEST(LazyCompileDispatcherTest, IdleTaskSmallIdleTime);
106   FRIEND_TEST(LazyCompileDispatcherTest, FinishNowWithWorkerTask);
107   FRIEND_TEST(LazyCompileDispatcherTest, AbortJobNotStarted);
108   FRIEND_TEST(LazyCompileDispatcherTest, AbortJobAlreadyStarted);
109   FRIEND_TEST(LazyCompileDispatcherTest, AsyncAbortAllPendingWorkerTask);
110   FRIEND_TEST(LazyCompileDispatcherTest, AsyncAbortAllRunningWorkerTask);
111   FRIEND_TEST(LazyCompileDispatcherTest, CompileMultipleOnBackgroundThread);
112 
113   // JobTask for PostJob API.
114   class JobTask;
115 
116   struct Job {
117     enum class State {
118       // Background thread states (Enqueue + DoBackgroundWork)
119       // ---
120 
121       // In the pending task queue.
122       kPending,
123       // Currently running on a background thread.
124       kRunning,
125       kAbortRequested,  // ... but we want to drop the result.
126       // In the finalizable task queue.
127       kReadyToFinalize,
128       kAborted,
129 
130       // Main thread states (FinishNow and FinalizeSingleJob)
131       // ---
132 
133       // Popped off the pending task queue.
134       kPendingToRunOnForeground,
135       // Popped off the finalizable task queue.
136       kFinalizingNow,
137       kAbortingNow,  // ... and we want to abort
138 
139       // Finished finalizing, ready for deletion.
140       kFinalized,
141     };
142 
143     explicit Job(std::unique_ptr<BackgroundCompileTask> task);
144     ~Job();
145 
is_running_on_backgroundJob146     bool is_running_on_background() const {
147       return state == State::kRunning || state == State::kAbortRequested;
148     }
149 
150     std::unique_ptr<BackgroundCompileTask> task;
151     State state = State::kPending;
152   };
153 
154   using SharedToJobMap = IdentityMap<Job*, FreeStoreAllocationPolicy>;
155 
156   void WaitForJobIfRunningOnBackground(Job* job, const base::MutexGuard&);
157   Job* GetJobFor(Handle<SharedFunctionInfo> shared,
158                  const base::MutexGuard&) const;
159   Job* PopSingleFinalizeJob();
160   void ScheduleIdleTaskFromAnyThread(const base::MutexGuard&);
161   bool FinalizeSingleJob();
162   void DoBackgroundWork(JobDelegate* delegate);
163   void DoIdleWork(double deadline_in_seconds);
164 
165   // DeleteJob without the mutex held.
166   void DeleteJob(Job* job);
167   // DeleteJob with the mutex already held.
168   void DeleteJob(Job* job, const base::MutexGuard&);
169 
NotifyAddedBackgroundJob(const base::MutexGuard & lock)170   void NotifyAddedBackgroundJob(const base::MutexGuard& lock) {
171     ++num_jobs_for_background_;
172     VerifyBackgroundTaskCount(lock);
173   }
NotifyRemovedBackgroundJob(const base::MutexGuard & lock)174   void NotifyRemovedBackgroundJob(const base::MutexGuard& lock) {
175     --num_jobs_for_background_;
176     VerifyBackgroundTaskCount(lock);
177   }
178 
179 #ifdef DEBUG
180   void VerifyBackgroundTaskCount(const base::MutexGuard&);
181 #else
VerifyBackgroundTaskCount(const base::MutexGuard &)182   void VerifyBackgroundTaskCount(const base::MutexGuard&) {}
183 #endif
184 
185   Isolate* isolate_;
186   WorkerThreadRuntimeCallStats* worker_thread_runtime_call_stats_;
187   TimedHistogram* background_compile_timer_;
188   std::shared_ptr<v8::TaskRunner> taskrunner_;
189   Platform* platform_;
190   size_t max_stack_size_;
191 
192   std::unique_ptr<JobHandle> job_handle_;
193 
194   // Copy of FLAG_trace_compiler_dispatcher to allow for access from any thread.
195   bool trace_compiler_dispatcher_;
196 
197   std::unique_ptr<CancelableTaskManager> idle_task_manager_;
198 
199   // The following members can be accessed from any thread. Methods need to hold
200   // the mutex |mutex_| while accessing them.
201   mutable base::Mutex mutex_;
202 
203   // True if an idle task is scheduled to be run.
204   bool idle_task_scheduled_;
205 
206   // The set of jobs that can be run on a background thread.
207   std::vector<Job*> pending_background_jobs_;
208 
209   // The set of jobs that can be finalized on the main thread.
210   std::vector<Job*> finalizable_jobs_;
211 
212   // The total number of jobs ready to execute on background, both those pending
213   // and those currently running.
214   std::atomic<size_t> num_jobs_for_background_;
215 
216 #ifdef DEBUG
217   // The set of all allocated jobs, used for verification of the various queues
218   // and counts.
219   std::unordered_set<Job*> all_jobs_;
220 #endif
221 
222   // A queue of jobs to delete on the background thread(s). Jobs in this queue
223   // are considered dead as far as the rest of the system is concerned, so they
224   // won't be pointed to by any SharedFunctionInfo and won't be in the all_jobs
225   // set above.
226   std::vector<Job*> jobs_to_dispose_;
227 
228   // If not nullptr, then the main thread waits for the task processing
229   // this job, and blocks on the ConditionVariable main_thread_blocking_signal_.
230   Job* main_thread_blocking_on_job_;
231   base::ConditionVariable main_thread_blocking_signal_;
232 
233   // Test support.
234   base::AtomicValue<bool> block_for_testing_;
235   base::Semaphore semaphore_for_testing_;
236 };
237 
238 }  // namespace internal
239 }  // namespace v8
240 
241 #endif  // V8_COMPILER_DISPATCHER_LAZY_COMPILE_DISPATCHER_H_
242