/** * * \section COPYRIGHT * * Copyright 2013-2020 Software Radio Systems Limited * * By using this file, you agree to the terms and conditions set * forth in the LICENSE file which can be found at the top level of * the distribution. * */ #ifndef SRSLOG_DETAIL_SUPPORT_THREAD_UTILS_H #define SRSLOG_DETAIL_SUPPORT_THREAD_UTILS_H #include namespace srslog { namespace detail { //:TODO: these are temp helpers that will be replaced by std utils. /// Abstraction of a pthread mutex. class mutex { public: mutex(const mutex&) = delete; mutex& operator=(const mutex&) = delete; mutex() { ::pthread_mutex_init(&m, nullptr); } ~mutex() { ::pthread_mutex_destroy(&m); } /// Mutex lock. void lock() { ::pthread_mutex_lock(&m); } /// Mutex unlock. void unlock() { ::pthread_mutex_unlock(&m); } /// Mutex try lock. Returns true if the lock was obtained, false otherwise. bool try_lock() { return (::pthread_mutex_trylock(&m) == 0); } /// Accessor to the raw mutex structure. pthread_mutex_t* raw() { return &m; } const pthread_mutex_t* raw() const { return &m; } private: pthread_mutex_t m; }; /// RAII style object for automatically locking and unlocking a mutex. class scoped_lock { mutex& m; public: explicit scoped_lock(mutex& m) : m(m) { m.lock(); } scoped_lock(const scoped_lock&) = delete; scoped_lock& operator=(const scoped_lock&) = delete; ~scoped_lock() { m.unlock(); } }; /// This class allows accessing and modifying data atomically. template class shared_variable { T value; mutable mutex m; public: shared_variable(const T& value) : value(value) {} shared_variable(const shared_variable&) = delete; shared_variable& operator=(const shared_variable&) = delete; /// Set this shared variable to a new value guarded by the associated mutex. shared_variable& operator=(const T& other) { scoped_lock lock(m); value = other; return *this; } /// Get the value of this shared variable guarded by the associated mutex. operator T() const { scoped_lock lock(m); return value; } }; /// Abstraction of a pthread condition variable. class condition_variable { public: condition_variable() { ::pthread_cond_init(&cond_var, nullptr); } condition_variable(const condition_variable&) = delete; condition_variable& operator=(const condition_variable&) = delete; ~condition_variable() { ::pthread_cond_destroy(&cond_var); } /// Internal mutex control. void lock() { m.lock(); } void unlock() { m.unlock(); } /// Blocks the calling thread on this condition variable. void wait() { ::pthread_cond_wait(&cond_var, m.raw()); } /// Unblocks at least one waiting thread that is blocked on this condition /// variable. void signal() { ::pthread_cond_signal(&cond_var); } /// Unblocks all waiting threads being blocked on this condition variable. void broadcast() { ::pthread_cond_broadcast(&cond_var); } /// Blocks the calling thread on this condition variable up to the specified /// timeout. Returns true on timeout expiration, otherwise false. bool wait(timespec ts) { return (::pthread_cond_timedwait(&cond_var, m.raw(), &ts) == ETIMEDOUT); } /// Builds an absolute time timespec structure adding the specified time out /// in ms. static timespec build_timeout(unsigned timeout_ms) { timespec ts; clock_gettime(CLOCK_REALTIME, &ts); ts.tv_sec += timeout_ms / 1000; ts.tv_nsec += (timeout_ms % 1000) * 1000000; if (ts.tv_nsec >= 1000000000) { ++ts.tv_sec; ts.tv_nsec -= 1000000000; } return ts; } private: mutable mutex m; pthread_cond_t cond_var; }; /// RAII style object for automatically locking and unlocking a condition /// variable. class cond_var_scoped_lock { condition_variable& cond_var; public: explicit cond_var_scoped_lock(condition_variable& cond_var) : cond_var(cond_var) { cond_var.lock(); } cond_var_scoped_lock(const cond_var_scoped_lock&) = delete; cond_var_scoped_lock& operator=(const cond_var_scoped_lock&) = delete; ~cond_var_scoped_lock() { cond_var.unlock(); } }; } // namespace detail } // namespace srslog #endif // SRSLOG_DETAIL_SUPPORT_THREAD_UTILS_H