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 : : #ifndef SIGNAL_HXX__ 26 : : #define SIGNAL_HXX__ 27 : : 28 : : #include <iostream> 29 : : #include <mutex> 30 : : 31 : : #include "Signal.hpp" 32 : : #include <logger/Logger.hpp> 33 : : 34 : : namespace ccut { 35 : : 36 : : /** 37 : : * @brief signal internals 38 : : */ 39 : : struct SignalData 40 : : { 41 : : public: 42 : : ~SignalData(); // for the shared_ptr 43 : : 44 : : protected: 45 : : template<typename... A> 46 : : friend class Signal; 47 : : friend class Connection; 48 : : 49 : : void gc(); 50 : : void disconnect(bool wait); 51 : : 52 : : std::mutex lock; 53 : : std::condition_variable cond; 54 : : std::set<std::thread::id> tids; 55 : : std::list<std::shared_ptr<BaseListener>> list; 56 : : }; 57 : : 58 : : 59 : : template<typename... Args> 60 : 25 : Signal<Args...>::Signal() : m_data(std::make_shared<SignalData>()) 61 : 25 : {} 62 : : 63 : : template<typename... Args> 64 : 2017 : Signal<Args...>::Listener::Listener(std::function<void(Args...)> fun) : 65 : 2017 : BaseListener(), call(fun) 66 : 2017 : {} 67 : : 68 : : template<typename... Args> 69 : 2083 : void Signal<Args...>::operator()(Args... args) 70 : : { 71 : 2083 : std::vector<std::weak_ptr<BaseListener>> list; 72 : : 73 : : { 74 : 2083 : std::unique_lock<std::mutex> lock(m_data->lock); 75 : 2083 : list.swap(m_cache); 76 : 2083 : const std::size_t sz = m_data->list.size(); 77 : : 78 : 2083 : if (sz > list.capacity()) 79 : : { 80 : 76 : lock.unlock(); 81 : 76 : list.reserve(sz); 82 : 76 : lock.lock(); 83 : : } 84 : : 85 : 2083 : list.assign(m_data->list.begin(), m_data->list.end()); 86 : 2083 : m_data->tids.insert(std::this_thread::get_id()); 87 : 2083 : } 88 : : 89 : 2083 : bool clean = false; 90 : 3844997 : for (std::weak_ptr<BaseListener> &wc : list) 91 : : { 92 : 1874443 : std::shared_ptr<BaseListener> c(wc.lock()); 93 : 1930755 : if (!c) 94 : 213 : continue; 95 : 1929959 : else if (!(*c)) 96 : 687 : clean = true; 97 : : else 98 : 1927734 : static_cast<Listener &>(*c).call(std::forward<Args>(args)...); 99 : : } 100 : : 101 : : { 102 : 1613 : std::lock_guard<std::mutex> lock(m_data->lock); 103 : 2083 : if (clean) 104 : 52 : m_data->gc(); 105 : 2083 : list.swap(m_cache); 106 : 2083 : m_data->tids.erase(std::this_thread::get_id()); 107 : 2083 : } 108 : 2083 : m_data->cond.notify_all(); 109 : 2083 : } 110 : : 111 : : template<typename... Args> 112 : 3 : void Signal<Args...>::disconnect(bool wait) 113 : : { 114 : 3 : m_data->disconnect(wait); 115 : 3 : } 116 : : 117 : : template<typename... Args> 118 : 1015 : Connection Signal<Args...>::connect(std::function<void(Args...)> fun) 119 : : { 120 : 1015 : std::lock_guard<std::mutex> lock(m_data->lock); 121 : 1015 : m_data->list.emplace_back(std::make_shared<Listener>(fun)); 122 : 2030 : return Connection(m_data, m_data->list.back()); 123 : 1015 : } 124 : : 125 : : template<typename... Args> 126 : 1002 : Connection &Signal<Args...>::connect(Connection &conn, 127 : : std::function<void(Args...)> fun) 128 : : { 129 : 1002 : std::lock_guard<std::mutex> lock(m_data->lock); 130 : 1002 : m_data->list.emplace_back(std::make_shared<Listener>(fun)); 131 : 1002 : conn = std::move(Connection(m_data, m_data->list.back())); 132 : 1002 : return conn; 133 : 1002 : } 134 : : 135 : : } // namespace ccut 136 : : 137 : : #endif