/** ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ #include "xenia/base/threading.h" #include "xenia/base/assert.h" #include "xenia/base/chrono_steady_cast.h" #include "xenia/base/platform.h" #include "xenia/base/threading_timer_queue.h" #include #include #include #include #include #include #include #include #include #include #include #include #if XE_PLATFORM_ANDROID #include #include "xenia/base/main_android.h" #include "xenia/base/string_util.h" #endif #if XE_PLATFORM_LINUX // SIGEV_THREAD_ID in timer_create(...) is a Linux extension #define XE_HAS_SIGEV_THREAD_ID 1 #ifdef __GLIBC__ #define sigev_notify_thread_id _sigev_un._tid #endif #if __GLIBC__ == 2 && __GLIBC_MINOR__ < 30 #define gettid() syscall(SYS_gettid) #endif #else #define XE_HAS_SIGEV_THREAD_ID 0 #endif namespace xe { namespace threading { #if XE_PLATFORM_ANDROID // May be null if no dynamically loaded functions are required. static void* android_libc_; // API 26+. static int (*android_pthread_getname_np_)(pthread_t pthread, char* buf, size_t n); void AndroidInitialize() { if (xe::GetAndroidApiLevel() >= 26) { android_libc_ = dlopen("libc.so", RTLD_NOW); assert_not_null(android_libc_); if (android_libc_) { android_pthread_getname_np_ = reinterpret_cast( dlsym(android_libc_, "pthread_getname_np")); assert_not_null(android_pthread_getname_np_); } } } void AndroidShutdown() { android_pthread_getname_np_ = nullptr; if (android_libc_) { dlclose(android_libc_); android_libc_ = nullptr; } } #endif template inline timespec DurationToTimeSpec( std::chrono::duration<_Rep, _Period> duration) { auto nanoseconds = std::chrono::duration_cast(duration); auto div = ldiv(nanoseconds.count(), 1000000000L); return timespec{div.quot, div.rem}; } // Thread interruption is done using user-defined signals // This implementation uses the SIGRTMAX - SIGRTMIN to signal to a thread // gdb tip, for SIG = SIGRTMIN + SignalType : handle SIG nostop // lldb tip, for SIG = SIGRTMIN + SignalType : process handle SIG -s false enum class SignalType { kThreadSuspend, kThreadUserCallback, #if XE_PLATFORM_ANDROID // pthread_cancel is not available on Android, using a signal handler for // simplified PTHREAD_CANCEL_ASYNCHRONOUS-like behavior - not disabling // cancellation currently, so should be enough. kThreadTerminate, #endif k_Count }; int GetSystemSignal(SignalType num) { auto result = SIGRTMIN + static_cast(num); assert_true(result < SIGRTMAX); return result; } SignalType GetSystemSignalType(int num) { return static_cast(num - SIGRTMIN); } thread_local std::array(SignalType::k_Count)> signal_handler_installed = {}; static void signal_handler(int signal, siginfo_t* info, void* context); void install_signal_handler(SignalType type) { if (signal_handler_installed[static_cast(type)]) return; struct sigaction action {}; action.sa_flags = SA_SIGINFO; action.sa_sigaction = signal_handler; sigemptyset(&action.sa_mask); if (sigaction(GetSystemSignal(type), &action, nullptr) == -1) signal_handler_installed[static_cast(type)] = true; } // TODO(dougvj) void EnableAffinityConfiguration() {} // uint64_t ticks() { return mach_absolute_time(); } uint32_t current_thread_system_id() { return static_cast(syscall(SYS_gettid)); } void MaybeYield() { sched_yield(); __sync_synchronize(); } void SyncMemory() { __sync_synchronize(); } void Sleep(std::chrono::microseconds duration) { timespec rqtp = DurationToTimeSpec(duration); timespec rmtp = {}; auto p_rqtp = &rqtp; auto p_rmtp = &rmtp; int ret = 0; do { ret = nanosleep(p_rqtp, p_rmtp); // Swap requested for remaining in case of signal interruption // in which case, we start sleeping again for the remainder std::swap(p_rqtp, p_rmtp); } while (ret == -1 && errno == EINTR); } // TODO(bwrsandman) Implement by allowing alert interrupts from IO operations thread_local bool alertable_state_ = false; SleepResult AlertableSleep(std::chrono::microseconds duration) { alertable_state_ = true; Sleep(duration); alertable_state_ = false; return SleepResult::kSuccess; } TlsHandle AllocateTlsHandle() { auto key = static_cast(-1); auto res = pthread_key_create(&key, nullptr); assert_zero(res); assert_true(key != static_cast(-1)); return static_cast(key); } bool FreeTlsHandle(TlsHandle handle) { return pthread_key_delete(static_cast(handle)) == 0; } uintptr_t GetTlsValue(TlsHandle handle) { return reinterpret_cast( pthread_getspecific(static_cast(handle))); } bool SetTlsValue(TlsHandle handle, uintptr_t value) { return pthread_setspecific(static_cast(handle), reinterpret_cast(value)) == 0; } class PosixConditionBase { public: virtual bool Signal() = 0; WaitResult Wait(std::chrono::milliseconds timeout) { bool executed; auto predicate = [this] { return this->signaled(); }; auto lock = std::unique_lock(mutex_); if (predicate()) { executed = true; } else { if (timeout == std::chrono::milliseconds::max()) { cond_.wait(lock, predicate); executed = true; // Did not time out; } else { executed = cond_.wait_for(lock, timeout, predicate); } } if (executed) { post_execution(); return WaitResult::kSuccess; } else { return WaitResult::kTimeout; } } static std::pair WaitMultiple( std::vector&& handles, bool wait_all, std::chrono::milliseconds timeout) { assert_true(handles.size() > 0); // Construct a condition for all or any depending on wait_all std::function predicate; { using iter_t = std::vector::const_iterator; const auto predicate_inner = [](auto h) { return h->signaled(); }; const auto operation = wait_all ? std::all_of : std::any_of; predicate = [&handles, operation, predicate_inner] { return operation(handles.cbegin(), handles.cend(), predicate_inner); }; } // TODO(bwrsandman, Triang3l) This is controversial, see issue #1677 // This will probably cause a deadlock on the next thread doing any waiting // if the thread is suspended between locking and waiting std::unique_lock lock(PosixConditionBase::mutex_); bool wait_success = true; // If the timeout is infinite, wait without timeout. // The predicate will be checked before beginning the wait if (timeout == std::chrono::milliseconds::max()) { PosixConditionBase::cond_.wait(lock, predicate); } else { // Wait with timeout. wait_success = PosixConditionBase::cond_.wait_for(lock, timeout, predicate); } if (wait_success) { auto first_signaled = std::numeric_limits::max(); for (auto i = 0u; i < handles.size(); ++i) { if (handles[i]->signaled()) { if (first_signaled > i) { first_signaled = i; } handles[i]->post_execution(); if (!wait_all) break; } } assert_true(std::numeric_limits::max() != first_signaled); return std::make_pair(WaitResult::kSuccess, first_signaled); } else { return std::make_pair(WaitResult::kTimeout, 0); } } virtual void* native_handle() const { return cond_.native_handle(); } protected: inline virtual bool signaled() const = 0; inline virtual void post_execution() = 0; static std::condition_variable cond_; static std::mutex mutex_; }; std::condition_variable PosixConditionBase::cond_; std::mutex PosixConditionBase::mutex_; // There really is no native POSIX handle for a single wait/signal construct // pthreads is at a lower level with more handles for such a mechanism. // This simple wrapper class functions as our handle and uses conditional // variables for waits and signals. template class PosixCondition {}; template <> class PosixCondition : public PosixConditionBase { public: PosixCondition(bool manual_reset, bool initial_state) : signal_(initial_state), manual_reset_(manual_reset) {} virtual ~PosixCondition() = default; bool Signal() override { auto lock = std::unique_lock(mutex_); signal_ = true; cond_.notify_all(); return true; } void Reset() { auto lock = std::unique_lock(mutex_); signal_ = false; } private: inline bool signaled() const override { return signal_; } inline void post_execution() override { if (!manual_reset_) { signal_ = false; } } bool signal_; const bool manual_reset_; }; template <> class PosixCondition : public PosixConditionBase { public: PosixCondition(uint32_t initial_count, uint32_t maximum_count) : count_(initial_count), maximum_count_(maximum_count) {} bool Signal() override { return Release(1, nullptr); } bool Release(uint32_t release_count, int* out_previous_count) { if (maximum_count_ - count_ >= release_count) { auto lock = std::unique_lock(mutex_); if (out_previous_count) *out_previous_count = count_; count_ += release_count; cond_.notify_all(); return true; } return false; } private: inline bool signaled() const override { return count_ > 0; } inline void post_execution() override { count_--; cond_.notify_all(); } uint32_t count_; const uint32_t maximum_count_; }; template <> class PosixCondition : public PosixConditionBase { public: explicit PosixCondition(bool initial_owner) : count_(0) { if (initial_owner) { count_ = 1; owner_ = std::this_thread::get_id(); } } bool Signal() override { return Release(); } bool Release() { if (owner_ == std::this_thread::get_id() && count_ > 0) { auto lock = std::unique_lock(mutex_); --count_; // Free to be acquired by another thread if (count_ == 0) { cond_.notify_all(); } return true; } return false; } void* native_handle() const override { return mutex_.native_handle(); } private: inline bool signaled() const override { return count_ == 0 || owner_ == std::this_thread::get_id(); } inline void post_execution() override { count_++; owner_ = std::this_thread::get_id(); } uint32_t count_; std::thread::id owner_; }; template <> class PosixCondition : public PosixConditionBase { public: explicit PosixCondition(bool manual_reset) : callback_(nullptr), signal_(false), manual_reset_(manual_reset) {} virtual ~PosixCondition() { Cancel(); } bool Signal() override { std::lock_guard lock(mutex_); signal_ = true; cond_.notify_all(); return true; } void SetOnce(std::chrono::steady_clock::time_point due_time, std::function opt_callback) { Cancel(); std::lock_guard lock(mutex_); callback_ = std::move(opt_callback); signal_ = false; wait_item_ = QueueTimerOnce(&CompletionRoutine, this, due_time); } void SetRepeating(std::chrono::steady_clock::time_point due_time, std::chrono::milliseconds period, std::function opt_callback) { Cancel(); std::lock_guard lock(mutex_); callback_ = std::move(opt_callback); signal_ = false; wait_item_ = QueueTimerRecurring(&CompletionRoutine, this, due_time, period); } void Cancel() { if (auto wait_item = wait_item_.lock()) { wait_item->Disarm(); } } void* native_handle() const override { assert_always(); return nullptr; } private: static void CompletionRoutine(void* userdata) { assert_not_null(userdata); auto timer = reinterpret_cast*>(userdata); timer->Signal(); // As the callback may reset the timer, store local. std::function callback; { std::lock_guard lock(timer->mutex_); callback = timer->callback_; } if (callback) { callback(); } } private: inline bool signaled() const override { return signal_; } inline void post_execution() override { if (!manual_reset_) { signal_ = false; } } std::weak_ptr wait_item_; std::function callback_; volatile bool signal_; const bool manual_reset_; }; struct ThreadStartData { std::function start_routine; bool create_suspended; Thread* thread_obj; }; template <> class PosixCondition : public PosixConditionBase { enum class State { kUninitialized, kRunning, kSuspended, kFinished, }; public: PosixCondition() : thread_(0), signaled_(false), exit_code_(0), state_(State::kUninitialized), suspend_count_(0) { #if XE_PLATFORM_ANDROID android_pre_api_26_name_[0] = '\0'; #endif } bool Initialize(Thread::CreationParameters params, ThreadStartData* start_data) { start_data->create_suspended = params.create_suspended; pthread_attr_t attr; if (pthread_attr_init(&attr) != 0) return false; if (pthread_attr_setstacksize(&attr, params.stack_size) != 0) { pthread_attr_destroy(&attr); return false; } if (params.initial_priority != 0) { sched_param sched{}; sched.sched_priority = params.initial_priority + 1; if (pthread_attr_setschedpolicy(&attr, SCHED_FIFO) != 0) { pthread_attr_destroy(&attr); return false; } if (pthread_attr_setschedparam(&attr, &sched) != 0) { pthread_attr_destroy(&attr); return false; } } if (pthread_create(&thread_, &attr, ThreadStartRoutine, start_data) != 0) { pthread_attr_destroy(&attr); return false; } pthread_attr_destroy(&attr); return true; } /// Constructor for existing thread. This should only happen once called by /// Thread::GetCurrentThread() on the main thread explicit PosixCondition(pthread_t thread) : thread_(thread), signaled_(false), exit_code_(0), state_(State::kRunning) { #if XE_PLATFORM_ANDROID android_pre_api_26_name_[0] = '\0'; #endif } virtual ~PosixCondition() { if (thread_ && !signaled_) { #if XE_PLATFORM_ANDROID if (pthread_kill(thread_, GetSystemSignal(SignalType::kThreadTerminate)) != 0) { assert_always(); } #else if (pthread_cancel(thread_) != 0) { assert_always(); } #endif if (pthread_join(thread_, nullptr) != 0) { assert_always(); } } } bool Signal() override { return true; } std::string name() const { WaitStarted(); auto result = std::array{'\0'}; std::unique_lock lock(state_mutex_); if (state_ != State::kUninitialized && state_ != State::kFinished) { #if XE_PLATFORM_ANDROID // pthread_getname_np was added in API 26 - below that, store the name in // this object, which may be only modified through Xenia threading, but // should be enough in most cases. if (android_pthread_getname_np_) { if (android_pthread_getname_np_(thread_, result.data(), result.size() - 1) != 0) { assert_always(); } } else { std::lock_guard lock(android_pre_api_26_name_mutex_); std::strcpy(result.data(), android_pre_api_26_name_); } #else if (pthread_getname_np(thread_, result.data(), result.size() - 1) != 0) { assert_always(); } #endif } return std::string(result.data()); } void set_name(const std::string& name) { WaitStarted(); std::unique_lock lock(state_mutex_); if (state_ != State::kUninitialized && state_ != State::kFinished) { pthread_setname_np(thread_, std::string(name).c_str()); #if XE_PLATFORM_ANDROID SetAndroidPreApi26Name(name); #endif } } #if XE_PLATFORM_ANDROID void SetAndroidPreApi26Name(const std::string_view name) { if (android_pthread_getname_np_) { return; } std::lock_guard lock(android_pre_api_26_name_mutex_); xe::string_util::copy_truncating(android_pre_api_26_name_, name, xe::countof(android_pre_api_26_name_)); } #endif uint32_t system_id() const { return static_cast(thread_); } uint64_t affinity_mask() { WaitStarted(); cpu_set_t cpu_set; #if XE_PLATFORM_ANDROID if (sched_getaffinity(pthread_gettid_np(thread_), sizeof(cpu_set_t), &cpu_set) != 0) { assert_always(); } #else if (pthread_getaffinity_np(thread_, sizeof(cpu_set_t), &cpu_set) != 0) { assert_always(); } #endif uint64_t result = 0; auto cpu_count = std::min(CPU_SETSIZE, 64); for (auto i = 0u; i < cpu_count; i++) { auto set = CPU_ISSET(i, &cpu_set); result |= set << i; } return result; } void set_affinity_mask(uint64_t mask) { WaitStarted(); cpu_set_t cpu_set; CPU_ZERO(&cpu_set); for (auto i = 0u; i < 64; i++) { if (mask & (1 << i)) { CPU_SET(i, &cpu_set); } } #if XE_PLATFORM_ANDROID if (sched_setaffinity(pthread_gettid_np(thread_), sizeof(cpu_set_t), &cpu_set) != 0) { assert_always(); } #else if (pthread_setaffinity_np(thread_, sizeof(cpu_set_t), &cpu_set) != 0) { assert_always(); } #endif } int priority() { WaitStarted(); int policy; sched_param param{}; int ret = pthread_getschedparam(thread_, &policy, ¶m); if (ret != 0) { return -1; } return param.sched_priority; } void set_priority(int new_priority) { WaitStarted(); sched_param param{}; param.sched_priority = new_priority; if (pthread_setschedparam(thread_, SCHED_FIFO, ¶m) != 0) assert_always(); } void QueueUserCallback(std::function callback) { WaitStarted(); std::unique_lock lock(callback_mutex_); user_callback_ = std::move(callback); sigval value{}; value.sival_ptr = this; #if XE_PLATFORM_ANDROID sigqueue(pthread_gettid_np(thread_), GetSystemSignal(SignalType::kThreadUserCallback), value); #else pthread_sigqueue(thread_, GetSystemSignal(SignalType::kThreadUserCallback), value); #endif } void CallUserCallback() { std::unique_lock lock(callback_mutex_); user_callback_(); } bool Resume(uint32_t* out_previous_suspend_count = nullptr) { if (out_previous_suspend_count) { *out_previous_suspend_count = 0; } WaitStarted(); std::unique_lock lock(state_mutex_); if (state_ != State::kSuspended) return false; if (out_previous_suspend_count) { *out_previous_suspend_count = suspend_count_; } --suspend_count_; state_signal_.notify_all(); return true; } bool Suspend(uint32_t* out_previous_suspend_count = nullptr) { if (out_previous_suspend_count) { *out_previous_suspend_count = 0; } WaitStarted(); { if (out_previous_suspend_count) { *out_previous_suspend_count = suspend_count_; } state_ = State::kSuspended; ++suspend_count_; } int result = pthread_kill(thread_, GetSystemSignal(SignalType::kThreadSuspend)); return result == 0; } void Terminate(int exit_code) { bool is_current_thread = pthread_self() == thread_; { std::unique_lock lock(state_mutex_); if (state_ == State::kFinished) { if (is_current_thread) { // This is really bad. Some thread must have called Terminate() on us // just before we decided to terminate ourselves assert_always(); for (;;) { // Wait for pthread_cancel() to actually happen. } } return; } state_ = State::kFinished; } { std::lock_guard lock(mutex_); exit_code_ = exit_code; signaled_ = true; cond_.notify_all(); } if (is_current_thread) { pthread_exit(reinterpret_cast(exit_code)); } else { #ifdef XE_PLATFORM_ANDROID if (pthread_kill(thread_, GetSystemSignal(SignalType::kThreadTerminate)) != 0) { assert_always(); } #else if (pthread_cancel(thread_) != 0) { assert_always(); } #endif } } void WaitStarted() const { std::unique_lock lock(state_mutex_); state_signal_.wait(lock, [this] { return state_ != State::kUninitialized; }); } /// Set state to suspended and wait until it reset by another thread void WaitSuspended() { std::unique_lock lock(state_mutex_); state_signal_.wait(lock, [this] { return suspend_count_ == 0; }); state_ = State::kRunning; } void* native_handle() const override { return reinterpret_cast(thread_); } private: static void* ThreadStartRoutine(void* parameter); inline bool signaled() const override { return signaled_; } inline void post_execution() override { if (thread_) { pthread_join(thread_, nullptr); } } pthread_t thread_; bool signaled_; int exit_code_; volatile State state_; volatile uint32_t suspend_count_; mutable std::mutex state_mutex_; mutable std::mutex callback_mutex_; mutable std::condition_variable state_signal_; std::function user_callback_; #if XE_PLATFORM_ANDROID // Name accessible via name() on Android before API 26 which added // pthread_getname_np. mutable std::mutex android_pre_api_26_name_mutex_; char android_pre_api_26_name_[16]; #endif }; class PosixWaitHandle { public: virtual PosixConditionBase& condition() = 0; }; // This wraps a condition object as our handle because posix has no single // native handle for higher level concurrency constructs such as semaphores template class PosixConditionHandle : public T, public PosixWaitHandle { public: PosixConditionHandle() = default; explicit PosixConditionHandle(bool); explicit PosixConditionHandle(pthread_t thread); PosixConditionHandle(bool manual_reset, bool initial_state); PosixConditionHandle(uint32_t initial_count, uint32_t maximum_count); ~PosixConditionHandle() override = default; PosixCondition& condition() override { return handle_; } void* native_handle() const override { return handle_.native_handle(); } protected: PosixCondition handle_; friend PosixCondition; }; template <> PosixConditionHandle::PosixConditionHandle(uint32_t initial_count, uint32_t maximum_count) : handle_(initial_count, maximum_count) {} template <> PosixConditionHandle::PosixConditionHandle(bool initial_owner) : handle_(initial_owner) {} template <> PosixConditionHandle::PosixConditionHandle(bool manual_reset) : handle_(manual_reset) {} template <> PosixConditionHandle::PosixConditionHandle(bool manual_reset, bool initial_state) : handle_(manual_reset, initial_state) {} template <> PosixConditionHandle::PosixConditionHandle(pthread_t thread) : handle_(thread) {} WaitResult Wait(WaitHandle* wait_handle, bool is_alertable, std::chrono::milliseconds timeout) { auto posix_wait_handle = dynamic_cast(wait_handle); if (posix_wait_handle == nullptr) { return WaitResult::kFailed; } if (is_alertable) alertable_state_ = true; auto result = posix_wait_handle->condition().Wait(timeout); if (is_alertable) alertable_state_ = false; return result; } WaitResult SignalAndWait(WaitHandle* wait_handle_to_signal, WaitHandle* wait_handle_to_wait_on, bool is_alertable, std::chrono::milliseconds timeout) { auto result = WaitResult::kFailed; auto posix_wait_handle_to_signal = dynamic_cast(wait_handle_to_signal); auto posix_wait_handle_to_wait_on = dynamic_cast(wait_handle_to_wait_on); if (posix_wait_handle_to_signal == nullptr || posix_wait_handle_to_wait_on == nullptr) { return WaitResult::kFailed; } if (is_alertable) alertable_state_ = true; if (posix_wait_handle_to_signal->condition().Signal()) { result = posix_wait_handle_to_wait_on->condition().Wait(timeout); } if (is_alertable) alertable_state_ = false; return result; } std::pair WaitMultiple(WaitHandle* wait_handles[], size_t wait_handle_count, bool wait_all, bool is_alertable, std::chrono::milliseconds timeout) { std::vector conditions; conditions.reserve(wait_handle_count); for (size_t i = 0u; i < wait_handle_count; ++i) { auto handle = dynamic_cast(wait_handles[i]); if (handle == nullptr) { return std::make_pair(WaitResult::kFailed, 0); } conditions.push_back(&handle->condition()); } if (is_alertable) alertable_state_ = true; auto result = PosixConditionBase::WaitMultiple(std::move(conditions), wait_all, timeout); if (is_alertable) alertable_state_ = false; return result; } class PosixEvent : public PosixConditionHandle { public: PosixEvent(bool manual_reset, bool initial_state) : PosixConditionHandle(manual_reset, initial_state) {} ~PosixEvent() override = default; void Set() override { handle_.Signal(); } void Reset() override { handle_.Reset(); } void Pulse() override { using namespace std::chrono_literals; handle_.Signal(); MaybeYield(); Sleep(10us); handle_.Reset(); } }; std::unique_ptr Event::CreateManualResetEvent(bool initial_state) { return std::make_unique(true, initial_state); } std::unique_ptr Event::CreateAutoResetEvent(bool initial_state) { return std::make_unique(false, initial_state); } class PosixSemaphore : public PosixConditionHandle { public: PosixSemaphore(int initial_count, int maximum_count) : PosixConditionHandle(static_cast(initial_count), static_cast(maximum_count)) {} ~PosixSemaphore() override = default; bool Release(int release_count, int* out_previous_count) override { if (release_count < 1) { return false; } return handle_.Release(static_cast(release_count), out_previous_count); } }; std::unique_ptr Semaphore::Create(int initial_count, int maximum_count) { if (initial_count < 0 || initial_count > maximum_count || maximum_count <= 0) { return nullptr; } return std::make_unique(initial_count, maximum_count); } class PosixMutant : public PosixConditionHandle { public: explicit PosixMutant(bool initial_owner) : PosixConditionHandle(initial_owner) {} ~PosixMutant() override = default; bool Release() override { return handle_.Release(); } }; std::unique_ptr Mutant::Create(bool initial_owner) { return std::make_unique(initial_owner); } class PosixTimer : public PosixConditionHandle { using WClock_ = Timer::WClock_; using GClock_ = Timer::GClock_; public: explicit PosixTimer(bool manual_reset) : PosixConditionHandle(manual_reset) {} ~PosixTimer() override = default; bool SetOnceAfter(xe::chrono::hundrednanoseconds rel_time, std::function opt_callback = nullptr) override { return SetOnceAt(GClock_::now() + rel_time, std::move(opt_callback)); } bool SetOnceAt(WClock_::time_point due_time, std::function opt_callback = nullptr) override { return SetOnceAt(date::clock_cast(due_time), std::move(opt_callback)); }; bool SetOnceAt(GClock_::time_point due_time, std::function opt_callback = nullptr) override { handle_.SetOnce(due_time, std::move(opt_callback)); return true; } bool SetRepeatingAfter( xe::chrono::hundrednanoseconds rel_time, std::chrono::milliseconds period, std::function opt_callback = nullptr) override { return SetRepeatingAt(GClock_::now() + rel_time, period, std::move(opt_callback)); } bool SetRepeatingAt(WClock_::time_point due_time, std::chrono::milliseconds period, std::function opt_callback = nullptr) override { return SetRepeatingAt(date::clock_cast(due_time), period, std::move(opt_callback)); } bool SetRepeatingAt(GClock_::time_point due_time, std::chrono::milliseconds period, std::function opt_callback = nullptr) override { handle_.SetRepeating(due_time, period, std::move(opt_callback)); return true; } bool Cancel() override { handle_.Cancel(); return true; } }; std::unique_ptr Timer::CreateManualResetTimer() { return std::make_unique(true); } std::unique_ptr Timer::CreateSynchronizationTimer() { return std::make_unique(false); } class PosixThread : public PosixConditionHandle { public: PosixThread() = default; explicit PosixThread(pthread_t thread) : PosixConditionHandle(thread) {} ~PosixThread() override = default; bool Initialize(CreationParameters params, std::function start_routine) { auto start_data = new ThreadStartData({std::move(start_routine), false, this}); return handle_.Initialize(params, start_data); } void set_name(std::string name) override { handle_.WaitStarted(); Thread::set_name(name); if (name.length() > 15) { name = name.substr(0, 15); } handle_.set_name(name); } uint32_t system_id() const override { return handle_.system_id(); } uint64_t affinity_mask() override { return handle_.affinity_mask(); } void set_affinity_mask(uint64_t mask) override { handle_.set_affinity_mask(mask); } int priority() override { return handle_.priority(); } void set_priority(int new_priority) override { handle_.set_priority(new_priority); } void QueueUserCallback(std::function callback) override { handle_.QueueUserCallback(std::move(callback)); } bool Resume(uint32_t* out_previous_suspend_count) override { return handle_.Resume(out_previous_suspend_count); } bool Suspend(uint32_t* out_previous_suspend_count) override { return handle_.Suspend(out_previous_suspend_count); } void Terminate(int exit_code) override { handle_.Terminate(exit_code); } void WaitSuspended() { handle_.WaitSuspended(); } }; thread_local PosixThread* current_thread_ = nullptr; void* PosixCondition::ThreadStartRoutine(void* parameter) { #if !XE_PLATFORM_ANDROID if (pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, nullptr) != 0) { assert_always(); } #endif threading::set_name(""); auto start_data = static_cast(parameter); assert_not_null(start_data); assert_not_null(start_data->thread_obj); auto thread = dynamic_cast(start_data->thread_obj); auto start_routine = std::move(start_data->start_routine); auto create_suspended = start_data->create_suspended; delete start_data; current_thread_ = thread; { std::unique_lock lock(thread->handle_.state_mutex_); thread->handle_.state_ = create_suspended ? State::kSuspended : State::kRunning; thread->handle_.state_signal_.notify_all(); } if (create_suspended) { std::unique_lock lock(thread->handle_.state_mutex_); thread->handle_.suspend_count_ = 1; thread->handle_.state_signal_.wait( lock, [thread] { return thread->handle_.suspend_count_ == 0; }); } start_routine(); { std::unique_lock lock(thread->handle_.state_mutex_); thread->handle_.state_ = State::kFinished; } std::unique_lock lock(mutex_); thread->handle_.exit_code_ = 0; thread->handle_.signaled_ = true; cond_.notify_all(); current_thread_ = nullptr; return nullptr; } std::unique_ptr Thread::Create(CreationParameters params, std::function start_routine) { install_signal_handler(SignalType::kThreadSuspend); install_signal_handler(SignalType::kThreadUserCallback); #if XE_PLATFORM_ANDROID install_signal_handler(SignalType::kThreadTerminate); #endif auto thread = std::make_unique(); if (!thread->Initialize(params, std::move(start_routine))) return nullptr; assert_not_null(thread); return thread; } Thread* Thread::GetCurrentThread() { if (current_thread_) { return current_thread_; } // Should take this route only for threads not created by Thread::Create. // The only thread not created by Thread::Create should be the main thread. pthread_t handle = pthread_self(); current_thread_ = new PosixThread(handle); // TODO(bwrsandman): Disabling deleting thread_local current thread to prevent // assert in destructor. Since this is thread local, the // "memory leaking" is controlled. // atexit([] { delete current_thread_; }); return current_thread_; } void Thread::Exit(int exit_code) { if (current_thread_) { current_thread_->Terminate(exit_code); } else { // Should only happen with the main thread pthread_exit(reinterpret_cast(exit_code)); } // Function must not return assert_always(); } void set_name(const std::string_view name) { pthread_setname_np(pthread_self(), std::string(name).c_str()); #if XE_PLATFORM_ANDROID if (!android_pthread_getname_np_ && current_thread_) { current_thread_->condition().SetAndroidPreApi26Name(name); } #endif } static void signal_handler(int signal, siginfo_t* info, void* /*context*/) { switch (GetSystemSignalType(signal)) { case SignalType::kThreadSuspend: { assert_not_null(current_thread_); current_thread_->WaitSuspended(); } break; case SignalType::kThreadUserCallback: { assert_not_null(info->si_value.sival_ptr); auto p_thread = static_cast*>(info->si_value.sival_ptr); if (alertable_state_) { p_thread->CallUserCallback(); } } break; #if XE_PLATFORM_ANDROID case SignalType::kThreadTerminate: { pthread_exit(reinterpret_cast(-1)); } break; #endif default: assert_always(); } } } // namespace threading } // namespace xe