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 2300 : Observable()
46 2300 : : mutex_()
47 2300 : , observers_()
48 2299 : {}
49 :
50 : /**
51 : * @brief ~Observable
52 : * Detach all observers to avoid making them call this observable when
53 : * destroyed
54 : */
55 2300 : virtual ~Observable()
56 : {
57 2300 : std::lock_guard lk(mutex_);
58 :
59 3078 : for (auto& pobs : priority_observers_) {
60 1229 : if (auto so = pobs.lock()) {
61 448 : so->detached(this);
62 : }
63 : }
64 :
65 2299 : for (auto& o : observers_)
66 0 : o->detached(this);
67 4600 : }
68 :
69 622 : bool attach(Observer<T>* o)
70 : {
71 622 : std::lock_guard lk(mutex_);
72 622 : if (o and observers_.insert(o).second) {
73 622 : o->attached(this);
74 622 : return true;
75 : }
76 0 : return false;
77 622 : }
78 :
79 810 : void attachPriorityObserver(std::shared_ptr<Observer<T>> o)
80 : {
81 810 : std::lock_guard lk(mutex_);
82 810 : priority_observers_.push_back(o);
83 810 : o->attached(this);
84 810 : }
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 860 : bool detach(Observer<T>* o)
101 : {
102 860 : std::lock_guard lk(mutex_);
103 860 : if (o and observers_.erase(o)) {
104 622 : o->detached(this);
105 622 : return true;
106 : }
107 238 : return false;
108 860 : }
109 :
110 9019 : size_t getObserversCount()
111 : {
112 9019 : std::lock_guard lk(mutex_);
113 18038 : return observers_.size() + priority_observers_.size();
114 9019 : }
115 :
116 : protected:
117 17918 : void notify(T data)
118 : {
119 17918 : std::lock_guard lk(mutex_);
120 26808 : for (auto it = priority_observers_.begin(); it != priority_observers_.end();) {
121 17780 : if (auto so = it->lock()) {
122 8860 : it++;
123 : try {
124 8860 : so->update(this, data);
125 0 : } catch (std::exception& e) {
126 : #ifndef __DEBUG__
127 0 : JAMI_ERROR("{}", e.what());
128 : #endif
129 : }
130 : } else {
131 30 : it = priority_observers_.erase(it);
132 : }
133 : }
134 :
135 37784 : for (auto observer : observers_) {
136 19866 : observer->update(this, data);
137 : }
138 17918 : }
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 2 : void publish(T data) { this->notify(data); }
154 : };
155 :
156 : /*=== Observer =============================================================*/
157 :
158 : template<typename T>
159 : class Observer
160 : {
161 : public:
162 1992 : virtual ~Observer() {}
163 : virtual void update(Observable<T>*, const T&) = 0;
164 509 : virtual void attached(Observable<T>*) {}
165 509 : 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 1330 : PublishMapSubject(F f)
192 1330 : : map_ {f}
193 1330 : {}
194 :
195 8860 : 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 810 : virtual void attached(Observable<T1>* srcObs) override
204 : {
205 810 : if (obs_ != nullptr && obs_ != srcObs) {
206 0 : obs_->detach(this);
207 0 : obs_ = srcObs;
208 : }
209 810 : }
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 1779 : virtual void detached(Observable<T1>*) override
217 : {
218 1779 : std::lock_guard lk(this->mutex_);
219 1779 : for (auto& pobs : this->priority_observers_) {
220 0 : if (auto so = pobs.lock()) {
221 0 : so->detached(this);
222 : }
223 : }
224 1778 : for (auto& o : this->observers_)
225 0 : o->detached(this);
226 1778 : }
227 :
228 : /**
229 : * @brief ~PublishMapSubject()
230 : * Detach all observers to avoid making them call this observable when
231 : * destroyed
232 : **/
233 1330 : ~PublishMapSubject() { detached(nullptr); }
234 :
235 : private:
236 : F map_;
237 : Observable<T1>* obs_ = nullptr;
238 : };
239 :
240 : }; // namespace jami
|