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 769 : Observable() 46 769 : : mutex_() 47 769 : , observers_() 48 770 : {} 49 : 50 : /** 51 : * @brief ~Observable 52 : * Detach all observers to avoid making them call this observable when 53 : * destroyed 54 : */ 55 770 : virtual ~Observable() 56 : { 57 770 : std::lock_guard lk(mutex_); 58 : 59 1002 : for (auto& pobs : priority_observers_) { 60 393 : if (auto so = pobs.lock()) { 61 163 : so->detached(this); 62 : } 63 : } 64 : 65 769 : for (auto& o : observers_) 66 0 : o->detached(this); 67 1539 : } 68 : 69 168 : bool attach(Observer<T>* o) 70 : { 71 168 : std::lock_guard lk(mutex_); 72 168 : if (o and observers_.insert(o).second) { 73 168 : o->attached(this); 74 168 : return true; 75 : } 76 0 : return false; 77 168 : } 78 : 79 239 : void attachPriorityObserver(std::shared_ptr<Observer<T>> o) 80 : { 81 239 : std::lock_guard lk(mutex_); 82 239 : priority_observers_.push_back(o); 83 239 : o->attached(this); 84 239 : } 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 226 : bool detach(Observer<T>* o) 101 : { 102 226 : std::lock_guard lk(mutex_); 103 226 : if (o and observers_.erase(o)) { 104 168 : o->detached(this); 105 168 : return true; 106 : } 107 58 : return false; 108 226 : } 109 : 110 544 : size_t getObserversCount() 111 : { 112 544 : std::lock_guard lk(mutex_); 113 1088 : return observers_.size() + priority_observers_.size(); 114 544 : } 115 : 116 : protected: 117 905 : void notify(T data) 118 : { 119 905 : std::lock_guard lk(mutex_); 120 1259 : for (auto it = priority_observers_.begin(); it != priority_observers_.end();) { 121 708 : if (auto so = it->lock()) { 122 347 : it++; 123 : try { 124 347 : 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 7 : it = priority_observers_.erase(it); 132 : } 133 : } 134 : 135 1453 : for (auto observer : observers_) { 136 548 : observer->update(this, data); 137 : } 138 905 : } 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 627 : virtual ~Observer() {} 163 : virtual void update(Observable<T>*, const T&) = 0; 164 158 : virtual void attached(Observable<T>*) {} 165 158 : 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 417 : PublishMapSubject(F f) 192 417 : : map_ {f} 193 417 : {} 194 : 195 347 : 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 239 : virtual void attached(Observable<T1>* srcObs) override 204 : { 205 239 : if (obs_ != nullptr && obs_ != srcObs) { 206 0 : obs_->detach(this); 207 0 : obs_ = srcObs; 208 : } 209 239 : } 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 580 : virtual void detached(Observable<T1>*) override 217 : { 218 580 : std::lock_guard lk(this->mutex_); 219 580 : for (auto& pobs : this->priority_observers_) { 220 0 : if (auto so = pobs.lock()) { 221 0 : so->detached(this); 222 : } 223 : } 224 580 : for (auto& o : this->observers_) 225 0 : o->detached(this); 226 580 : } 227 : 228 : /** 229 : * @brief ~PublishMapSubject() 230 : * Detach all observers to avoid making them call this observable when 231 : * destroyed 232 : **/ 233 417 : ~PublishMapSubject() { detached(nullptr); } 234 : 235 : private: 236 : F map_; 237 : Observable<T1>* obs_ = nullptr; 238 : }; 239 : 240 : }; // namespace jami