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 929 : 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 929 : tid = std::this_thread::get_id(); 30 : try { 31 929 : if (setup()) { 32 51397 : while (state_ == ThreadState::RUNNING) 33 50469 : process(); 34 928 : 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 929 : stop(); 44 929 : } 45 : 46 1328 : ThreadLoop::ThreadLoop(const std::function<bool()>& setup, 47 : const std::function<void()>& process, 48 1328 : const std::function<void()>& cleanup) 49 1328 : : setup_(setup) 50 1328 : , process_(process) 51 1328 : , cleanup_(cleanup) 52 2656 : , thread_() 53 1328 : {} 54 : 55 1328 : ThreadLoop::~ThreadLoop() 56 : { 57 1328 : if (isRunning()) { 58 0 : JAMI_ERR("join() should be explicitly called in owner's destructor"); 59 0 : join(); 60 : } 61 1328 : } 62 : 63 : void 64 934 : ThreadLoop::start() 65 : { 66 934 : const auto s = state_.load(); 67 : 68 934 : 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 929 : if (s == ThreadState::STOPPING and thread_.joinable()) { 75 14 : JAMI_DBG("stop pending"); 76 14 : thread_.join(); 77 : } 78 : 79 929 : state_ = ThreadState::RUNNING; 80 929 : thread_ = std::thread(&ThreadLoop::mainloop, this, std::ref(threadId_), setup_, process_, cleanup_); 81 929 : threadId_ = thread_.get_id(); 82 : } 83 : 84 : void 85 3000 : ThreadLoop::stop() 86 : { 87 3000 : auto expected = ThreadState::RUNNING; 88 3000 : state_.compare_exchange_strong(expected, ThreadState::STOPPING); 89 2999 : } 90 : 91 : void 92 1599 : ThreadLoop::join() 93 : { 94 1599 : stop(); 95 1598 : if (thread_.joinable()) 96 913 : thread_.join(); 97 1599 : } 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 16647 : ThreadLoop::isRunning() const noexcept 115 : { 116 : #ifdef _WIN32 117 : return state_ == ThreadState::RUNNING; 118 : #else 119 16647 : return thread_.joinable() and state_ == ThreadState::RUNNING; 120 : #endif 121 : } 122 : 123 : void 124 1115 : InterruptedThreadLoop::stop() 125 : { 126 1115 : ThreadLoop::stop(); 127 1115 : cv_.notify_one(); 128 1115 : } 129 : } // namespace jami