LCOV - code coverage report
Current view: top level - src - observer.h (source / functions) Coverage Total Hit
Test: jami-coverage-filtered.info Lines: 85.5 % 69 59
Test Date: 2026-06-13 09:18:46 Functions: 54.5 % 33 18

            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
        

Generated by: LCOV version 2.0-1