blob: f6a029de1f6f2dad5ef49b570f408902b0b8e6ef [file] [log] [blame]
// Copyright 2019 The Chromium 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 "third_party/blink/renderer/platform/scheduler/public/cooperative_scheduling_manager.h"
#include "base/auto_reset.h"
#include "base/run_loop.h"
#include "base/time/default_tick_clock.h"
#include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
#include "third_party/blink/renderer/platform/scheduler/public/thread.h"
#include "third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h"
#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
#include "third_party/blink/renderer/platform/wtf/thread_specific.h"
namespace blink {
namespace scheduler {
namespace {
// Minimum time interval between nested loop runs.
constexpr base::TimeDelta kNestedLoopMinimumInterval =
base::TimeDelta::FromMilliseconds(15);
} // namespace
// static
CooperativeSchedulingManager* CooperativeSchedulingManager::Instance() {
DEFINE_THREAD_SAFE_STATIC_LOCAL(ThreadSpecific<CooperativeSchedulingManager>,
manager, ());
return &(*manager);
}
CooperativeSchedulingManager::AllowedStackScope::AllowedStackScope(
CooperativeSchedulingManager* manager)
: cooperative_scheduling_manager_(manager) {
DCHECK(cooperative_scheduling_manager_);
cooperative_scheduling_manager_->EnterAllowedStackScope();
}
CooperativeSchedulingManager::AllowedStackScope::~AllowedStackScope() {
cooperative_scheduling_manager_->LeaveAllowedStackScope();
}
CooperativeSchedulingManager::CooperativeSchedulingManager()
: clock_(base::DefaultTickClock::GetInstance()),
feature_enabled_(RuntimeEnabledFeatures::CooperativeSchedulingEnabled()) {
}
void CooperativeSchedulingManager::EnterAllowedStackScope() {
TRACE_EVENT_NESTABLE_ASYNC_BEGIN0("renderer.scheduler",
"PreemptionAllowedStackScope",
TRACE_ID_LOCAL(this));
allowed_stack_scope_depth_++;
}
void CooperativeSchedulingManager::LeaveAllowedStackScope() {
TRACE_EVENT_NESTABLE_ASYNC_END0("renderer.scheduler",
"PreemptionAllowedStackScope",
TRACE_ID_LOCAL(this));
allowed_stack_scope_depth_--;
DCHECK_GE(allowed_stack_scope_depth_, 0);
}
void CooperativeSchedulingManager::SafepointSlow() {
// Avoid nesting more than two levels.
if (running_nested_loop_ || base::RunLoop::IsNestedOnCurrentThread())
return;
if (!feature_enabled_)
return;
// TODO(keishi): Also bail if V8 EnteredContextCount is more than 1
// This task slice completes here.
Thread::MainThread()->Scheduler()->OnSafepointEntered();
RunNestedLoop();
// A new task slice starts here.
Thread::MainThread()->Scheduler()->OnSafepointExited();
}
void CooperativeSchedulingManager::RunNestedLoop() {
TRACE_EVENT0("renderer.scheduler",
"CooperativeSchedulingManager::RunNestedLoop");
base::AutoReset<bool> nested_loop_scope(&running_nested_loop_, true);
wait_until_ = clock_->NowTicks() + kNestedLoopMinimumInterval;
// TODO(keishi): Ask scheduler to run high priority tasks from different
// EventLoops.
}
void CooperativeSchedulingManager::SetTickClockForTesting(
const base::TickClock* clock) {
clock_ = clock;
}
} // namespace scheduler
} // namespace blink