Branch data Line data Source code
1 : : /* 2 : : ** Copyright (C) 2020 Sylvain Fargier 3 : : ** 4 : : ** This software is provided 'as-is', without any express or implied 5 : : ** warranty. In no event will the authors be held liable for any damages 6 : : ** arising from the use of this software. 7 : : ** 8 : : ** Permission is granted to anyone to use this software for any purpose, 9 : : ** including commercial applications, and to alter it and redistribute it 10 : : ** freely, subject to the following restrictions: 11 : : ** 12 : : ** 1. The origin of this software must not be misrepresented; you must not 13 : : ** claim that you wrote the original software. If you use this software 14 : : ** in a product, an acknowledgment in the product documentation would be 15 : : ** appreciated but is not required. 16 : : ** 2. Altered source versions must be plainly marked as such, and must not be 17 : : ** misrepresented as being the original software. 18 : : ** 3. This notice may not be removed or altered from any source distribution. 19 : : ** 20 : : ** Created on: 2021-11-20T16:42:00+01:00 21 : : ** Author: Sylvain Fargier <fargie_s> <fargier.sylvain@gmail.com> 22 : : ** 23 : : */ 24 : : 25 : : #include "Signal.hpp" 26 : : 27 : : #include <algorithm> 28 : : 29 : : namespace ccut { 30 : : 31 : 2017 : BaseListener::BaseListener() : active(true) {} 32 : : 33 : 2062 : void SignalData::SignalData::gc() 34 : : { 35 : 1036419 : list.remove_if([](const std::shared_ptr<BaseListener> &c) { return !(*c); }); 36 : 2062 : } 37 : : 38 : 28 : void SignalData::disconnect(bool wait) 39 : : { 40 : 28 : std::unique_lock<std::mutex> l(lock); 41 : : 42 : 35 : for (const std::shared_ptr<BaseListener> &listener : list) 43 : 7 : listener->active.store(false); 44 : 28 : list.clear(); 45 : : 46 : 28 : if (wait) 47 : : { 48 : : /* this thread won't run anything else, let's simply remove it from tids 49 : : */ 50 : 28 : if (tids.erase(std::this_thread::get_id()) != 0) 51 : 3 : cond.notify_all(); 52 : 57 : cond.wait(l, [this]() { return tids.size() == 0; }); 53 : : } 54 : 28 : } 55 : : 56 : 25 : SignalData::~SignalData() 57 : : { 58 : 25 : disconnect(true); 59 : 25 : } 60 : : 61 : 1002 : Connection::Connection() {} 62 : : 63 : 2017 : Connection::Connection(const std::shared_ptr<SignalData> &sig, 64 : 2017 : const std::shared_ptr<BaseListener> &listener) : 65 : 2017 : m_signal(sig), 66 : 2017 : m_listener(listener) 67 : 2017 : {} 68 : : 69 : 5042 : Connection::~Connection() 70 : : { 71 : 5042 : disconnect(); 72 : 5042 : } 73 : : 74 : 0 : void Connection::swap(Connection &other) 75 : : { 76 : 0 : m_signal.swap(other.m_signal); 77 : 0 : m_listener.swap(other.m_listener); 78 : 0 : } 79 : : 80 : 1577 : bool Connection::isConnected() const 81 : : { 82 : 1577 : std::shared_ptr<SignalData> signal(m_signal.lock()); 83 : 1577 : std::shared_ptr<BaseListener> listener(m_listener.lock()); 84 : : 85 : 1577 : if (signal && listener) 86 : : { 87 : 1466 : const bool ret = *listener; 88 : : { 89 : 1466 : std::unique_lock<std::mutex> lock(signal->lock); 90 : 1466 : listener.reset(); 91 : 1466 : } 92 : 1466 : signal->cond.notify_all(); 93 : 1466 : return ret; 94 : : } 95 : : else 96 : 111 : return false; 97 : 1577 : } 98 : : 99 : 6052 : void Connection::disconnect(bool wait) 100 : : { 101 : 6052 : std::shared_ptr<SignalData> signal(m_signal.lock()); 102 : 6051 : std::shared_ptr<BaseListener> listener(m_listener.lock()); 103 : : 104 : 6052 : if (!signal || !listener) 105 : : { 106 : 4034 : return; 107 : : } 108 : 2017 : if (listener->active.exchange(false) == false) 109 : : { 110 : : { 111 : 7 : std::unique_lock<std::mutex> lock(signal->lock); 112 : 7 : listener.reset(); 113 : 7 : } 114 : 7 : signal->cond.notify_all(); 115 : 7 : return; // was already inactive, let's not wait again 116 : : } 117 : : 118 : 2011 : if (wait) 119 : : { 120 : 2010 : std::unique_lock<std::mutex> lock(signal->lock); 121 : 2010 : const bool inHandler = signal->tids.count(std::this_thread::get_id()) != 122 : 2010 : 0; 123 : 2010 : signal->gc(); 124 : : 125 : : /* 126 : : + 1 ref here 127 : : + 1 ref if current thread is in handler 128 : : */ 129 : 2010 : signal->cond.wait(lock, [&listener, &inHandler]() { 130 : 3531 : return inHandler ? (listener.use_count() <= 2) : 131 : 3531 : (listener.use_count() <= 1); 132 : : }); 133 : 2010 : } 134 : 10093 : } 135 : : 136 : : } // namespace ccut