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
25namespace v8 {
26
27class Platform;
28enum class MemoryPressureLevel;
29
30namespace internal {
31
32class AstRawString;
33class AstValueFactory;
34class BackgroundCompileTask;
35class CancelableTaskManager;
36class UnoptimizedCompileJob;
37class UnoptimizedCompileState;
38class FunctionLiteral;
39class Isolate;
40class ParseInfo;
41class ProducedPreparseData;
42class SharedFunctionInfo;
43class TimedHistogram;
44class Utf16CharacterStream;
45class WorkerThreadRuntimeCallStats;
46class Zone;
47
48template <typename T>
49class 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.
77class 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
146    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
170  void NotifyAddedBackgroundJob(const base::MutexGuard& lock) {
171    ++num_jobs_for_background_;
172    VerifyBackgroundTaskCount(lock);
173  }
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
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