// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin // SPDX-License-Identifier: CC-BY-NC-ND-4.0 #pragma once #include "types.h" #if defined(__APPLE__) #include #elif !defined(_WIN32) #include #endif #include #include namespace Threading { extern u64 GetThreadCpuTime(); extern u64 GetThreadTicksPerSecond(); /// Set the name of the current thread extern void SetNameOfCurrentThread(const char* name); // Releases a timeslice to other threads. extern void Timeslice(); // -------------------------------------------------------------------------------------- // ThreadHandle // -------------------------------------------------------------------------------------- // Abstracts an OS's handle to a thread, closing the handle when necessary. Currently, // only used for getting the CPU time for a thread. // class ThreadHandle { public: ThreadHandle(); ThreadHandle(ThreadHandle&& handle); ThreadHandle(const ThreadHandle& handle); ~ThreadHandle(); /// Returns a new handle for the calling thread. static ThreadHandle GetForCallingThread(); ThreadHandle& operator=(ThreadHandle&& handle); ThreadHandle& operator=(const ThreadHandle& handle); operator void*() const { return m_native_handle; } operator bool() const { return (m_native_handle != nullptr); } /// Returns the amount of CPU time consumed by the thread, at the GetThreadTicksPerSecond() frequency. u64 GetCPUTime() const; /// Sets the affinity for a thread to the specified processors. /// Obviously, only works up to 64 processors. bool SetAffinity(u64 processor_mask) const; /// Returns true if the calling thread matches this handle. bool IsCallingThread() const; #ifdef __APPLE__ /// Only available on MacOS, sets a period/maximum time for the scheduler. bool SetTimeConstraints(bool enabled, u64 period, u64 typical_time, u64 maximum_time) const; #endif protected: void* m_native_handle = nullptr; // We need the thread ID for affinity adjustments on Linux. #if defined(_WIN32) || defined(__linux__) unsigned int m_native_id = 0; u32 m_stack_size = 0; #endif }; // -------------------------------------------------------------------------------------- // Thread // -------------------------------------------------------------------------------------- // Abstracts a native thread in a lightweight manner. Provides more functionality than // std::thread (allowing stack size adjustments). // class Thread : public ThreadHandle { public: using EntryPoint = std::function; Thread(); Thread(Thread&& thread); Thread(const Thread&) = delete; Thread(EntryPoint func); ~Thread(); ThreadHandle& operator=(Thread&& thread); ThreadHandle& operator=(const Thread& handle) = delete; ALWAYS_INLINE bool Joinable() const { return (m_native_handle != nullptr); } ALWAYS_INLINE u32 GetStackSize() const { return m_stack_size; } /// Sets the stack size for the thread. Do not call if the thread has already been started. void SetStackSize(u32 size); bool Start(EntryPoint func); void Detach(); void Join(); protected: #ifdef _WIN32 static unsigned __stdcall ThreadProc(void* param); #else static void* ThreadProc(void* param); #endif #if !defined(_WIN32) && !defined(__linux__) // Stored in ThreadHandle to save 8 bytes. u32 m_stack_size = 0; #endif }; /// A semaphore that requires a system call to wake/sleep. class KernelSemaphore { public: KernelSemaphore(); KernelSemaphore(const KernelSemaphore&) = delete; ~KernelSemaphore(); KernelSemaphore& operator=(const KernelSemaphore&) = delete; void Post(); void Wait(); bool TryWait(); private: #if defined(_WIN32) void* m_sema; #elif defined(__APPLE__) semaphore_t m_sema; #else sem_t m_sema; #endif }; } // namespace Threading