| // Copyright 2013 the V8 project authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "src/libplatform/default-platform.h" |
| |
| #include <algorithm> |
| #include <queue> |
| |
| #include "include/libplatform/libplatform.h" |
| #include "src/base/debug/stack_trace.h" |
| #include "src/base/logging.h" |
| #include "src/base/page-allocator.h" |
| #include "src/base/platform/platform.h" |
| #include "src/base/platform/time.h" |
| #include "src/base/sys-info.h" |
| #include "src/libplatform/default-foreground-task-runner.h" |
| #include "src/libplatform/default-job.h" |
| #include "src/libplatform/default-worker-threads-task-runner.h" |
| |
| namespace v8 { |
| namespace platform { |
| |
| namespace { |
| |
| void PrintStackTrace() { |
| v8::base::debug::StackTrace trace; |
| trace.Print(); |
| // Avoid dumping duplicate stack trace on abort signal. |
| v8::base::debug::DisableSignalStackDump(); |
| } |
| |
| } // namespace |
| |
| std::unique_ptr<v8::Platform> NewDefaultPlatform( |
| int thread_pool_size, IdleTaskSupport idle_task_support, |
| InProcessStackDumping in_process_stack_dumping, |
| std::unique_ptr<v8::TracingController> tracing_controller) { |
| if (in_process_stack_dumping == InProcessStackDumping::kEnabled) { |
| v8::base::debug::EnableInProcessStackDumping(); |
| } |
| auto platform = std::make_unique<DefaultPlatform>( |
| thread_pool_size, idle_task_support, std::move(tracing_controller)); |
| platform->EnsureBackgroundTaskRunnerInitialized(); |
| return std::unique_ptr<v8::Platform>(platform.release()); |
| } |
| |
| V8_PLATFORM_EXPORT std::unique_ptr<JobHandle> NewDefaultJobHandle( |
| Platform* platform, TaskPriority priority, |
| std::unique_ptr<JobTask> job_task, size_t num_worker_threads) { |
| return std::make_unique<DefaultJobHandle>(std::make_shared<DefaultJobState>( |
| platform, std::move(job_task), priority, num_worker_threads)); |
| } |
| |
| bool PumpMessageLoop(v8::Platform* platform, v8::Isolate* isolate, |
| MessageLoopBehavior behavior) { |
| return static_cast<DefaultPlatform*>(platform)->PumpMessageLoop(isolate, |
| behavior); |
| } |
| |
| void RunIdleTasks(v8::Platform* platform, v8::Isolate* isolate, |
| double idle_time_in_seconds) { |
| static_cast<DefaultPlatform*>(platform)->RunIdleTasks(isolate, |
| idle_time_in_seconds); |
| } |
| |
| void SetTracingController( |
| v8::Platform* platform, |
| v8::platform::tracing::TracingController* tracing_controller) { |
| static_cast<DefaultPlatform*>(platform)->SetTracingController( |
| std::unique_ptr<v8::TracingController>(tracing_controller)); |
| } |
| |
| void NotifyIsolateShutdown(v8::Platform* platform, Isolate* isolate) { |
| static_cast<DefaultPlatform*>(platform)->NotifyIsolateShutdown(isolate); |
| } |
| |
| namespace { |
| constexpr int kMaxThreadPoolSize = 16; |
| |
| int GetActualThreadPoolSize(int thread_pool_size) { |
| DCHECK_GE(thread_pool_size, 0); |
| if (thread_pool_size < 1) { |
| thread_pool_size = base::SysInfo::NumberOfProcessors() - 1; |
| } |
| return std::max(std::min(thread_pool_size, kMaxThreadPoolSize), 1); |
| } |
| } // namespace |
| |
| DefaultPlatform::DefaultPlatform( |
| int thread_pool_size, IdleTaskSupport idle_task_support, |
| std::unique_ptr<v8::TracingController> tracing_controller) |
| : thread_pool_size_(GetActualThreadPoolSize(thread_pool_size)), |
| idle_task_support_(idle_task_support), |
| tracing_controller_(std::move(tracing_controller)), |
| page_allocator_(std::make_unique<v8::base::PageAllocator>()) { |
| if (!tracing_controller_) { |
| tracing::TracingController* controller = new tracing::TracingController(); |
| #if !defined(V8_USE_PERFETTO) |
| controller->Initialize(nullptr); |
| #endif |
| tracing_controller_.reset(controller); |
| } |
| } |
| |
| DefaultPlatform::~DefaultPlatform() { |
| base::MutexGuard guard(&lock_); |
| if (worker_threads_task_runner_) worker_threads_task_runner_->Terminate(); |
| for (const auto& it : foreground_task_runner_map_) { |
| it.second->Terminate(); |
| } |
| } |
| |
| namespace { |
| |
| double DefaultTimeFunction() { |
| return base::TimeTicks::HighResolutionNow().ToInternalValue() / |
| static_cast<double>(base::Time::kMicrosecondsPerSecond); |
| } |
| |
| } // namespace |
| |
| void DefaultPlatform::EnsureBackgroundTaskRunnerInitialized() { |
| base::MutexGuard guard(&lock_); |
| if (!worker_threads_task_runner_) { |
| worker_threads_task_runner_ = |
| std::make_shared<DefaultWorkerThreadsTaskRunner>( |
| thread_pool_size_, time_function_for_testing_ |
| ? time_function_for_testing_ |
| : DefaultTimeFunction); |
| } |
| } |
| |
| void DefaultPlatform::SetTimeFunctionForTesting( |
| DefaultPlatform::TimeFunction time_function) { |
| base::MutexGuard guard(&lock_); |
| time_function_for_testing_ = time_function; |
| // The time function has to be right after the construction of the platform. |
| DCHECK(foreground_task_runner_map_.empty()); |
| } |
| |
| bool DefaultPlatform::PumpMessageLoop(v8::Isolate* isolate, |
| MessageLoopBehavior wait_for_work) { |
| bool failed_result = wait_for_work == MessageLoopBehavior::kWaitForWork; |
| std::shared_ptr<DefaultForegroundTaskRunner> task_runner; |
| { |
| base::MutexGuard guard(&lock_); |
| auto it = foreground_task_runner_map_.find(isolate); |
| if (it == foreground_task_runner_map_.end()) return failed_result; |
| task_runner = it->second; |
| } |
| |
| std::unique_ptr<Task> task = task_runner->PopTaskFromQueue(wait_for_work); |
| if (!task) return failed_result; |
| |
| DefaultForegroundTaskRunner::RunTaskScope scope(task_runner); |
| task->Run(); |
| return true; |
| } |
| |
| void DefaultPlatform::RunIdleTasks(v8::Isolate* isolate, |
| double idle_time_in_seconds) { |
| DCHECK_EQ(IdleTaskSupport::kEnabled, idle_task_support_); |
| std::shared_ptr<DefaultForegroundTaskRunner> task_runner; |
| { |
| base::MutexGuard guard(&lock_); |
| if (foreground_task_runner_map_.find(isolate) == |
| foreground_task_runner_map_.end()) { |
| return; |
| } |
| task_runner = foreground_task_runner_map_[isolate]; |
| } |
| double deadline_in_seconds = |
| MonotonicallyIncreasingTime() + idle_time_in_seconds; |
| |
| while (deadline_in_seconds > MonotonicallyIncreasingTime()) { |
| std::unique_ptr<IdleTask> task = task_runner->PopTaskFromIdleQueue(); |
| if (!task) return; |
| DefaultForegroundTaskRunner::RunTaskScope scope(task_runner); |
| task->Run(deadline_in_seconds); |
| } |
| } |
| |
| std::shared_ptr<TaskRunner> DefaultPlatform::GetForegroundTaskRunner( |
| v8::Isolate* isolate) { |
| base::MutexGuard guard(&lock_); |
| if (foreground_task_runner_map_.find(isolate) == |
| foreground_task_runner_map_.end()) { |
| foreground_task_runner_map_.insert(std::make_pair( |
| isolate, std::make_shared<DefaultForegroundTaskRunner>( |
| idle_task_support_, time_function_for_testing_ |
| ? time_function_for_testing_ |
| : DefaultTimeFunction))); |
| } |
| return foreground_task_runner_map_[isolate]; |
| } |
| |
| void DefaultPlatform::CallOnWorkerThread(std::unique_ptr<Task> task) { |
| EnsureBackgroundTaskRunnerInitialized(); |
| worker_threads_task_runner_->PostTask(std::move(task)); |
| } |
| |
| void DefaultPlatform::CallDelayedOnWorkerThread(std::unique_ptr<Task> task, |
| double delay_in_seconds) { |
| EnsureBackgroundTaskRunnerInitialized(); |
| worker_threads_task_runner_->PostDelayedTask(std::move(task), |
| delay_in_seconds); |
| } |
| |
| bool DefaultPlatform::IdleTasksEnabled(Isolate* isolate) { |
| return idle_task_support_ == IdleTaskSupport::kEnabled; |
| } |
| |
| std::unique_ptr<JobHandle> DefaultPlatform::PostJob( |
| TaskPriority priority, std::unique_ptr<JobTask> job_task) { |
| size_t num_worker_threads = NumberOfWorkerThreads(); |
| if (priority == TaskPriority::kBestEffort && num_worker_threads > 2) { |
| num_worker_threads = 2; |
| } |
| return NewDefaultJobHandle(this, priority, std::move(job_task), |
| num_worker_threads); |
| } |
| |
| double DefaultPlatform::MonotonicallyIncreasingTime() { |
| if (time_function_for_testing_) return time_function_for_testing_(); |
| return DefaultTimeFunction(); |
| } |
| |
| double DefaultPlatform::CurrentClockTimeMillis() { |
| return base::OS::TimeCurrentMillis(); |
| } |
| |
| TracingController* DefaultPlatform::GetTracingController() { |
| return tracing_controller_.get(); |
| } |
| |
| void DefaultPlatform::SetTracingController( |
| std::unique_ptr<v8::TracingController> tracing_controller) { |
| DCHECK_NOT_NULL(tracing_controller.get()); |
| tracing_controller_ = std::move(tracing_controller); |
| } |
| |
| int DefaultPlatform::NumberOfWorkerThreads() { return thread_pool_size_; } |
| |
| Platform::StackTracePrinter DefaultPlatform::GetStackTracePrinter() { |
| return PrintStackTrace; |
| } |
| |
| v8::PageAllocator* DefaultPlatform::GetPageAllocator() { |
| return page_allocator_.get(); |
| } |
| |
| void DefaultPlatform::NotifyIsolateShutdown(Isolate* isolate) { |
| base::MutexGuard guard(&lock_); |
| auto it = foreground_task_runner_map_.find(isolate); |
| if (it != foreground_task_runner_map_.end()) { |
| it->second->Terminate(); |
| foreground_task_runner_map_.erase(it); |
| } |
| } |
| |
| } // namespace platform |
| } // namespace v8 |