Line data Source code
1 : /* 2 : * Copyright (C) 2004-2026 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 : #pragma once 18 : 19 : #include "noncopyable.h" 20 : 21 : #include <cstdlib> 22 : #include <memory> 23 : #include <set> 24 : #include <list> 25 : #include <mutex> 26 : #include <functional> 27 : 28 : #ifndef __DEBUG__ // this is only defined on plugins build for debugging 29 : #include "logger.h" 30 : #endif 31 : 32 : namespace jami { 33 : 34 : template<typename T> 35 : class Observer; 36 : template<typename T> 37 : class Observable; 38 : 39 : /*=== Observable =============================================================*/ 40 : 41 : template<typename T> 42 : class Observable 43 : { 44 : public: 45 2047 : Observable() 46 2047 : : mutex_() 47 2047 : , observers_() 48 2047 : {} 49 : 50 : /** 51 : * @brief ~Observable 52 : * Detach all observers to avoid making them call this observable when 53 : * destroyed 54 : */ 55 2047 : virtual ~Observable() 56 : { 57 2047 : std::lock_guard lk(mutex_); 58 : 59 2750 : for (auto& pobs : priority_observers_) { 60 1099 : if (auto so = pobs.lock()) { 61 396 : so->detached(this); 62 : } 63 : } 64 : 65 2047 : for (auto& o : observers_) 66 0 : o->detached(this); 67 4094 : } 68 : 69 575 : bool attach(Observer<T>* o) 70 : { 71 575 : std::lock_guard lk(mutex_); 72 575 : if (o and observers_.insert(o).second) { 73 575 : o->attached(this); 74 575 : return true; 75 : } 76 0 : return false; 77 575 : } 78 : 79 732 : void attachPriorityObserver(std::shared_ptr<Observer<T>> o) 80 : { 81 732 : std::lock_guard lk(mutex_); 82 732 : priority_observers_.push_back(o); 83 732 : o->attached(this); 84 732 : } 85 : 86 : void detachPriorityObserver(Observer<T>* o) 87 : { 88 : std::lock_guard lk(mutex_); 89 : for (auto it = priority_observers_.begin(); it != priority_observers_.end(); it++) { 90 : if (auto so = it->lock()) { 91 : if (so.get() == o) { 92 : so->detached(this); 93 : priority_observers_.erase(it); 94 : return; 95 : } 96 : } 97 : } 98 : } 99 : 100 799 : bool detach(Observer<T>* o) 101 : { 102 799 : std::lock_guard lk(mutex_); 103 799 : if (o and observers_.erase(o)) { 104 575 : o->detached(this); 105 575 : return true; 106 : } 107 224 : return false; 108 799 : } 109 : 110 8611 : size_t getObserversCount() 111 : { 112 8611 : std::lock_guard lk(mutex_); 113 17222 : return observers_.size() + priority_observers_.size(); 114 8611 : } 115 : 116 : protected: 117 17116 : void notify(T data) 118 : { 119 17116 : std::lock_guard lk(mutex_); 120 25614 : for (auto it = priority_observers_.begin(); it != priority_observers_.end();) { 121 16996 : if (auto so = it->lock()) { 122 8469 : it++; 123 : try { 124 8469 : so->update(this, data); 125 0 : } catch (std::exception& e) { 126 : #ifndef __DEBUG__ 127 0 : JAMI_ERR() << e.what(); 128 : #endif 129 : } 130 : } else { 131 29 : it = priority_observers_.erase(it); 132 : } 133 : } 134 : 135 35629 : for (auto observer : observers_) { 136 18513 : observer->update(this, data); 137 : } 138 17116 : } 139 : 140 : private: 141 : NON_COPYABLE(Observable); 142 : 143 : protected: 144 : std::mutex mutex_; // lock observers_ 145 : std::list<std::weak_ptr<Observer<T>>> priority_observers_; 146 : std::set<Observer<T>*> observers_; 147 : }; 148 : 149 : template<typename T> 150 : class PublishObservable : public Observable<T> 151 : { 152 : public: 153 0 : void publish(T data) { this->notify(data); } 154 : }; 155 : 156 : /*=== Observer =============================================================*/ 157 : 158 : template<typename T> 159 : class Observer 160 : { 161 : public: 162 1778 : virtual ~Observer() {} 163 : virtual void update(Observable<T>*, const T&) = 0; 164 465 : virtual void attached(Observable<T>*) {} 165 465 : virtual void detached(Observable<T>*) {} 166 : }; 167 : 168 : template<typename T> 169 : class FuncObserver : public Observer<T> 170 : { 171 : public: 172 : using F = std::function<void(const T&)>; 173 : FuncObserver(F f) 174 : : f_(f) 175 : {} 176 : virtual ~FuncObserver() {} 177 : void update(Observable<T>*, const T& t) override { f_(t); } 178 : 179 : private: 180 : F f_; 181 : }; 182 : 183 : /*=== PublishMapSubject ====================================================*/ 184 : 185 : template<typename T1, typename T2> 186 : class PublishMapSubject : public Observer<T1>, public Observable<T2> 187 : { 188 : public: 189 : using F = std::function<T2(const T1&)>; 190 : 191 1174 : PublishMapSubject(F f) 192 1174 : : map_ {f} 193 1174 : {} 194 : 195 8469 : void update(Observable<T1>*, const T1& t) override { this->notify(map_(t)); } 196 : 197 : /** 198 : * @brief attached 199 : * Here we just make sure that the PublishMapSubject is only attached to one 200 : * Observable at a time. 201 : * @param srcObs 202 : */ 203 732 : virtual void attached(Observable<T1>* srcObs) override 204 : { 205 732 : if (obs_ != nullptr && obs_ != srcObs) { 206 0 : obs_->detach(this); 207 0 : obs_ = srcObs; 208 : } 209 732 : } 210 : 211 : /** 212 : * @brief detached 213 : * Since a MapSubject is only attached to one Observable, when detached 214 : * We should detach all of it observers 215 : */ 216 1570 : virtual void detached(Observable<T1>*) override 217 : { 218 1570 : std::lock_guard lk(this->mutex_); 219 1570 : for (auto& pobs : this->priority_observers_) { 220 0 : if (auto so = pobs.lock()) { 221 0 : so->detached(this); 222 : } 223 : } 224 1570 : for (auto& o : this->observers_) 225 0 : o->detached(this); 226 1570 : } 227 : 228 : /** 229 : * @brief ~PublishMapSubject() 230 : * Detach all observers to avoid making them call this observable when 231 : * destroyed 232 : **/ 233 1174 : ~PublishMapSubject() { detached(nullptr); } 234 : 235 : private: 236 : F map_; 237 : Observable<T1>* obs_ = nullptr; 238 : }; 239 : 240 : }; // namespace jami