LCOV - code coverage report
Current view: top level - src - observer.h (source / functions) Coverage Total Hit
Test: jami-coverage-filtered.info Lines: 87.0 % 69 60
Test Date: 2026-07-29 09:02:12 Functions: 81.5 % 27 22

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

Generated by: LCOV version 2.0-1