// Copyright 2012 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // WARNING: You should *NOT* be using this class directly. PlatformThread is // the low-level platform-specific abstraction to the OS's threading interface. // You should instead be using a message-loop driven Thread, see thread.h. #ifndef BASE_THREADING_PLATFORM_THREAD_H_ #define BASE_THREADING_PLATFORM_THREAD_H_ #include #include #include #include #include #include #include #include #include #include "base/base_export.h" #include "base/memory/raw_ptr.h" #include "base/message_loop/message_pump_type.h" #include "base/process/process_handle.h" #include "base/task/thread_type.h" #include "base/threading/platform_thread_ref.h" #include "base/trace_event/base_tracing_forward.h" #include "build/build_config.h" #if BUILDFLAG(IS_WIN) #include "base/win/windows_types.h" #elif BUILDFLAG(IS_FUCHSIA) #include #elif BUILDFLAG(IS_POSIX) #include #include #endif #if BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID) #include "base/feature.h" #endif namespace base { namespace internal { class ThreadTypeManager; } #if BUILDFLAG(IS_ANDROID) BASE_EXPORT BASE_DECLARE_FEATURE(kRestrictBigCoreThreadAffinity); #endif class TimeDelta; // Used for uniquely identifying a thread. // // Wraps a platform-specific integer value with platform-specific size, // guaranteed to have a maximum bitness of 64-bit. Getting a 32-bit value is // possible only if we either know the platform-specific size (because we're in // platform-specific code), or if we are ok with truncation of the value (e.g. // because we are logging and the occasional false match is not catastrophic). class BASE_EXPORT PlatformThreadId { public: #if BUILDFLAG(IS_WIN) using UnderlyingType = DWORD; #elif BUILDFLAG(IS_FUCHSIA) using UnderlyingType = zx_koid_t; #elif BUILDFLAG(IS_APPLE) using UnderlyingType = uint64_t; #elif BUILDFLAG(IS_POSIX) using UnderlyingType = pid_t; #endif static_assert(std::is_integral_v, "Always an integer value."); constexpr PlatformThreadId() = default; // Special templated constructor which prevents implicit conversion of the // integer argument. template explicit constexpr PlatformThreadId(T value) requires(std::is_same_v) : value_(value) {} static constexpr PlatformThreadId ForTest(int value) { return PlatformThreadId(static_cast(value)); } // Allow conversion to u/int64_t, whether the underlying type is signed or // not, and whether it is 32-bit or 64-bit. explicit constexpr operator uint64_t() const { static_assert(sizeof(uint64_t) >= sizeof(UnderlyingType)); return static_cast(value_); } explicit constexpr operator int64_t() const { static_assert(sizeof(int64_t) >= sizeof(UnderlyingType)); return static_cast(value_); } // Forbid conversion to u/int32_t, since we might have a 64-bit // value -- use truncate_to_int32_for_display_only() or raw() instead. explicit constexpr operator uint32_t() const = delete; explicit constexpr operator int32_t() const = delete; // Truncating getter for an int32 representation of the id. // // AVOID: This should only be used in cases where truncation is not // catastrophic, e.g. displaying the thread id in traces or logs. It will // always be preferable to display the full, untruncated thread id. constexpr int32_t truncate_to_int32_for_display_only() const { return static_cast(value_); } // Getter for the underlying raw value. Should only be used when // exposing the UnderlyingType, e.g. passing into system APIs or passing into // functions overloaded on different integer sizes like NumberToString. constexpr UnderlyingType raw() const { return value_; } constexpr friend auto operator<=>(const PlatformThreadId& lhs, const PlatformThreadId& rhs) = default; constexpr friend bool operator==(const PlatformThreadId& lhs, const PlatformThreadId& rhs) = default; // Allow serialising into a trace. void WriteIntoTrace(perfetto::TracedValue&& context) const; private: // TODO(crbug.com/393384253): Use a system-specific invalid value, which might // be 0, -1, or some other value from a system header. UnderlyingType value_ = 0; }; inline std::ostream& operator<<(std::ostream& stream, const PlatformThreadId& id) { return stream << id.raw(); } // Used to operate on threads. class PlatformThreadHandle { public: #if BUILDFLAG(IS_WIN) typedef void* Handle; #elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) typedef pthread_t Handle; #endif constexpr PlatformThreadHandle() : handle_(0) {} explicit constexpr PlatformThreadHandle(Handle handle) : handle_(handle) {} bool is_equal(const PlatformThreadHandle& other) const { #if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) return pthread_equal(handle_, other.handle_); #else return handle_ == other.handle_; #endif } bool is_null() const { return !handle_; } Handle platform_handle() const { return handle_; } private: Handle handle_; }; static constexpr PlatformThreadId kInvalidThreadId = PlatformThreadId(); // A namespace for low-level thread functions. class BASE_EXPORT PlatformThreadBase { public: // Implement this interface to run code on a background thread. Your // ThreadMain method will be called on the newly created thread. class BASE_EXPORT Delegate { public: virtual void ThreadMain() = 0; #if BUILDFLAG(IS_APPLE) // TODO: Move this to the PlatformThreadApple class. // The interval at which the thread expects to have work to do. Zero if // unknown. (Example: audio buffer duration for real-time audio.) Is used to // optimize the thread real-time behavior. Is called on the newly created // thread before ThreadMain(). virtual TimeDelta GetRealtimePeriod(); #endif protected: virtual ~Delegate() = default; }; // A class for leasing a thread type for the lifetime of the object. The // effective thread type is the maximum of all active leases and the default // thread type on the current thread (Set with // PlatformThread::SetCurrentThreadType). class BASE_EXPORT RaiseThreadTypeLease { public: // Creates a new lease for the given thread type. The lease is active for // the lifetime of the object. The effective thread type is the maximum of // all active leases and the default thread type on the current thread (Set // with PlatformThread::SetCurrentThreadType). explicit RaiseThreadTypeLease(ThreadType thread_type); ~RaiseThreadTypeLease(); RaiseThreadTypeLease() = delete; RaiseThreadTypeLease(const RaiseThreadTypeLease&) = delete; RaiseThreadTypeLease(RaiseThreadTypeLease&& other) noexcept; RaiseThreadTypeLease& operator=(const RaiseThreadTypeLease&) = delete; ThreadType thread_type() const { return leased_thread_type_; } private: friend class PlatformThreadThreadTypeManagerTest; RaiseThreadTypeLease(ThreadType thread_type, internal::ThreadTypeManager* manager); ThreadType leased_thread_type_; raw_ptr manager_; }; PlatformThreadBase() = delete; PlatformThreadBase(const PlatformThreadBase&) = delete; PlatformThreadBase& operator=(const PlatformThreadBase&) = delete; // Gets the current thread id, which may be useful for logging purposes. static PlatformThreadId CurrentId(); // Gets the current thread reference, which can be used to check if // we're on the right thread quickly. static PlatformThreadRef CurrentRef(); // Get the handle representing the current thread. On Windows, this is a // pseudo handle constant which will always represent the thread using it and // hence should not be shared with other threads nor be used to differentiate // the current thread from another. static PlatformThreadHandle CurrentHandle(); // Yield the current thread so another thread can be scheduled. // // Note: this is likely not the right call to make in most situations. If this // is part of a spin loop, consider base::Lock, which likely has better tail // latency. Yielding the thread has different effects depending on the // platform, system load, etc., and can result in yielding the CPU for less // than 1us, or many tens of ms. static void YieldCurrentThread(); // Sleeps for the specified duration (real-time; ignores time overrides). // Note: The sleep duration may be in base::Time or base::TimeTicks, depending // on platform. If you're looking to use this in unit tests testing delayed // tasks, this will be unreliable - instead, use // base::test::TaskEnvironment with MOCK_TIME mode. static void Sleep(base::TimeDelta duration); // Sets the thread name visible to debuggers/tools. This will try to // initialize the context for current thread unless it's a WorkerThread. static void SetName(const std::string& name); // Gets the thread name, if previously set by SetName. static const char* GetName(); // Creates a new thread. The `stack_size` parameter can be 0 to indicate // that the default stack size should be used. Upon success, // `*thread_handle` will be assigned a handle to the newly created thread, // and `delegate`'s ThreadMain method will be executed on the newly created // thread. // NOTE: When you are done with the thread handle, you must call Join to // release system resources associated with the thread. You must ensure that // the Delegate object outlives the thread. static bool Create(size_t stack_size, Delegate* delegate, PlatformThreadHandle* thread_handle) { return CreateWithType(stack_size, delegate, thread_handle, ThreadType::kDefault); } // CreateWithType() does the same thing as Create() except the priority and // possibly the QoS of the thread is set based on `thread_type`. // `pump_type_hint` must be provided if the thread will be using a // MessagePumpForUI or MessagePumpForIO as this affects the application of // `thread_type`. static bool CreateWithType( size_t stack_size, Delegate* delegate, PlatformThreadHandle* thread_handle, ThreadType thread_type, MessagePumpType pump_type_hint = MessagePumpType::DEFAULT); // CreateNonJoinable() does the same thing as Create() except the thread // cannot be Join()'d. Therefore, it also does not output a // PlatformThreadHandle. static bool CreateNonJoinable(size_t stack_size, Delegate* delegate); // CreateNonJoinableWithType() does the same thing as CreateNonJoinable() // except the type of the thread is set based on `type`. `pump_type_hint` must // be provided if the thread will be using a MessagePumpForUI or // MessagePumpForIO as this affects the application of `thread_type`. static bool CreateNonJoinableWithType( size_t stack_size, Delegate* delegate, ThreadType thread_type, MessagePumpType pump_type_hint = MessagePumpType::DEFAULT); // Joins with a thread created via the Create function. This function blocks // the caller until the designated thread exits. This will invalidate // `thread_handle`. static void Join(PlatformThreadHandle thread_handle); // Detaches and releases the thread handle. The thread is no longer joinable // and `thread_handle` is invalidated after this call. static void Detach(PlatformThreadHandle thread_handle); // Returns true if SetCurrentThreadType() should be able to change the type // of a thread in current process from `from` to `to`. static bool CanChangeThreadType(ThreadType from, ThreadType to); // Declares the type of work running on the current thread. This will affect // things like thread priority and thread QoS (Quality of Service) to the best // of the current platform's abilities. static void SetDefaultThreadType(ThreadType thread_type); // Get the last effective `thread_type` set by SetCurrentThreadType and // currently active leases. static ThreadType GetCurrentThreadType(); // Returns a realtime period provided by `delegate`. static TimeDelta GetRealtimePeriod(Delegate* delegate); // Returns the override of task leeway if any. static std::optional GetThreadLeewayOverride(); // Returns the default thread stack size set by chrome. If we do not // explicitly set default size then returns 0. static size_t GetDefaultThreadStackSize(); // Returns the ThreadType that corresponds to the current OS thread settings. // Note that this returns a canonical ThreadType for the settings and may not // match the exact ThreadType set if multiple ThreadTypes map to the same OS // thread settings. static ThreadType GetCurrentEffectiveThreadTypeForTest(); // Returns true if the current thread has any active thread type leases. static bool CurrentThreadHasLeases(); protected: static void SetNameCommon(const std::string& name); }; #if BUILDFLAG(IS_APPLE) class BASE_EXPORT PlatformThreadApple : public PlatformThreadBase { public: // Stores the period value in TLS. static void SetCurrentThreadRealtimePeriodValue(TimeDelta realtime_period); static TimeDelta GetCurrentThreadRealtimePeriodForTest(); // Initializes features for this class. See `base::features::Init()`. static void InitializeFeatures(); }; #endif // BUILDFLAG(IS_APPLE) #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) class ThreadTypeDelegate; class BASE_EXPORT PlatformThreadLinux : public PlatformThreadBase { public: static constexpr struct sched_param kRealTimeAudioPrio = {8}; static constexpr struct sched_param kRealTimeDisplayPrio = {6}; // Sets a delegate which handles thread type changes for this process. This // must be externally synchronized with any call to SetCurrentThreadType. static void SetThreadTypeDelegate(ThreadTypeDelegate* delegate); // Toggles a specific thread's type at runtime. This can be used to // change the priority of a thread in a different process and will fail // if the calling process does not have proper permissions. The // SetCurrentThreadType() function above is preferred in favor of // security but on platforms where sandboxed processes are not allowed to // change priority this function exists to allow a non-sandboxed process // to change the priority of sandboxed threads for improved performance. // Warning: Don't use this for a main thread because that will change the // whole thread group's (i.e. process) priority. static void SetThreadType(ProcessId process_id, PlatformThreadId thread_id, ThreadType thread_type); // For a given thread id and thread type, setup the cpuset and schedtune // CGroups for the thread. static void SetThreadCgroupsForThreadType(PlatformThreadId thread_id, ThreadType thread_type); // Determine if thread_id is a background thread by looking up whether // it is in the urgent or non-urgent cpuset static bool IsThreadBackgroundedForTest(PlatformThreadId thread_id); }; #endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) #if BUILDFLAG(IS_CHROMEOS) class CrossProcessPlatformThreadDelegate; class BASE_EXPORT PlatformThreadChromeOS : public PlatformThreadLinux { public: // Sets a delegate which handles thread type changes for threads of another // process. This must be externally synchronized with any call to // SetCurrentThreadType. static void SetCrossProcessPlatformThreadDelegate( CrossProcessPlatformThreadDelegate* delegate); // Initializes features for this class. See `base::features::Init()`. static void InitializeFeatures(); // Toggles a specific thread's type at runtime. This is the ChromeOS-specific // version and includes Linux's functionality but does slightly more. See // PlatformThreadLinux's SetThreadType() header comment for Linux details. static void SetThreadType(ProcessId process_id, PlatformThreadId thread_id, ThreadType thread_type); }; #endif // BUILDFLAG(IS_CHROMEOS) // Alias to the correct platform-specific class based on preprocessor directives #if BUILDFLAG(IS_APPLE) using PlatformThread = PlatformThreadApple; #elif BUILDFLAG(IS_CHROMEOS) using PlatformThread = PlatformThreadChromeOS; #elif BUILDFLAG(IS_LINUX) using PlatformThread = PlatformThreadLinux; #else using PlatformThread = PlatformThreadBase; #endif #if BUILDFLAG(IS_ANDROID) BASE_EXPORT void SetMaxFrequencyPerProcessorOverrideForTesting( std::vector* value); // Returns whether `SetCanRunOnBigCore()` is a no-op. This is intended to help // with experiment targeting, by making sure that the base::Feature is only // queried for eligible devices. It is thus intended to be temporary, and to be // removed once the experiments conclude. BASE_EXPORT bool IsEligibleForBigCoreAffinityChange(); // Sets whether a thread is allowed to run on the big core cluster, on // configurations where this is relevant, i.e. at least 3 distinct // clusters. Otherwise this is a no-op. BASE_EXPORT void SetCanRunOnBigCore(PlatformThreadId thread_id, bool can_run); #endif // BUILDFLAG(IS_ANDROID) namespace internal { #if BUILDFLAG(IS_APPLE) using PlatformPriorityOverride = pthread_override_t; #else using PlatformPriorityOverride = bool; #endif PlatformPriorityOverride SetThreadTypeOverride( PlatformThreadHandle thread_handle, ThreadType thread_type); void RemoveThreadTypeOverride( PlatformThreadHandle thread_handle, const PlatformPriorityOverride& priority_override_handle, ThreadType initial_thread_type); void SetCurrentThreadTypeImpl(ThreadType thread_type, MessagePumpType pump_type_hint); // Manages the thread's ThreadType by allowing it to be raised via // leases. The effective thread type is the maximum of: // 1. The default thread type (set via SetDefault()). // 2. The highest active raise lease (managed via Acquire/DropRaiseLease()). // // This allows a thread to have a baseline priority while temporarily boosting // it for critical sections or based on workload requirements, without losing // track of the original priority. class BASE_EXPORT ThreadTypeManager { public: class RaiseLeases { public: void Acquire(ThreadType thread_type); void Drop(ThreadType thread_type); std::optional GetHighestLease() const; private: // Contains the number of active leases for each thread type. The positions // correspond to the enum values of ThreadType. std::array(ThreadType::kMaxValue) + 1> leases = {}; // A bitmask of the thread types that have active leases. This is used to // find the highest active lease thread type via Log2Floor. uint32_t bitmask = 0; }; ThreadTypeManager() = default; ~ThreadTypeManager() = default; ThreadTypeManager(const ThreadTypeManager&) = delete; ThreadTypeManager& operator=(const ThreadTypeManager&) = delete; void SetDefault(ThreadType type); ThreadType GetCurrent() const; void MaybeUpdate(); void AcquireRaiseLease(ThreadType type); void DropRaiseLease(ThreadType type); bool HasLeases() const; private: void SetCurrentThreadTypeImpl(ThreadType thread_type, MessagePumpType pump_type_hint); // `default_thread_type_` can be nullopt to be able to express the state // where neither SetDefault has been used, nor any leases have been created. // In this state, the thread's type isn't managed by ThreadTypeManager but is // what the OS has assigned for the thread (which may not be expressible as a // Chromium ThreadType). From this state, the first lease or SetDefault call // will apply the thread's initial type. std::optional default_thread_type_; // The thread type last applied to the thread. This is nullopt if no thread // type has ever been applied by SetDefault or leases. std::optional effective_thread_type_; RaiseLeases raise_leases_; }; } // namespace internal } // namespace base #endif // BASE_THREADING_PLATFORM_THREAD_H_