1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
|
// 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.
#ifndef BASE_THREADING_SCOPED_THREAD_PRIORITY_H_
#define BASE_THREADING_SCOPED_THREAD_PRIORITY_H_
#include <atomic>
#include <optional>
#include "base/base_export.h"
#include "base/compiler_specific.h"
#include "base/location.h"
#include "base/macros/uniquify.h"
#include "base/memory/raw_ptr.h"
#include "base/task/task_observer.h"
#include "base/threading/thread_checker.h"
#include "build/build_config.h"
#if BUILDFLAG(IS_WIN)
#include "base/win/scoped_handle.h"
#endif
namespace base {
class Location;
enum class ThreadType : int;
// All code that may load a DLL on a background thread must be surrounded by a
// scope that starts with this macro.
//
// Example:
// Foo();
// {
// SCOPED_MAY_LOAD_LIBRARY_AT_BACKGROUND_PRIORITY();
// LoadMyDll();
// }
// Bar();
//
// The macro raises the thread priority to match ThreadType::kDefault for the
// scope if no other thread has completed the current scope already (multiple
// threads can racily begin the initialization and will all be boosted for it).
// On Windows, loading a DLL on a background thread can lead to a priority
// inversion on the loader lock and cause huge janks.
#define SCOPED_MAY_LOAD_LIBRARY_AT_BACKGROUND_PRIORITY() \
static std::atomic_bool BASE_UNIQUIFY(already_loaded){false}; \
base::internal::ScopedMayLoadLibraryAtBackgroundPriority BASE_UNIQUIFY( \
scoped_may_load_library_at_background_priority)( \
FROM_HERE, &BASE_UNIQUIFY(already_loaded));
// Like SCOPED_MAY_LOAD_LIBRARY_AT_BACKGROUND_PRIORITY, but raises the thread
// priority every time the scope is entered. Use this around code that may
// conditionally load a DLL each time it is executed, or which repeatedly loads
// and unloads DLLs.
#define SCOPED_MAY_LOAD_LIBRARY_AT_BACKGROUND_PRIORITY_REPEATEDLY() \
base::internal::ScopedMayLoadLibraryAtBackgroundPriority BASE_UNIQUIFY( \
scoped_may_load_library_at_background_priority)(FROM_HERE, nullptr);
// Boosts the current thread's priority to match the priority of threads of
// `target_thread_type` in this scope. `target_thread_type` must be lower
// priority than kRealtimeAudio, since realtime priority should only be used by
// dedicated media threads.
class BASE_EXPORT ScopedBoostPriority {
public:
explicit ScopedBoostPriority(ThreadType target_thread_type);
~ScopedBoostPriority();
ScopedBoostPriority(const ScopedBoostPriority&) = delete;
ScopedBoostPriority& operator=(const ScopedBoostPriority&) = delete;
private:
std::optional<ThreadType> original_thread_type_;
};
// Allows another thread to temporarily boost the current thread's priority to
// match the priority of threads of `target_thread_type`. The priority is reset
// when the object is destroyed, which must happens on the current thread.
// `target_thread_type` must be lower priority than kRealtimeAudio, since
// realtime priority should only be used by dedicated media threads.
class BASE_EXPORT ScopedBoostablePriority {
public:
ScopedBoostablePriority();
~ScopedBoostablePriority();
ScopedBoostablePriority(const ScopedBoostablePriority&) = delete;
ScopedBoostablePriority& operator=(const ScopedBoostablePriority& other) =
delete;
// Boosts the priority of the thread where this ScopedBoostablePriority was
// created. Can be called from any thread, but requires proper external
// synchronization with the constructor, destructor and any other call to
// BoostPriority/Reset(). If called multiple times, only the first call takes
// effect.
bool BoostPriority(ThreadType target_thread_type);
// Resets the priority of the thread where this ScopedBoostablePriority was
// created to its original priority. Can be called from any thread, but
// requires proper external synchronization with the constructor, destructor
// and any other call to BoostPriority/Reset().
void Reset();
private:
const ThreadType initial_thread_type_;
PlatformThreadHandle thread_handle_;
#if BUILDFLAG(IS_WIN)
win::ScopedHandle scoped_handle_;
#endif
bool did_override_priority_{false};
internal::PlatformPriorityOverride priority_override_handle_;
THREAD_CHECKER(thread_checker_);
};
// This wraps ScopedBoostPriority with a callback to determine whether
// the priority should be boosted or not before every task execution.
class BASE_EXPORT TaskMonitoringScopedBoostPriority : public TaskObserver {
public:
explicit TaskMonitoringScopedBoostPriority(
ThreadType target_thread_type,
RepeatingCallback<bool()> should_boost_callback);
~TaskMonitoringScopedBoostPriority() override;
TaskMonitoringScopedBoostPriority(const TaskMonitoringScopedBoostPriority&) =
delete;
TaskMonitoringScopedBoostPriority& operator=(
const TaskMonitoringScopedBoostPriority&) = delete;
// TaskObserver implementation:
void WillProcessTask(const PendingTask& pending_task,
bool was_blocked_or_low_priority) override;
void DidProcessTask(const PendingTask& pending_task) override {}
private:
std::optional<ScopedBoostPriority> scoped_boost_priority_;
ThreadType target_thread_type_;
RepeatingCallback<bool()> should_boost_callback_;
};
namespace internal {
class BASE_EXPORT ScopedMayLoadLibraryAtBackgroundPriority {
public:
// Boosts thread priority to match ThreadType::kDefault within its scope if
// `already_loaded` is nullptr or set to false.
explicit ScopedMayLoadLibraryAtBackgroundPriority(
const Location& from_here,
std::atomic_bool* already_loaded);
ScopedMayLoadLibraryAtBackgroundPriority(
const ScopedMayLoadLibraryAtBackgroundPriority&) = delete;
ScopedMayLoadLibraryAtBackgroundPriority& operator=(
const ScopedMayLoadLibraryAtBackgroundPriority&) = delete;
~ScopedMayLoadLibraryAtBackgroundPriority();
private:
#if BUILDFLAG(IS_WIN)
// The original priority when invoking entering the scope().
std::optional<ThreadType> original_thread_type_;
const raw_ptr<std::atomic_bool> already_loaded_;
#endif
};
} // namespace internal
} // namespace base
#endif // BASE_THREADING_SCOPED_THREAD_PRIORITY_H_
|