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