blob: 3d29893a94d3bf8a537f01887dff37deb37cb982 [file] [log] [blame]
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/threading/scoped_thread_priority.h"
#include "base/check_op.h"
#include "base/location.h"
#include "base/threading/platform_thread.h"
#include "base/trace_event/interned_args_helper.h"
#include "base/trace_event/trace_event.h"
#include "build/build_config.h"
#if BUILDFLAG(IS_WIN)
#include <windows.h>
#endif
namespace base {
namespace {
PlatformThreadHandle PortableCurrentThreadHandle() {
PlatformThreadHandle current = PlatformThread::CurrentHandle();
#if BUILDFLAG(IS_WIN)
PlatformThreadHandle::Handle platform_handle;
BOOL did_dup = ::DuplicateHandle(
::GetCurrentProcess(), current.platform_handle(), ::GetCurrentProcess(),
&platform_handle, 0, false, DUPLICATE_SAME_ACCESS);
if (!did_dup) {
return PlatformThreadHandle();
}
return PlatformThreadHandle(platform_handle);
#else
return current;
#endif
}
constinit thread_local internal::ScopedBoostPriorityBase* current_boost_scope =
nullptr;
} // namespace
namespace internal {
ScopedBoostPriorityBase::ScopedBoostPriorityBase()
: initial_thread_type_(current_boost_scope &&
current_boost_scope->target_thread_type_
? *current_boost_scope->target_thread_type_
: PlatformThread::GetCurrentThreadType()) {
previous_boost_scope_ = std::exchange(current_boost_scope, this);
}
ScopedBoostPriorityBase::~ScopedBoostPriorityBase() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK_EQ(current_boost_scope, this);
current_boost_scope = previous_boost_scope_;
}
bool ScopedBoostPriorityBase::ShouldBoostTo(
ThreadType target_thread_type) const {
return initial_thread_type_ < target_thread_type &&
PlatformThread::CanChangeThreadType(initial_thread_type_,
target_thread_type) &&
PlatformThread::CanChangeThreadType(target_thread_type,
initial_thread_type_);
}
bool ScopedBoostPriorityBase::CurrentThreadHasScope() {
return current_boost_scope != nullptr;
}
} // namespace internal
ScopedBoostPriority::ScopedBoostPriority(ThreadType target_thread_type) {
CHECK_LT(target_thread_type, ThreadType::kRealtimeAudio);
const bool should_boost = ShouldBoostTo(target_thread_type);
if (should_boost) {
target_thread_type_ = target_thread_type;
priority_override_handle_ = internal::SetThreadTypeOverride(
PlatformThread::CurrentHandle(), target_thread_type);
}
}
ScopedBoostPriority::~ScopedBoostPriority() {
if (target_thread_type_) {
internal::RemoveThreadTypeOverride(PlatformThread::CurrentHandle(),
priority_override_handle_,
initial_thread_type_);
}
}
ScopedBoostablePriority::ScopedBoostablePriority()
: thread_handle_(PortableCurrentThreadHandle())
#if BUILDFLAG(IS_WIN)
,
scoped_handle_(thread_handle_.platform_handle())
#endif
{
}
ScopedBoostablePriority::~ScopedBoostablePriority() {
Reset();
}
bool ScopedBoostablePriority::BoostPriority(ThreadType target_thread_type) {
CHECK_LT(target_thread_type, ThreadType::kRealtimeAudio);
if (thread_handle_.is_null()) {
return false;
}
const bool should_boost = ShouldBoostTo(target_thread_type);
if (!should_boost) {
return false;
}
if (target_thread_type_) {
return false;
}
target_thread_type_ = target_thread_type;
priority_override_handle_ =
internal::SetThreadTypeOverride(thread_handle_, target_thread_type);
return priority_override_handle_;
}
void ScopedBoostablePriority::Reset() {
if (thread_handle_.is_null()) {
return;
}
if (target_thread_type_) {
internal::RemoveThreadTypeOverride(
thread_handle_, priority_override_handle_, initial_thread_type_);
target_thread_type_ = std::nullopt;
}
}
namespace internal {
ScopedMayLoadLibraryAtBackgroundPriority::
ScopedMayLoadLibraryAtBackgroundPriority(const Location& from_here,
std::atomic_bool* already_loaded)
#if BUILDFLAG(IS_WIN)
: already_loaded_(already_loaded)
#endif // BUILDFLAG(IS_WIN)
{
TRACE_EVENT_BEGIN(
"base", "ScopedMayLoadLibraryAtBackgroundPriority",
[&](perfetto::EventContext ctx) {
ctx.event()->set_source_location_iid(
base::trace_event::InternedSourceLocation::Get(&ctx, from_here));
});
#if BUILDFLAG(IS_WIN)
if (already_loaded_ && already_loaded_->load(std::memory_order_relaxed)) {
return;
}
boost_priority_.emplace(base::ThreadType::kDefault);
#endif // BUILDFLAG(IS_WIN)
}
ScopedMayLoadLibraryAtBackgroundPriority::
~ScopedMayLoadLibraryAtBackgroundPriority() {
#if BUILDFLAG(IS_WIN)
boost_priority_.reset();
if (already_loaded_) {
already_loaded_->store(true, std::memory_order_relaxed);
}
#endif // BUILDFLAG(IS_WIN)
TRACE_EVENT_END0("base", "ScopedMayLoadLibraryAtBackgroundPriority");
}
} // namespace internal
} // namespace base