Line data Source code
1 : /* 2 : * Copyright (C) 2004-2024 Savoir-faire Linux Inc. 3 : * 4 : * Author: Guillaume Roguez <Guillaume.Roguez@savoirfairelinux.com> 5 : * Author: Adrien BĂ©raud <adrien.beraud@savoirfairelinux.com> 6 : * 7 : * This program is free software; you can redistribute it and/or modify 8 : * it under the terms of the GNU General Public License as published by 9 : * the Free Software Foundation; either version 3 of the License, or 10 : * (at your option) any later version. 11 : * 12 : * This program is distributed in the hope that it will be useful, 13 : * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 : * GNU General Public License for more details. 16 : * 17 : * You should have received a copy of the GNU General Public License 18 : * along with this program; if not, write to the Free Software 19 : * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 20 : */ 21 : 22 : #pragma once 23 : 24 : #include "noncopyable.h" 25 : 26 : #include <cstdlib> 27 : #include <cstdint> 28 : #include <memory> 29 : #include <set> 30 : #include <list> 31 : #include <mutex> 32 : #include <functional> 33 : #include <ciso646> // fix windows compiler bug 34 : #ifndef __DEBUG__ // this is only defined on plugins build for debugging 35 : #include "logger.h" 36 : #endif 37 : 38 : namespace jami { 39 : 40 : template<typename T> 41 : class Observer; 42 : template<typename T> 43 : class Observable; 44 : 45 : /*=== Observable =============================================================*/ 46 : 47 : template<typename T> 48 : class Observable 49 : { 50 : public: 51 1747 : Observable() 52 1747 : : mutex_() 53 1747 : , observers_() 54 1747 : {} 55 : 56 : /** 57 : * @brief ~Observable 58 : * Detach all observers to avoid making them call this observable when 59 : * destroyed 60 : */ 61 1747 : virtual ~Observable() 62 : { 63 1747 : std::lock_guard lk(mutex_); 64 : 65 2493 : for (auto& pobs : priority_observers_) { 66 1144 : if (auto so = pobs.lock()) { 67 398 : so->detached(this); 68 : } 69 : } 70 : 71 1751 : for (auto& o : observers_) 72 4 : o->detached(this); 73 3494 : } 74 : 75 550 : bool attach(Observer<T>* o) 76 : { 77 550 : std::lock_guard lk(mutex_); 78 550 : if (o and observers_.insert(o).second) { 79 550 : o->attached(this); 80 550 : return true; 81 : } 82 0 : return false; 83 550 : } 84 : 85 782 : void attachPriorityObserver(std::shared_ptr<Observer<T>> o) 86 : { 87 782 : std::lock_guard lk(mutex_); 88 782 : priority_observers_.push_back(o); 89 782 : o->attached(this); 90 782 : } 91 : 92 : void detachPriorityObserver(Observer<T>* o) 93 : { 94 : std::lock_guard lk(mutex_); 95 : for (auto it = priority_observers_.begin(); it != priority_observers_.end(); it++) { 96 : if (auto so = it->lock()) { 97 : if (so.get() == o) { 98 : so->detached(this); 99 : priority_observers_.erase(it); 100 : return; 101 : } 102 : } 103 : } 104 : } 105 : 106 808 : bool detach(Observer<T>* o) 107 : { 108 808 : std::lock_guard lk(mutex_); 109 808 : if (o and observers_.erase(o)) { 110 546 : o->detached(this); 111 546 : return true; 112 : } 113 262 : return false; 114 808 : } 115 : 116 9004 : size_t getObserversCount() 117 : { 118 9004 : std::lock_guard lk(mutex_); 119 18008 : return observers_.size() + priority_observers_.size(); 120 9004 : } 121 : 122 : protected: 123 18044 : void notify(T data) 124 : { 125 18044 : std::lock_guard lk(mutex_); 126 27083 : for (auto it = priority_observers_.begin(); it != priority_observers_.end();) { 127 18078 : if (auto so = it->lock()) { 128 9003 : it++; 129 : try { 130 9003 : so->update(this, data); 131 0 : } catch (std::exception& e) { 132 : #ifndef __DEBUG__ 133 0 : JAMI_ERR() << e.what(); 134 : #endif 135 : } 136 : } else { 137 36 : it = priority_observers_.erase(it); 138 : } 139 : } 140 : 141 37814 : for (auto observer : observers_) { 142 19770 : observer->update(this, data); 143 : } 144 18044 : } 145 : 146 : private: 147 : NON_COPYABLE(Observable<T>); 148 : 149 : protected: 150 : std::mutex mutex_; // lock observers_ 151 : std::list<std::weak_ptr<Observer<T>>> priority_observers_; 152 : std::set<Observer<T>*> observers_; 153 : }; 154 : 155 : template<typename T> 156 : class PublishObservable : public Observable<T> 157 : { 158 : public: 159 1 : void publish(T data) { this->notify(data); } 160 : }; 161 : 162 : /*=== Observer =============================================================*/ 163 : 164 : template<typename T> 165 : class Observer 166 : { 167 : public: 168 1495 : virtual ~Observer() {} 169 : virtual void update(Observable<T>*, const T&) = 0; 170 465 : virtual void attached(Observable<T>*) {} 171 465 : virtual void detached(Observable<T>*) {} 172 : }; 173 : 174 : template<typename T> 175 : class FuncObserver : public Observer<T> 176 : { 177 : public: 178 : using F = std::function<void(const T&)>; 179 : FuncObserver(F f) 180 : : f_(f) 181 : {} 182 : virtual ~FuncObserver() {} 183 : void update(Observable<T>*, const T& t) override { f_(t); } 184 : 185 : private: 186 : F f_; 187 : }; 188 : 189 : /*=== PublishMapSubject ====================================================*/ 190 : 191 : template<typename T1, typename T2> 192 : class PublishMapSubject : public Observer<T1>, public Observable<T2> 193 : { 194 : public: 195 : using F = std::function<T2(const T1&)>; 196 : 197 849 : PublishMapSubject(F f) 198 849 : : map_ {f} 199 849 : {} 200 : 201 9003 : void update(Observable<T1>*, const T1& t) override { this->notify(map_(t)); } 202 : 203 : /** 204 : * @brief attached 205 : * Here we just make sure that the PublishMapSubject is only attached to one 206 : * Observable at a time. 207 : * @param srcObs 208 : */ 209 782 : virtual void attached(Observable<T1>* srcObs) override 210 : { 211 782 : if (obs_ != nullptr && obs_ != srcObs) { 212 0 : obs_->detach(this); 213 0 : obs_ = srcObs; 214 : } 215 782 : } 216 : 217 : /** 218 : * @brief detached 219 : * Since a MapSubject is only attached to one Observable, when detached 220 : * We should detach all of it observers 221 : */ 222 1247 : virtual void detached(Observable<T1>*) override 223 : { 224 1247 : std::lock_guard lk(this->mutex_); 225 1247 : for (auto& pobs : this->priority_observers_) { 226 0 : if (auto so = pobs.lock()) { 227 0 : so->detached(this); 228 : } 229 : } 230 1247 : for (auto& o : this->observers_) 231 0 : o->detached(this); 232 1247 : } 233 : 234 : /** 235 : * @brief ~PublishMapSubject() 236 : * Detach all observers to avoid making them call this observable when 237 : * destroyed 238 : **/ 239 849 : ~PublishMapSubject() { detached(nullptr); } 240 : 241 : private: 242 : F map_; 243 : Observable<T1>* obs_ = nullptr; 244 : }; 245 : 246 : }; // namespace jami