From b143b91fbbc371a7433a4fdabfaafaa01c18c2bb Mon Sep 17 00:00:00 2001 From: Doug Johnson Date: Mon, 10 Jul 2017 02:00:52 -0600 Subject: [PATCH] base: threading_linux: Stub out several synchronization constructs --- src/xenia/base/threading_linux.cc | 1 - src/xenia/base/threading_posix.cc | 291 +++++++++++++++++++++++++++++- 2 files changed, 283 insertions(+), 9 deletions(-) diff --git a/src/xenia/base/threading_linux.cc b/src/xenia/base/threading_linux.cc index 6f4a97584..21d5418c7 100644 --- a/src/xenia/base/threading_linux.cc +++ b/src/xenia/base/threading_linux.cc @@ -15,7 +15,6 @@ namespace xe { namespace threading { -void MaybeYield() { pthread_yield(); } } // namespace threading } // namespace xe diff --git a/src/xenia/base/threading_posix.cc b/src/xenia/base/threading_posix.cc index 3089f11b8..1a0bea457 100644 --- a/src/xenia/base/threading_posix.cc +++ b/src/xenia/base/threading_posix.cc @@ -15,12 +15,18 @@ #include #include #include +#include #include #include namespace xe { namespace threading { +//TODO(dougvj) +void EnableAffinityConfiguration() { + +} + // uint64_t ticks() { return mach_absolute_time(); } uint32_t current_thread_system_id() { @@ -32,7 +38,16 @@ void set_name(const std::string& name) { } void set_name(std::thread::native_handle_type handle, const std::string& name) { - pthread_setname_np(pthread_self(), name.c_str()); + pthread_setname_np(handle, name.c_str()); +} + +void MaybeYield() { + pthread_yield(); + __sync_synchronize(); +} + +void SyncMemory() { + __sync_synchronize(); } void Sleep(std::chrono::microseconds duration) { @@ -42,11 +57,133 @@ void Sleep(std::chrono::microseconds duration) { // TODO(benvanik): spin while rmtp >0? } -template -class PosixHandle : public T { +//TODO(dougvj) Not sure how to implement the equivalent of this on POSIX. +SleepResult AlertableSleep(std::chrono::microseconds duration) { + sleep(duration.count() / 1000); + return SleepResult::kSuccess; +} + +//TODO(dougvj) We can probably wrap this with pthread_key_t but the type of +//TlsHandle probably needs to be refactored +TlsHandle AllocateTlsHandle() { + assert_always(); +} + +bool FreeTlsHandle(TlsHandle handle) { return true; } + +uintptr_t GetTlsValue(TlsHandle handle) { + assert_always(); +} + +bool SetTlsValue(TlsHandle handle, uintptr_t value) { + assert_always(); +} + +//TODO(dougvj) +class PosixHighResolutionTimer : public HighResolutionTimer { public: - explicit PosixHandle(pthread_t handle) : handle_(handle) {} - ~PosixHandle() override {} + PosixHighResolutionTimer(std::function callback) + : callback_(callback) {} + ~PosixHighResolutionTimer() override { + } + + bool Initialize(std::chrono::milliseconds period) { + assert_always(); + return false; + } + + private: + std::function callback_; +}; + +std::unique_ptr HighResolutionTimer::CreateRepeating( + std::chrono::milliseconds period, std::function callback) { + auto timer = std::make_unique(std::move(callback)); + if (!timer->Initialize(period)) { + return nullptr; + } + return std::unique_ptr(timer.release()); +} + +// TODO(dougvj) 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 could function as our handle, but probably needs +// some more functionality +class PosixCondition { + public: + PosixCondition(): signal_(false) { + pthread_mutex_init(&mutex_, NULL); + pthread_cond_init(&cond_, NULL); + } + + ~PosixCondition() { + pthread_mutex_destroy(&mutex_); + pthread_cond_destroy(&cond_); + } + + void Signal() { + pthread_mutex_lock(&mutex_); + signal_ = true; + pthread_cond_broadcast(&cond_); + pthread_mutex_unlock(&mutex_); + } + + void Reset() { + pthread_mutex_lock(&mutex_); + signal_ = false; + pthread_mutex_unlock(&mutex_); + } + + bool Wait(unsigned int timeout_ms) { + //Assume 0 means no timeout, not instant timeout + if (timeout_ms == 0) { + Wait(); + } + struct timespec time_to_wait; + struct timeval now; + gettimeofday(&now, NULL); + + //Add the number of seconds we want to wait to the current time + time_to_wait.tv_sec = now.tv_sec + (timeout_ms/1000); + //Add the number of nanoseconds we want to wait to the current nanosecond + //stride + long nsec = (now.tv_usec+(timeout_ms % 1000)) * 1000; + //If we overflowed the nanosecond count then we add a second + time_to_wait.tv_sec += nsec/1000000000UL; + //We only add nanoseconds within the 1 second stride + time_to_wait.tv_nsec = nsec % 1000000000UL; + pthread_mutex_lock(&mutex_); + while(!signal_) { + int status = pthread_cond_timedwait(&cond_, &mutex_, &time_to_wait); + if (status == ETIMEDOUT) + return false; //We timed out + } + pthread_mutex_unlock(&mutex_); + return true; //We didn't time out + } + + bool Wait() { + pthread_mutex_lock(&mutex_); + while(!signal_) { + pthread_cond_wait(&cond_, &mutex_); + } + pthread_mutex_unlock(&mutex_); + return true; //Did not time out; + } + + private: + bool signal_; + pthread_cond_t cond_; + pthread_mutex_t mutex_; + +}; + +//Native posix thread handle +template +class PosixThreadHandle : public T { + public: + explicit PosixThreadHandle(pthread_t handle) : handle_(handle) {} + ~PosixThreadHandle() override {} protected: void* native_handle() const override { @@ -56,13 +193,136 @@ class PosixHandle : public T { pthread_t handle_; }; -class PosixThread : public PosixHandle { +//This is 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: - explicit PosixThread(pthread_t handle) : PosixHandle(handle) {} + ~PosixConditionHandle() override {} + + protected: + void* native_handle() const override { + return reinterpret_cast(const_cast(&handle_)); + } + + PosixCondition handle_; +}; + + +// TODO(dougvj) +WaitResult Wait(WaitHandle* wait_handle, bool is_alertable, + std::chrono::milliseconds timeout) { + assert_always(); + return WaitResult::kFailed; +} + +// TODO(dougvj) +WaitResult SignalAndWait(WaitHandle* wait_handle_to_signal, + WaitHandle* wait_handle_to_wait_on, bool is_alertable, + std::chrono::milliseconds timeout) { + assert_always(); + return WaitResult::kFailed; +} + +// TODO(dougvj) +std::pair WaitMultiple(WaitHandle* wait_handles[], + size_t wait_handle_count, + bool wait_all, bool is_alertable, + std::chrono::milliseconds timeout) { + assert_always(); + return std::pair(WaitResult::kFailed, 0); +} + + +//TODO(dougvj) +class PosixEvent: public PosixConditionHandle { + public: + PosixEvent(bool initial_state, int auto_reset) { assert_always(); } + ~PosixEvent() override = default; + void Set() override { assert_always(); } + void Reset() override { assert_always(); } + void Pulse() override { assert_always(); } + private: + PosixCondition condition_; +}; + +std::unique_ptr Event::CreateManualResetEvent(bool initial_state) { + return std::make_unique(PosixEvent(initial_state, false)); +} + +std::unique_ptr Event::CreateAutoResetEvent(bool initial_state) { + return std::make_unique(PosixEvent(initial_state, true)); +} + +//TODO(dougvj) +class PosixSemaphore : public PosixConditionHandle { + public: + PosixSemaphore(int initial_count, int maximum_count) { assert_always(); } + ~PosixSemaphore() override = default; + bool Release(int release_count, int* out_previous_count) override { + assert_always(); + return false; + } +}; + +std::unique_ptr Semaphore::Create(int initial_count, + int maximum_count) { + return std::make_unique(initial_count, maximum_count); +} + +//TODO(dougvj) +class PosixMutant : public PosixConditionHandle { + public: + PosixMutant(bool initial_owner) { + assert_always(); + } + ~PosixMutant() = default; + bool Release() override { assert_always(); return false; } + private: +}; + +std::unique_ptr Mutant::Create(bool initial_owner) { + return std::make_unique(initial_owner); +} + +//TODO(dougvj) +class PosixTimer : public PosixConditionHandle { + public: + PosixTimer(bool manual_reset) { assert_always(); } + ~PosixTimer() = default; + bool SetOnce(std::chrono::nanoseconds due_time, + std::function opt_callback) override { + assert_always(); + return false; + } + bool SetRepeating(std::chrono::nanoseconds due_time, + std::chrono::milliseconds period, + std::function opt_callback) override { + assert_always(); + return false; + } + bool Cancel() override { + assert_always(); + return false; + } + +}; + +std::unique_ptr Timer::CreateManualResetTimer() { + return std::make_unique(true); +} + +std::unique_ptr Timer::CreateSynchronizationTimer() { + return std::make_unique(false); +} + +class PosixThread : public PosixThreadHandle { + public: + explicit PosixThread(pthread_t handle) : PosixThreadHandle(handle) {} ~PosixThread() = default; void set_name(std::string name) override { - // TODO(DrChat) + pthread_setname_np(handle_, name.c_str()); } uint32_t system_id() const override { return 0; } @@ -141,5 +401,20 @@ std::unique_ptr Thread::Create(CreationParameters params, return std::unique_ptr(new PosixThread(handle)); } +Thread* Thread::GetCurrentThread() { + if (current_thread_) { + return current_thread_.get(); + } + + pthread_t handle = pthread_self(); + + current_thread_ = std::make_unique(handle); + return current_thread_.get(); +} + +void Thread::Exit(int exit_code) { + pthread_exit(reinterpret_cast(exit_code)); +} + } // namespace threading } // namespace xe