/** * * \section COPYRIGHT * * Copyright 2013-2021 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 SRSRAN_THREADS_H #define SRSRAN_THREADS_H #include #include #include #include #include // Default priority for all threads below UHD threads #define DEFAULT_PRIORITY 60 #ifdef __cplusplus extern "C" { #endif // __cplusplus bool threads_new_rt(pthread_t* thread, void* (*start_routine)(void*), void* arg); bool threads_new_rt_prio(pthread_t* thread, void* (*start_routine)(void*), void* arg, int prio_offset); bool threads_new_rt_cpu(pthread_t* thread, void* (*start_routine)(void*), void* arg, int cpu, int prio_offset); bool threads_new_rt_mask(pthread_t* thread, void* (*start_routine)(void*), void* arg, int mask, int prio_offset); void threads_print_self(); #ifdef __cplusplus } #include #include namespace srsran { class thread { public: thread(const std::string& name_) : _thread(0), name(name_) {} thread(const thread&) = delete; thread(thread&& other) noexcept { _thread = other._thread; name = std::move(other.name); other._thread = 0; other.name = ""; } virtual ~thread() = default; thread& operator=(const thread&) = delete; thread& operator=(thread&&) noexcept = delete; bool start(int prio = -1) { return threads_new_rt_prio(&_thread, thread_function_entry, this, prio); } bool start_cpu(int prio, int cpu) { return threads_new_rt_cpu(&_thread, thread_function_entry, this, cpu, prio); } bool start_cpu_mask(int prio, int mask) { return threads_new_rt_mask(&_thread, thread_function_entry, this, mask, prio); } void print_priority() { threads_print_self(); } void set_name(const std::string& name_) { name = name_; pthread_setname_np(pthread_self(), name.c_str()); } void wait_thread_finish() { pthread_join(_thread, NULL); } void thread_cancel() { pthread_cancel(_thread); } static std::string get_name() { const uint32_t MAX_LEN = 16; char name[MAX_LEN] = {}; const pthread_t tid = pthread_self(); if (pthread_getname_np(tid, name, MAX_LEN)) { perror("Could not get pthread name"); } return std::string(name); } protected: virtual void run_thread() = 0; private: static void* thread_function_entry(void* _this) { pthread_setname_np(pthread_self(), ((thread*)_this)->name.c_str()); ((thread*)_this)->run_thread(); return NULL; } pthread_t _thread; std::string name; }; class periodic_thread : public thread { public: periodic_thread(const std::string name_) : thread(name_) {} void start_periodic(int period_us_, int priority = -1) { run_enable = true; period_us = period_us_; start(priority); } void stop_thread() { run_enable = false; wait_thread_finish(); } protected: virtual void run_period() = 0; private: int wakeups_missed = 0; int timer_fd = 0; int period_us = 0; std::atomic run_enable = {false}; void run_thread() { if (make_periodic()) { return; } while (run_enable) { run_period(); if (run_enable) { wait_period(); } } } int make_periodic() { int ret = -1; unsigned int ns; unsigned int sec; struct itimerspec itval; /* Create the timer */ ret = timerfd_create(CLOCK_MONOTONIC, 0); wakeups_missed = 0; timer_fd = ret; if (ret > 0) { /* Make the timer periodic */ sec = period_us / 1e6; ns = (period_us - (sec * 1000000)) * 1000; itval.it_interval.tv_sec = sec; itval.it_interval.tv_nsec = ns; itval.it_value.tv_sec = sec; itval.it_value.tv_nsec = ns; ret = timerfd_settime(timer_fd, 0, &itval, NULL); if (ret < 0) { perror("timerfd_settime"); } } else { perror("timerfd_create"); } return ret; } void wait_period() { unsigned long long missed; int ret; /* Wait for the next timer event. If we have missed any the number is written to "missed" */ ret = read(timer_fd, &missed, sizeof(missed)); if (ret == -1) { perror("read timer"); return; } /* "missed" should always be >= 1, but just to be sure, check it is not 0 anyway */ if (missed > 0) { wakeups_missed += (missed - 1); } } }; } // namespace srsran #endif // __cplusplus #endif // SRSRAN_THREADS_H