1 // Copyright 2012 The Chromium Authors 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 // WARNING: You should *NOT* be using this class directly. PlatformThread is 6 // the low-level platform-specific abstraction to the OS's threading interface. 7 // You should instead be using a message-loop driven Thread, see thread.h. 8 9 #ifndef BASE_THREADING_PLATFORM_THREAD_H_ 10 #define BASE_THREADING_PLATFORM_THREAD_H_ 11 12 #include <stddef.h> 13 14 #include <iosfwd> 15 #include <type_traits> 16 17 #include "base/base_export.h" 18 #include "base/message_loop/message_pump_type.h" 19 #include "base/process/process_handle.h" 20 #include "base/sequence_checker_impl.h" 21 #include "base/threading/platform_thread_ref.h" 22 #include "base/time/time.h" 23 #include "base/types/strong_alias.h" 24 #include "build/build_config.h" 25 #include "build/chromeos_buildflags.h" 26 #include "third_party/abseil-cpp/absl/types/optional.h" 27 28 #if BUILDFLAG(IS_WIN) 29 #include "base/win/windows_types.h" 30 #elif BUILDFLAG(IS_FUCHSIA) 31 #include <zircon/types.h> 32 #elif BUILDFLAG(IS_APPLE) 33 #include <mach/mach_types.h> 34 #elif BUILDFLAG(IS_POSIX) 35 #include <pthread.h> 36 #include <unistd.h> 37 #endif 38 39 namespace base { 40 41 // Used for logging. Always an integer value. 42 #if BUILDFLAG(IS_WIN) 43 typedef DWORD PlatformThreadId; 44 #elif BUILDFLAG(IS_FUCHSIA) 45 typedef zx_handle_t PlatformThreadId; 46 #elif BUILDFLAG(IS_APPLE) 47 typedef mach_port_t PlatformThreadId; 48 #elif BUILDFLAG(IS_POSIX) 49 typedef pid_t PlatformThreadId; 50 #endif 51 static_assert(std::is_integral_v<PlatformThreadId>, "Always an integer value."); 52 53 // Used to operate on threads. 54 class PlatformThreadHandle { 55 public: 56 #if BUILDFLAG(IS_WIN) 57 typedef void* Handle; 58 #elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) 59 typedef pthread_t Handle; 60 #endif 61 PlatformThreadHandle()62 constexpr PlatformThreadHandle() : handle_(0) {} 63 PlatformThreadHandle(Handle handle)64 explicit constexpr PlatformThreadHandle(Handle handle) : handle_(handle) {} 65 is_equal(const PlatformThreadHandle & other)66 bool is_equal(const PlatformThreadHandle& other) const { 67 return handle_ == other.handle_; 68 } 69 is_null()70 bool is_null() const { 71 return !handle_; 72 } 73 platform_handle()74 Handle platform_handle() const { 75 return handle_; 76 } 77 78 private: 79 Handle handle_; 80 }; 81 82 const PlatformThreadId kInvalidThreadId(0); 83 84 // Valid values for `thread_type` of Thread::Options, SimpleThread::Options, 85 // and SetCurrentThreadType(), listed in increasing order of importance. 86 // 87 // It is up to each platform-specific implementation what these translate to. 88 // Callers should avoid setting different ThreadTypes on different platforms 89 // (ifdefs) at all cost, instead the platform differences should be encoded in 90 // the platform-specific implementations. Some implementations may treat 91 // adjacent ThreadTypes in this enum as equivalent. 92 // 93 // Reach out to //base/task/OWNERS (scheduler-dev@chromium.org) before changing 94 // thread type assignments in your component, as such decisions affect the whole 95 // of Chrome. 96 // 97 // Refer to PlatformThreadTest.SetCurrentThreadTypeTest in 98 // platform_thread_unittest.cc for the most up-to-date state of each platform's 99 // handling of ThreadType. 100 enum class ThreadType : int { 101 // Suitable for threads that have the least urgency and lowest priority, and 102 // can be interrupted or delayed by other types. 103 kBackground, 104 // Suitable for threads that are less important than normal type, and can be 105 // interrupted or delayed by threads with kDefault type. 106 kUtility, 107 // Suitable for threads that produce user-visible artifacts but aren't 108 // latency sensitive. The underlying platform will try to be economic 109 // in its usage of resources for this thread, if possible. 110 kResourceEfficient, 111 // Default type. The thread priority or quality of service will be set to 112 // platform default. In Chrome, this is suitable for handling user 113 // interactions (input), only display and audio can get a higher priority. 114 kDefault, 115 // Suitable for threads which are critical to compositing the foreground 116 // content. 117 kCompositing, 118 // Suitable for display critical threads. 119 kDisplayCritical, 120 // Suitable for low-latency, glitch-resistant audio. 121 kRealtimeAudio, 122 kMaxValue = kRealtimeAudio, 123 }; 124 125 // Cross-platform mapping of physical thread priorities. Used by tests to verify 126 // the underlying effects of SetCurrentThreadType. 127 enum class ThreadPriorityForTest : int { 128 kBackground, 129 kUtility, 130 kResourceEfficient, 131 kNormal, 132 kCompositing, 133 kDisplay, 134 kRealtimeAudio, 135 kMaxValue = kRealtimeAudio, 136 }; 137 138 // A namespace for low-level thread functions. 139 class BASE_EXPORT PlatformThreadBase { 140 public: 141 // Implement this interface to run code on a background thread. Your 142 // ThreadMain method will be called on the newly created thread. 143 class BASE_EXPORT Delegate { 144 public: 145 virtual void ThreadMain() = 0; 146 147 #if BUILDFLAG(IS_APPLE) 148 // TODO: Move this to the PlatformThreadApple class. 149 // The interval at which the thread expects to have work to do. Zero if 150 // unknown. (Example: audio buffer duration for real-time audio.) Is used to 151 // optimize the thread real-time behavior. Is called on the newly created 152 // thread before ThreadMain(). 153 virtual TimeDelta GetRealtimePeriod(); 154 #endif 155 156 protected: 157 virtual ~Delegate() = default; 158 }; 159 160 PlatformThreadBase() = delete; 161 PlatformThreadBase(const PlatformThreadBase&) = delete; 162 PlatformThreadBase& operator=(const PlatformThreadBase&) = delete; 163 164 // Gets the current thread id, which may be useful for logging purposes. 165 static PlatformThreadId CurrentId(); 166 167 // Gets the current thread reference, which can be used to check if 168 // we're on the right thread quickly. 169 static PlatformThreadRef CurrentRef(); 170 171 // Get the handle representing the current thread. On Windows, this is a 172 // pseudo handle constant which will always represent the thread using it and 173 // hence should not be shared with other threads nor be used to differentiate 174 // the current thread from another. 175 static PlatformThreadHandle CurrentHandle(); 176 177 // Yield the current thread so another thread can be scheduled. 178 // 179 // Note: this is likely not the right call to make in most situations. If this 180 // is part of a spin loop, consider base::Lock, which likely has better tail 181 // latency. Yielding the thread has different effects depending on the 182 // platform, system load, etc., and can result in yielding the CPU for less 183 // than 1us, or many tens of ms. 184 static void YieldCurrentThread(); 185 186 // Sleeps for the specified duration (real-time; ignores time overrides). 187 // Note: The sleep duration may be in base::Time or base::TimeTicks, depending 188 // on platform. If you're looking to use this in unit tests testing delayed 189 // tasks, this will be unreliable - instead, use 190 // base::test::TaskEnvironment with MOCK_TIME mode. 191 static void Sleep(base::TimeDelta duration); 192 193 // Sets the thread name visible to debuggers/tools. This will try to 194 // initialize the context for current thread unless it's a WorkerThread. 195 static void SetName(const std::string& name); 196 197 // Gets the thread name, if previously set by SetName. 198 static const char* GetName(); 199 200 // Creates a new thread. The `stack_size` parameter can be 0 to indicate 201 // that the default stack size should be used. Upon success, 202 // `*thread_handle` will be assigned a handle to the newly created thread, 203 // and `delegate`'s ThreadMain method will be executed on the newly created 204 // thread. 205 // NOTE: When you are done with the thread handle, you must call Join to 206 // release system resources associated with the thread. You must ensure that 207 // the Delegate object outlives the thread. Create(size_t stack_size,Delegate * delegate,PlatformThreadHandle * thread_handle)208 static bool Create(size_t stack_size, 209 Delegate* delegate, 210 PlatformThreadHandle* thread_handle) { 211 return CreateWithType(stack_size, delegate, thread_handle, 212 ThreadType::kDefault); 213 } 214 215 // CreateWithType() does the same thing as Create() except the priority and 216 // possibly the QoS of the thread is set based on `thread_type`. 217 // `pump_type_hint` must be provided if the thread will be using a 218 // MessagePumpForUI or MessagePumpForIO as this affects the application of 219 // `thread_type`. 220 static bool CreateWithType( 221 size_t stack_size, 222 Delegate* delegate, 223 PlatformThreadHandle* thread_handle, 224 ThreadType thread_type, 225 MessagePumpType pump_type_hint = MessagePumpType::DEFAULT); 226 227 // CreateNonJoinable() does the same thing as Create() except the thread 228 // cannot be Join()'d. Therefore, it also does not output a 229 // PlatformThreadHandle. 230 static bool CreateNonJoinable(size_t stack_size, Delegate* delegate); 231 232 // CreateNonJoinableWithType() does the same thing as CreateNonJoinable() 233 // except the type of the thread is set based on `type`. `pump_type_hint` must 234 // be provided if the thread will be using a MessagePumpForUI or 235 // MessagePumpForIO as this affects the application of `thread_type`. 236 static bool CreateNonJoinableWithType( 237 size_t stack_size, 238 Delegate* delegate, 239 ThreadType thread_type, 240 MessagePumpType pump_type_hint = MessagePumpType::DEFAULT); 241 242 // Joins with a thread created via the Create function. This function blocks 243 // the caller until the designated thread exits. This will invalidate 244 // `thread_handle`. 245 static void Join(PlatformThreadHandle thread_handle); 246 247 // Detaches and releases the thread handle. The thread is no longer joinable 248 // and `thread_handle` is invalidated after this call. 249 static void Detach(PlatformThreadHandle thread_handle); 250 251 // Returns true if SetCurrentThreadType() should be able to change the type 252 // of a thread in current process from `from` to `to`. 253 static bool CanChangeThreadType(ThreadType from, ThreadType to); 254 255 // Declares the type of work running on the current thread. This will affect 256 // things like thread priority and thread QoS (Quality of Service) to the best 257 // of the current platform's abilities. 258 static void SetCurrentThreadType(ThreadType thread_type); 259 260 // Get the last `thread_type` set by SetCurrentThreadType, no matter if the 261 // underlying priority successfully changed or not. 262 static ThreadType GetCurrentThreadType(); 263 264 // Returns a realtime period provided by `delegate`. 265 static TimeDelta GetRealtimePeriod(Delegate* delegate); 266 267 // Returns the override of task leeway if any. 268 static absl::optional<TimeDelta> GetThreadLeewayOverride(); 269 270 // Returns the default thread stack size set by chrome. If we do not 271 // explicitly set default size then returns 0. 272 static size_t GetDefaultThreadStackSize(); 273 274 static ThreadPriorityForTest GetCurrentThreadPriorityForTest(); 275 276 protected: 277 static void SetNameCommon(const std::string& name); 278 }; 279 280 #if BUILDFLAG(IS_APPLE) 281 class BASE_EXPORT PlatformThreadApple : public PlatformThreadBase { 282 public: 283 // Stores the period value in TLS. 284 static void SetCurrentThreadRealtimePeriodValue(TimeDelta realtime_period); 285 286 // Signals that the feature list has been initialized which allows to check 287 // the feature's value now and initialize state. This prevents race 288 // conditions where the feature is being checked while it is being 289 // initialized, which can cause a crash. 290 static void InitFeaturesPostFieldTrial(); 291 }; 292 #endif // BUILDFLAG(IS_APPLE) 293 294 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) 295 class ThreadTypeDelegate; 296 using IsViaIPC = base::StrongAlias<class IsViaIPCTag, bool>; 297 298 class BASE_EXPORT PlatformThreadLinux : public PlatformThreadBase { 299 public: 300 static constexpr struct sched_param kRealTimeAudioPrio = {8}; 301 static constexpr struct sched_param kRealTimeDisplayPrio = {6}; 302 303 // Sets a delegate which handles thread type changes for this process. This 304 // must be externally synchronized with any call to SetCurrentThreadType. 305 static void SetThreadTypeDelegate(ThreadTypeDelegate* delegate); 306 307 // Toggles a specific thread's type at runtime. This can be used to 308 // change the priority of a thread in a different process and will fail 309 // if the calling process does not have proper permissions. The 310 // SetCurrentThreadType() function above is preferred in favor of 311 // security but on platforms where sandboxed processes are not allowed to 312 // change priority this function exists to allow a non-sandboxed process 313 // to change the priority of sandboxed threads for improved performance. 314 // Warning: Don't use this for a main thread because that will change the 315 // whole thread group's (i.e. process) priority. 316 static void SetThreadType(PlatformThreadId process_id, 317 PlatformThreadId thread_id, 318 ThreadType thread_type, 319 IsViaIPC via_ipc); 320 321 // For a given thread id and thread type, setup the cpuset and schedtune 322 // CGroups for the thread. 323 static void SetThreadCgroupsForThreadType(PlatformThreadId thread_id, 324 ThreadType thread_type); 325 326 // Determine if thread_id is a background thread by looking up whether 327 // it is in the urgent or non-urgent cpuset 328 static bool IsThreadBackgroundedForTest(PlatformThreadId thread_id); 329 }; 330 #endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) 331 332 #if BUILDFLAG(IS_CHROMEOS) 333 334 class BASE_EXPORT PlatformThreadChromeOS : public PlatformThreadLinux { 335 public: 336 // Signals that the feature list has been initialized. Used for preventing 337 // race conditions and crashes, see comments in PlatformThreadApple. 338 static void InitFeaturesPostFieldTrial(); 339 340 // Toggles a specific thread's type at runtime. This is the ChromeOS-specific 341 // version and includes Linux's functionality but does slightly more. See 342 // PlatformThreadLinux's SetThreadType() header comment for Linux details. 343 static void SetThreadType(PlatformThreadId process_id, 344 PlatformThreadId thread_id, 345 ThreadType thread_type, 346 IsViaIPC via_ipc); 347 348 // Returns true if the feature for backgrounding of threads is enabled. 349 static bool IsThreadsBgFeatureEnabled(); 350 351 // Returns true if the feature for setting display threads to RT is enabled. 352 static bool IsDisplayThreadsRtFeatureEnabled(); 353 354 // Set a specific thread as backgrounded. This is called when the process 355 // moves to and from the background and changes have to be made to each of its 356 // thread's scheduling attributes. 357 static void SetThreadBackgrounded(ProcessId process_id, 358 PlatformThreadId thread_id, 359 bool backgrounded); 360 361 // Returns the thread type of a thread given its thread id. 362 static absl::optional<ThreadType> GetThreadTypeFromThreadId( 363 ProcessId process_id, 364 PlatformThreadId thread_id); 365 366 // Returns a SequenceChecker which should be used to verify that all 367 // cross-process priority changes are performed without races. 368 static SequenceCheckerImpl& GetCrossProcessThreadPrioritySequenceChecker(); 369 }; 370 #endif // BUILDFLAG(IS_CHROMEOS) 371 372 // Alias to the correct platform-specific class based on preprocessor directives 373 #if BUILDFLAG(IS_APPLE) 374 using PlatformThread = PlatformThreadApple; 375 #elif BUILDFLAG(IS_CHROMEOS) 376 using PlatformThread = PlatformThreadChromeOS; 377 #elif BUILDFLAG(IS_LINUX) 378 using PlatformThread = PlatformThreadLinux; 379 #else 380 using PlatformThread = PlatformThreadBase; 381 #endif 382 383 namespace internal { 384 385 void SetCurrentThreadType(ThreadType thread_type, 386 MessagePumpType pump_type_hint); 387 388 void SetCurrentThreadTypeImpl(ThreadType thread_type, 389 MessagePumpType pump_type_hint); 390 391 } // namespace internal 392 393 } // namespace base 394 395 #endif // BASE_THREADING_PLATFORM_THREAD_H_ 396