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