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