Line data Source code
1 : /* 2 : * Copyright (C) 2004-2026 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 : 18 : #include "threadloop.h" 19 : #include "logger.h" 20 : 21 : namespace jami { 22 : 23 : void 24 896 : ThreadLoop::mainloop(std::thread::id& tid, 25 : const std::function<bool()> setup, 26 : const std::function<void()> process, 27 : const std::function<void()> cleanup) 28 : { 29 896 : tid = std::this_thread::get_id(); 30 : try { 31 895 : if (setup()) { 32 52454 : while (state_ == ThreadState::RUNNING) 33 51558 : process(); 34 894 : cleanup(); 35 : } else { 36 0 : JAMI_ERR("setup failed"); 37 : } 38 1 : } catch (const ThreadLoopException& e) { 39 0 : JAMI_ERR("[threadloop:%p] ThreadLoopException: %s", this, e.what()); 40 1 : } catch (const std::exception& e) { 41 1 : JAMI_ERR("[threadloop:%p] Unwaited exception: %s", this, e.what()); 42 1 : } 43 896 : stop(); 44 896 : } 45 : 46 1295 : ThreadLoop::ThreadLoop(const std::function<bool()>& setup, 47 : const std::function<void()>& process, 48 1295 : const std::function<void()>& cleanup) 49 1295 : : setup_(setup) 50 1295 : , process_(process) 51 1295 : , cleanup_(cleanup) 52 2590 : , thread_() 53 1294 : {} 54 : 55 1295 : ThreadLoop::~ThreadLoop() 56 : { 57 1295 : if (isRunning()) { 58 0 : JAMI_ERR("join() should be explicitly called in owner's destructor"); 59 0 : join(); 60 : } 61 1295 : } 62 : 63 : void 64 901 : ThreadLoop::start() 65 : { 66 901 : const auto s = state_.load(); 67 : 68 901 : if (s == ThreadState::RUNNING) { 69 5 : JAMI_ERR("already started"); 70 5 : return; 71 : } 72 : 73 : // stop pending but not processed by thread yet? 74 896 : if (s == ThreadState::STOPPING and thread_.joinable()) { 75 16 : JAMI_DBG("stop pending"); 76 16 : thread_.join(); 77 : } 78 : 79 896 : state_ = ThreadState::RUNNING; 80 896 : thread_ = std::thread(&ThreadLoop::mainloop, this, std::ref(threadId_), setup_, process_, cleanup_); 81 896 : threadId_ = thread_.get_id(); 82 : } 83 : 84 : void 85 2898 : ThreadLoop::stop() 86 : { 87 2898 : auto expected = ThreadState::RUNNING; 88 2898 : state_.compare_exchange_strong(expected, ThreadState::STOPPING); 89 2899 : } 90 : 91 : void 92 1550 : ThreadLoop::join() 93 : { 94 1550 : stop(); 95 1550 : if (thread_.joinable()) 96 880 : thread_.join(); 97 1550 : } 98 : 99 : void 100 0 : ThreadLoop::waitForCompletion() 101 : { 102 0 : if (thread_.joinable()) 103 0 : thread_.join(); 104 0 : } 105 : 106 : void 107 0 : ThreadLoop::exit() 108 : { 109 0 : stop(); 110 0 : throw ThreadLoopException(); 111 : } 112 : 113 : bool 114 17303 : ThreadLoop::isRunning() const noexcept 115 : { 116 : #ifdef _WIN32 117 : return state_ == ThreadState::RUNNING; 118 : #else 119 17303 : return thread_.joinable() and state_ == ThreadState::RUNNING; 120 : #endif 121 : } 122 : 123 : void 124 1075 : InterruptedThreadLoop::stop() 125 : { 126 1075 : ThreadLoop::stop(); 127 1075 : cv_.notify_one(); 128 1075 : } 129 : } // namespace jami