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