Line data Source code
1 : /* 2 : * Copyright (C) 2004-2024 Savoir-faire Linux Inc. 3 : * 4 : * Author: Guillaume Roguez <Guillaume.Roguez@savoirfairelinux.com> 5 : * 6 : * This program is free software; you can redistribute it and/or modify 7 : * it under the terms of the GNU General Public License as published by 8 : * the Free Software Foundation; either version 3 of the License, or 9 : * (at your option) any later version. 10 : * 11 : * This program is distributed in the hope that it will be useful, 12 : * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 : * GNU General Public License for more details. 15 : * 16 : * You should have received a copy of the GNU General Public License 17 : * along with this program; if not, write to the Free Software 18 : * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 : */ 20 : 21 : #include "threadloop.h" 22 : #include "logger.h" 23 : 24 : #include <ciso646> // fix windows compiler bug 25 : 26 : namespace jami { 27 : 28 : void 29 944 : ThreadLoop::mainloop(std::thread::id& tid, 30 : const std::function<bool()> setup, 31 : const std::function<void()> process, 32 : const std::function<void()> cleanup) 33 : { 34 944 : tid = std::this_thread::get_id(); 35 : try { 36 944 : if (setup()) { 37 203877918 : while (state_ == ThreadState::RUNNING) 38 203876545 : process(); 39 935 : cleanup(); 40 : } else { 41 0 : JAMI_ERR("setup failed"); 42 : } 43 2 : } catch (const ThreadLoopException& e) { 44 0 : JAMI_ERR("[threadloop:%p] ThreadLoopException: %s", this, e.what()); 45 2 : } catch (const std::exception& e) { 46 2 : JAMI_ERR("[threadloop:%p] Unwaited exception: %s", this, e.what()); 47 2 : } 48 944 : stop(); 49 944 : } 50 : 51 1409 : ThreadLoop::ThreadLoop(const std::function<bool()>& setup, 52 : const std::function<void()>& process, 53 1409 : const std::function<void()>& cleanup) 54 1409 : : setup_(setup) 55 1409 : , process_(process) 56 1409 : , cleanup_(cleanup) 57 2818 : , thread_() 58 1409 : {} 59 : 60 1409 : ThreadLoop::~ThreadLoop() 61 : { 62 1409 : if (isRunning()) { 63 0 : JAMI_ERR("join() should be explicitly called in owner's destructor"); 64 0 : join(); 65 : } 66 1409 : } 67 : 68 : void 69 954 : ThreadLoop::start() 70 : { 71 954 : const auto s = state_.load(); 72 : 73 954 : if (s == ThreadState::RUNNING) { 74 10 : JAMI_ERR("already started"); 75 10 : return; 76 : } 77 : 78 : // stop pending but not processed by thread yet? 79 944 : if (s == ThreadState::STOPPING and thread_.joinable()) { 80 0 : JAMI_DBG("stop pending"); 81 0 : thread_.join(); 82 : } 83 : 84 944 : state_ = ThreadState::RUNNING; 85 944 : thread_ = std::thread(&ThreadLoop::mainloop, this, std::ref(threadId_), setup_, process_, cleanup_); 86 944 : threadId_ = thread_.get_id(); 87 : } 88 : 89 : void 90 3123 : ThreadLoop::stop() 91 : { 92 3123 : auto expected = ThreadState::RUNNING; 93 3123 : state_.compare_exchange_strong(expected, ThreadState::STOPPING); 94 3123 : } 95 : 96 : void 97 1705 : ThreadLoop::join() 98 : { 99 1705 : stop(); 100 1705 : if (thread_.joinable()) 101 944 : thread_.join(); 102 1705 : } 103 : 104 : void 105 0 : ThreadLoop::waitForCompletion() 106 : { 107 0 : if (thread_.joinable()) 108 0 : thread_.join(); 109 0 : } 110 : 111 : void 112 0 : ThreadLoop::exit() 113 : { 114 0 : stop(); 115 0 : throw ThreadLoopException(); 116 : } 117 : 118 : bool 119 14455 : ThreadLoop::isRunning() const noexcept 120 : { 121 : #ifdef _WIN32 122 : return state_ == ThreadState::RUNNING; 123 : #else 124 14455 : return thread_.joinable() and state_ == ThreadState::RUNNING; 125 : #endif 126 : } 127 : 128 : void 129 1206 : InterruptedThreadLoop::stop() 130 : { 131 1206 : ThreadLoop::stop(); 132 1206 : cv_.notify_one(); 133 1206 : } 134 : } // namespace jami