Line data Source code
1 : /* 2 : * Copyright (C) 2004-2024 Savoir-faire Linux Inc. 3 : * 4 : * This program is free software: you can redistribute it and/or modify 5 : * it under the terms of the GNU General Public License as published by 6 : * the Free Software Foundation, either version 3 of the License, or 7 : * (at your option) any later version. 8 : * 9 : * This program is distributed in the hope that it will be useful, 10 : * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 : * GNU General Public License for more details. 13 : * 14 : * You should have received a copy of the GNU General Public License 15 : * along with this program. If not, see <https://www.gnu.org/licenses/>. 16 : */ 17 : #pragma once 18 : 19 : #include <atomic> 20 : #include <thread> 21 : #include <functional> 22 : #include <stdexcept> 23 : #include <condition_variable> 24 : #include <mutex> 25 : 26 : namespace jami { 27 : 28 : struct ThreadLoopException : public std::runtime_error 29 : { 30 0 : ThreadLoopException() 31 0 : : std::runtime_error("ThreadLoopException") 32 0 : {} 33 : }; 34 : 35 : class ThreadLoop 36 : { 37 : public: 38 : enum class ThreadState { READY, RUNNING, STOPPING }; 39 : 40 : ThreadLoop(const std::function<bool()>& setup, 41 : const std::function<void()>& process, 42 : const std::function<void()>& cleanup); 43 : virtual ~ThreadLoop(); 44 : 45 : void start(); 46 : void exit(); 47 : virtual void stop(); 48 : void join(); 49 : void waitForCompletion(); // thread will stop itself 50 : 51 : bool isRunning() const noexcept; 52 330 : bool isStopping() const noexcept { return state_ == ThreadState::STOPPING; } 53 : std::thread::id get_id() const noexcept { return threadId_; } 54 : 55 : private: 56 : ThreadLoop(const ThreadLoop&) = delete; 57 : ThreadLoop(ThreadLoop&&) noexcept = delete; 58 : ThreadLoop& operator=(const ThreadLoop&) = delete; 59 : ThreadLoop& operator=(ThreadLoop&&) noexcept = delete; 60 : 61 : // These must be provided by users of ThreadLoop 62 : std::function<bool()> setup_; 63 : std::function<void()> process_; 64 : std::function<void()> cleanup_; 65 : 66 : void mainloop(std::thread::id& tid, 67 : const std::function<bool()> setup, 68 : const std::function<void()> process, 69 : const std::function<void()> cleanup); 70 : 71 : std::atomic<ThreadState> state_ {ThreadState::READY}; 72 : std::thread::id threadId_; 73 : std::thread thread_; 74 : }; 75 : 76 : class InterruptedThreadLoop : public ThreadLoop 77 : { 78 : public: 79 712 : InterruptedThreadLoop(const std::function<bool()>& setup, 80 : const std::function<void()>& process, 81 : const std::function<void()>& cleanup) 82 712 : : ThreadLoop::ThreadLoop(setup, process, cleanup) 83 712 : {} 84 : 85 : void stop() override; 86 : 87 : void interrupt() noexcept { cv_.notify_one(); } 88 : 89 : template<typename Rep, typename Period> 90 : void wait_for(const std::chrono::duration<Rep, Period>& rel_time) 91 : { 92 : if (std::this_thread::get_id() != get_id()) 93 : throw std::runtime_error("Unable to call wait_for outside thread context"); 94 : 95 : std::unique_lock lk(mutex_); 96 : cv_.wait_for(lk, rel_time, [this]() { return isStopping(); }); 97 : } 98 : 99 : template<typename Rep, typename Period, typename Pred> 100 : bool wait_for(const std::chrono::duration<Rep, Period>& rel_time, Pred&& pred) 101 : { 102 : if (std::this_thread::get_id() != get_id()) 103 : throw std::runtime_error("Unable to call wait_for outside thread context"); 104 : 105 : std::unique_lock lk(mutex_); 106 : return cv_.wait_for(lk, rel_time, [this, pred] { return isStopping() || pred(); }); 107 : } 108 : 109 : template<typename Pred> 110 : void wait(Pred&& pred) 111 : { 112 : if (std::this_thread::get_id() != get_id()) 113 : throw std::runtime_error("Unable to call wait outside thread context"); 114 : 115 : std::unique_lock lk(mutex_); 116 : cv_.wait(lk, [this, p = std::forward<Pred>(pred)] { return isStopping() || p(); }); 117 : } 118 : 119 : private: 120 : std::mutex mutex_; 121 : std::condition_variable cv_; 122 : }; 123 : 124 : } // namespace jami