LCOV - code coverage report
Current view: top level - core/core/utils - SPSubscription.h (source / functions) Hit Total Coverage
Test: coverage.info Lines: 84 93 90.3 %
Date: 2024-05-12 00:16:13 Functions: 48 51 94.1 %

          Line data    Source code
       1             : /**
       2             : Copyright (c) 2023 Stappler LLC <admin@stappler.dev>
       3             : 
       4             : Permission is hereby granted, free of charge, to any person obtaining a copy
       5             : of this software and associated documentation files (the "Software"), to deal
       6             : in the Software without restriction, including without limitation the rights
       7             : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
       8             : copies of the Software, and to permit persons to whom the Software is
       9             : furnished to do so, subject to the following conditions:
      10             : 
      11             : The above copyright notice and this permission notice shall be included in
      12             : all copies or substantial portions of the Software.
      13             : 
      14             : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
      15             : IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      16             : FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
      17             : AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      18             : LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
      19             : OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
      20             : THE SOFTWARE.
      21             : **/
      22             : 
      23             : #ifndef STAPPLER_CORE_UTILS_SPSUBSCRIPTION_H_
      24             : #define STAPPLER_CORE_UTILS_SPSUBSCRIPTION_H_
      25             : 
      26             : #include "SPRef.h"
      27             : 
      28             : namespace STAPPLER_VERSIONIZED stappler {
      29             : 
      30             : using SubscriptionId = ValueWrapper<uint64_t, class SubscriptionIdClassFlag>;
      31             : 
      32             : struct SubscriptionFlags : public ValueWrapper<uint64_t, class SubscriptionFlagsClassFlag> {
      33             :         using Super = ValueWrapper<uint64_t, class SubscriptionFlagsClassFlag>;
      34             : 
      35             :         inline constexpr explicit SubscriptionFlags(const Type &val) : Super(val) { }
      36       49091 :         inline constexpr explicit SubscriptionFlags(Type &&val) : Super(std::move(val)) { }
      37             : 
      38       50689 :         inline constexpr SubscriptionFlags(const SubscriptionFlags &other) { value = other.value; }
      39             :         inline constexpr SubscriptionFlags &operator=(const SubscriptionFlags &other) { value = other.value; return *this; }
      40       50094 :         inline constexpr SubscriptionFlags(SubscriptionFlags &&other) { value = std::move(other.value); }
      41       48941 :         inline constexpr SubscriptionFlags &operator=(SubscriptionFlags &&other) { value = std::move(other.value); return *this; }
      42             : 
      43             :         inline constexpr SubscriptionFlags(const Super &other) { value = other.value; }
      44             :         inline constexpr SubscriptionFlags &operator=(const Super &other) { value = other.value; return *this; }
      45             :         inline constexpr SubscriptionFlags(Super &&other) { value = std::move(other.value); }
      46             :         inline constexpr SubscriptionFlags &operator=(Super &&other) { value = std::move(other.value); return *this; }
      47             : 
      48             :         template <typename T>
      49             :         inline constexpr bool hasFlag(T f) const {
      50             :                 return (toInt(f) & value) != 0;
      51             :         }
      52             : 
      53             :         inline constexpr bool initial() const {
      54             :                 return (value & 1) != 0;
      55             :         }
      56             : };
      57             : 
      58             : /* Subscription is a refcounted class, that allow
      59             :  * provider object (derived from Subscription) to post some update flags,
      60             :  * and subscribers (any other objects) to receive it via `check` call
      61             :  *
      62             :  * Every registered subscriber has it's own id, that used to check for updates
      63             :  * since last call of `check`
      64             :  */
      65             : template <typename _Interface>
      66             : class SubscriptionTemplate : public RefBase<_Interface> {
      67             : public:
      68             :         using Interface = _Interface;
      69             :         using Id = SubscriptionId;
      70             :         using Flags = SubscriptionFlags;
      71             :         using FlagsMap = typename Interface::template MapType<Id, Flags>;
      72             : 
      73             :         // get unique subscription id
      74             :         static Id getNextId();
      75             : 
      76             :         // initial flags value, every new subscriber receive this on first call of 'check'
      77             :         static constexpr Flags Initial = Flags(1);
      78             : 
      79         558 :         virtual ~SubscriptionTemplate() {
      80         558 :                 setForwardedSubscription(nullptr);
      81         558 :         }
      82             : 
      83             :         // safe way to get Flag with specific bit set, prevents UB
      84             :         template <class T>
      85             :         static Flags _Flag(T idx) { return ((uint8_t)idx == 0 || (uint8_t)idx > (sizeof(Flags) * 8))?Flags(0):Flags(1 << (uint8_t)idx); }
      86             : 
      87             :         inline static Flags Flag() { return Flags(0); }
      88             : 
      89             :         // Variadic way to get specific bits set via multiple arguments of different integral types
      90             :         template <class T, class... Args>
      91             :         static Flags Flag(T val, Args&&... args) { return _Flag(val) | Flag(args...); }
      92             : 
      93             :         // Set subscription dirty flags
      94             :         void setDirty(Flags flags = Initial, bool forwardedOnly = false);
      95             : 
      96             :         // subscribe with specific object id
      97             :         bool subscribe(Id);
      98             : 
      99             :         // unsubscribe object by id
     100             :         bool unsubscribe(Id);
     101             : 
     102             :         // check wich flags has been set dirty since last check
     103             :         Flags check(Id);
     104             : 
     105             :         // subscription can actually be a reference to other subscription, in this case
     106             :         // current subscription works as bidirectional forwarder for reference subscription
     107             :         void setForwardedSubscription(SubscriptionTemplate *);
     108             : 
     109             : protected:
     110             :         Rc<SubscriptionTemplate> _forwarded;
     111             :         FlagsMap *_forwardedFlags = nullptr;
     112             :         FlagsMap _flags;
     113             : };
     114             : 
     115             : /** Binding is an interface or slot for subscription, that handles
     116             :  * - unique id acquisition
     117             :  * - proper refcounting for binded subscription
     118             :  * - easy access and not-null check for binded subscription
     119             :  *
     120             :  */
     121             : template <class T>
     122             : class Binding {
     123             : public:
     124             :         using Id = SubscriptionId;
     125             :         using Flags = SubscriptionFlags;
     126             : 
     127             :         Binding();
     128             :         Binding(T *);
     129             : 
     130             :         ~Binding();
     131             : 
     132             :         Binding(const Binding<T> &);
     133             :         Binding &operator= (const Binding<T> &);
     134             : 
     135             :         Binding(Binding<T> &&);
     136             :         Binding &operator= (Binding<T> &&);
     137             : 
     138       18074 :         inline operator T * () { return get(); }
     139             :         inline operator T * () const { return get(); }
     140             :         inline explicit operator bool () const { return _subscription; }
     141             : 
     142             :         inline T * operator->() { return get(); }
     143             :         inline const T * operator->() const { return get(); }
     144             : 
     145             :         Flags check();
     146             : 
     147             :         void set(T *);
     148             :         T *get() const;
     149             : 
     150             : protected:
     151             :         Id _id;
     152             :         Rc<T> _subscription;
     153             : };
     154             : 
     155             : template <typename Interface>
     156         570 : void SubscriptionTemplate<Interface>::setDirty(Flags flags, bool forwardedOnly) {
     157         570 :         if (_forwardedFlags) {
     158          75 :                 for (auto &it : (*_forwardedFlags)) {
     159          50 :                         if (forwardedOnly) {
     160          50 :                                 if (_flags.find(it.first) != _flags.end()) {
     161          25 :                                         it.second |= flags;
     162             :                                 }
     163             :                         } else {
     164           0 :                                 it.second |= flags;
     165             :                         }
     166             :                 }
     167             :         } else {
     168         657 :                 for (auto &it : _flags) {
     169         112 :                         it.second |= flags;
     170             :                 }
     171             :         }
     172         570 : }
     173             : 
     174             : template <typename Interface>
     175         583 : bool SubscriptionTemplate<Interface>::subscribe(Id id) {
     176         583 :         if (_forwardedFlags) {
     177          25 :                 auto it = _forwardedFlags->find(id);
     178          25 :                 if (it == _forwardedFlags->end()) {
     179          25 :                         _forwardedFlags->insert(std::make_pair(id, Initial));
     180          25 :                         _flags.insert(std::make_pair(id, Initial));
     181          25 :                         return true;
     182             :                 }
     183             :         } else {
     184         558 :                 auto it = _flags.find(id);
     185         558 :                 if (it == _flags.end()) {
     186         558 :                         _flags.insert(std::make_pair(id, Initial));
     187         558 :                         return true;
     188             :                 }
     189             :         }
     190           0 :         return false;
     191             : }
     192             : 
     193             : template <typename Interface>
     194         583 : bool SubscriptionTemplate<Interface>::unsubscribe(Id id) {
     195         583 :         if (_forwardedFlags) {
     196          25 :                 auto it = _forwardedFlags->find(id);
     197          25 :                 if (it == _forwardedFlags->end()) {
     198           0 :                         _forwardedFlags->erase(id);
     199           0 :                         _flags.erase(id);
     200           0 :                         return true;
     201             :                 }
     202             :         } else {
     203         558 :                 auto it = _flags.find(id);
     204         558 :                 if (it != _flags.end()) {
     205         558 :                         _flags.erase(id);
     206         558 :                         return true;
     207             :                 }
     208             :         }
     209          25 :         return false;
     210             : }
     211             : 
     212             : template <typename Interface>
     213       48941 : SubscriptionFlags SubscriptionTemplate<Interface>::check(Id id) {
     214       48941 :         if (_forwardedFlags) {
     215          25 :                 auto it = _forwardedFlags->find(id);
     216          25 :                 if (it != _forwardedFlags->end()) {
     217          25 :                         auto val = it->second;
     218          25 :                         it->second = Flags(0);
     219          25 :                         return val;
     220             :                 }
     221             :         } else {
     222       48916 :                 auto it = _flags.find(id);
     223       48916 :                 if (it != _flags.end()) {
     224       48916 :                         auto val = it->second;
     225       48916 :                         it->second = Flags(0);
     226       48916 :                         return val;
     227             :                 }
     228             :         }
     229           0 :         return Flags(0);
     230             : }
     231             : 
     232             : template <typename Interface>
     233         583 : void SubscriptionTemplate<Interface>::setForwardedSubscription(SubscriptionTemplate *sub) {
     234         583 :         if (_forwardedFlags) {
     235             :                 // erase forwarded items from sub
     236          50 :                 for (auto &it : _flags) {
     237          25 :                         _forwardedFlags->erase(it.first);
     238             :                 }
     239             :         }
     240         583 :         _forwardedFlags = nullptr;
     241         583 :         _forwarded = sub;
     242         583 :         _flags.clear();
     243         583 :         if (sub) {
     244          25 :                 _forwardedFlags = sub->_forwardedFlags ? sub->_forwardedFlags : &sub->_flags;
     245             :         }
     246         583 : }
     247             : 
     248             : template <class T>
     249         240 : Binding<T>::Binding() : _id(T::getNextId()) { }
     250             : 
     251             : template <class T>
     252         200 : Binding<T>::Binding(T *sub) : _id(T::getNextId()), _subscription(sub) {
     253         200 :         if (_subscription) {
     254         200 :                 _subscription->subscribe(_id);
     255             :         }
     256         200 : }
     257             : 
     258             : template <class T>
     259         440 : Binding<T>::~Binding() {
     260         440 :         if (_subscription) {
     261         210 :                 _subscription->unsubscribe(_id);
     262             :         }
     263         440 : }
     264             : 
     265             : template <class T>
     266             : Binding<T>::Binding(const Binding<T> &other) : _id(T::getNextId()), _subscription(other._subscription) {
     267             :         if (_subscription) {
     268             :                 _subscription->subscribe(_id);
     269             :         }
     270             : }
     271             : 
     272             : template <class T>
     273             : Binding<T>& Binding<T>::operator= (const Binding<T> &other) {
     274             :         if (_subscription) {
     275             :                 _subscription->unsubscribe(_id);
     276             :         }
     277             :         _subscription = other._subscription;
     278             :         if (_subscription) {
     279             :                 _subscription->subscribe(_id);
     280             :         }
     281             :         return *this;
     282             : }
     283             : 
     284             : template <class T>
     285             : Binding<T>::Binding(Binding &&other) {
     286             :         _id = other._id;
     287             :         _subscription = std::move(other._subscription);
     288             : }
     289             : 
     290             : template <class T>
     291         200 : Binding<T> &Binding<T>::operator= (Binding<T> &&other) {
     292         200 :         if (_subscription) {
     293           0 :                 _subscription->unsubscribe(_id);
     294             :         }
     295         200 :         _subscription = std::move(other._subscription);
     296         200 :         _id = other._id;
     297         200 :         return *this;
     298             : }
     299             : 
     300             : template <class T>
     301       17864 : SubscriptionFlags Binding<T>::check() {
     302       17864 :         if (_subscription) {
     303       17864 :                 return _subscription->check(_id);
     304             :         }
     305           0 :         return Flags(0);
     306             : }
     307             : 
     308             : template <class T>
     309         240 : void Binding<T>::set(T *sub) {
     310         240 :         if (_subscription) {
     311           0 :                 _subscription->unsubscribe(_id);
     312             :         }
     313         240 :         _subscription = sub;
     314         240 :         if (_subscription) {
     315          10 :                 _subscription->subscribe(_id);
     316             :         }
     317         240 : }
     318             : 
     319             : template <class T>
     320       19830 : T *Binding<T>::get() const {
     321       19830 :         return _subscription ? _subscription.get() : nullptr;
     322             : }
     323             : 
     324             : }
     325             : 
     326             : namespace STAPPLER_VERSIONIZED stappler::mem_std {
     327             : 
     328             : using Subscription = stappler::SubscriptionTemplate<memory::StandartInterface>;
     329             : 
     330             : }
     331             : 
     332             : namespace STAPPLER_VERSIONIZED stappler::mem_pool {
     333             : 
     334             : using Subscription = stappler::SubscriptionTemplate<memory::PoolInterface>;
     335             : 
     336             : }
     337             : 
     338             : #endif /* STAPPLER_CORE_UTILS_SPSUBSCRIPTION_H_ */

Generated by: LCOV version 1.14