LCOV - code coverage report
Current view: top level - src/bindings/cpp/include - kdbthread.hpp (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 142 146 97.3 %
Date: 2019-09-12 12:28:41 Functions: 44 59 74.6 %

          Line data    Source code
       1             : /**
       2             :  * @file
       3             :  *
       4             :  * @brief
       5             :  *
       6             :  * @copyright BSD License (see LICENSE.md or https://www.libelektra.org)
       7             :  */
       8             : 
       9             : #ifndef ELEKTRA_KDBTHREAD_HPP
      10             : #define ELEKTRA_KDBTHREAD_HPP
      11             : 
      12             : #include <kdbcontext.hpp>
      13             : 
      14             : #include <kdb.hpp>
      15             : 
      16             : #include <algorithm>
      17             : #include <cassert>
      18             : #include <functional>
      19             : #include <mutex>
      20             : #include <thread>
      21             : #include <unordered_map>
      22             : #include <vector>
      23             : 
      24             : namespace kdb
      25             : {
      26             : 
      27             : /// Subject from Observer pattern for ThreadContext
      28          18 : class ThreadSubject
      29             : {
      30             : public:
      31             :         virtual void notify (KeySet & ks) = 0;
      32             :         virtual void syncLayers () = 0;
      33             : };
      34             : 
      35        2056 : struct LayerAction
      36             : {
      37         348 :         LayerAction (bool activate_, std::shared_ptr<Layer> const & layer_) : activate (activate_), layer (std::move (layer_))
      38             :         {
      39             :         }
      40             :         bool activate; // false if deactivate
      41             :         std::shared_ptr<Layer> layer;
      42             : };
      43             : 
      44             : /// A vector of layers
      45             : typedef std::unordered_map<std::string, LayerAction> LayerMap;
      46             : typedef std::unordered_map<std::string, std::vector<std::function<void()>>> FunctionMap;
      47             : 
      48             : /// A data structure that is stored by context inside the Coordinator
      49        1806 : struct PerContext
      50             : {
      51             :         KeySet toUpdate;
      52             :         LayerMap toActivate;
      53             : };
      54             : 
      55             : class ThreadNoContext
      56             : {
      57             : public:
      58             :         /**
      59             :          * @brief attach a new value
      60             :          *
      61             :          * NoContext will never update anything
      62             :          */
      63             :         void attachByName (ELEKTRA_UNUSED std::string const & key_name, ELEKTRA_UNUSED ValueObserver & ValueObserver)
      64             :         {
      65             :         }
      66             : 
      67             :         /**
      68             :          * @brief The evaluated equals the non-evaluated name!
      69             :          *
      70             :          * @return NoContext always returns the same string
      71             :          */
      72             :         std::string evaluate (std::string const & key_name) const
      73             :         {
      74           2 :                 return key_name;
      75             :         }
      76             : 
      77             :         std::string evaluate (std::string const & key_name,
      78             :                               std::function<bool(std::string const &, std::string &, bool in_group)> const &) const
      79             :         {
      80           0 :                 return key_name;
      81             :         }
      82             : 
      83             :         /**
      84             :          * @brief (Re)attaches a ValueSubject to a thread or simply
      85             :          *        execute code in a locked section.
      86             :          *
      87             :          * NoContext just executes the function but does so in a
      88             :          * thread-safe way
      89             :          *
      90             :          * @param c the command to apply
      91             :          */
      92          10 :         void execute (Command & c)
      93             :         {
      94          30 :                 std::lock_guard<std::mutex> lock (m_mutex);
      95          20 :                 c ();
      96          10 :         }
      97             : 
      98             : private:
      99             :         std::mutex m_mutex;
     100             : };
     101             : 
     102             : /**
     103             :  * @brief Thread safe coordination of ThreadContext per Threads.
     104             :  */
     105             : class Coordinator
     106             : {
     107             : public:
     108             :         template <typename T>
     109           2 :         void onLayerActivation (std::function<void()> f)
     110             :         {
     111           6 :                 std::lock_guard<std::mutex> lock (m_mutexOnActivate);
     112           6 :                 std::shared_ptr<Layer> layer = std::make_shared<T> ();
     113           6 :                 m_onActivate[layer->id ()].push_back (f);
     114           2 :         }
     115             : 
     116             :         template <typename T>
     117           2 :         void onLayerDeactivation (std::function<void()> f)
     118             :         {
     119           6 :                 std::lock_guard<std::mutex> lock (m_mutexOnDeactivate);
     120           6 :                 std::shared_ptr<Layer> layer = std::make_shared<T> ();
     121           6 :                 m_onDeactivate[layer->id ()].push_back (f);
     122           2 :         }
     123             : 
     124             :         void onLayerActivation (std::string layerid, std::function<void()> f)
     125             :         {
     126             :                 std::lock_guard<std::mutex> lock (m_mutexOnActivate);
     127             :                 m_onActivate[layerid].push_back (f);
     128             :         }
     129             : 
     130             :         void onLayerDeactivation (std::string layerid, std::function<void()> f)
     131             :         {
     132             :                 std::lock_guard<std::mutex> lock (m_mutexOnDeactivate);
     133             :                 m_onDeactivate[layerid].push_back (f);
     134             :         }
     135             : 
     136             :         void clearOnLayerActivation (std::string layerid)
     137             :         {
     138             :                 std::lock_guard<std::mutex> lock (m_mutexOnActivate);
     139             :                 m_onActivate[layerid].clear ();
     140             :         }
     141             : 
     142             :         void clearOnLayerDeactivation (std::string layerid)
     143             :         {
     144             :                 std::lock_guard<std::mutex> lock (m_mutexOnDeactivate);
     145             :                 m_onDeactivate[layerid].clear ();
     146             :         }
     147             : 
     148             :         std::unique_lock<std::mutex> requireLock ()
     149             :         {
     150             :                 std::unique_lock<std::mutex> lock (m_mutex);
     151             :                 return lock;
     152             :         }
     153             : 
     154          44 :         Coordinator ()
     155         308 :         {
     156         132 :                 std::lock_guard<std::mutex> lock (m_mutex);
     157         264 :                 m_updates.insert (std::make_pair (nullptr, PerContext ()));
     158          44 :         }
     159             : 
     160          42 :         ~Coordinator ()
     161         168 :         {
     162             : #if DEBUG
     163         168 :                 for (auto & i : m_updates)
     164             :                 {
     165         336 :                         std::cout << "coordinator " << this << " left over: " << i.first << " with updates: " << i.second.toUpdate.size ()
     166         210 :                                   << " activations: " << i.second.toActivate.size () << std::endl;
     167             :                 }
     168             : #endif
     169          42 :         }
     170             : 
     171             : 
     172             : private:
     173             :         friend class ThreadContext;
     174             : 
     175          76 :         void attach (ThreadSubject * c)
     176             :         {
     177         228 :                 std::lock_guard<std::mutex> lock (m_mutex);
     178         380 :                 m_updates.insert (std::make_pair (c, m_updates[nullptr]));
     179          76 :         }
     180             : 
     181          94 :         void detach (ThreadSubject * c)
     182             :         {
     183         282 :                 std::lock_guard<std::mutex> lock (m_mutex);
     184         188 :                 m_updates.erase (c);
     185          94 :         }
     186             : 
     187             :         /**
     188             :          * @brief Update the given ThreadContext with newly assigned
     189             :          * values.
     190             :          */
     191         128 :         void updateNewlyAssignedValues (ThreadSubject * c)
     192             :         {
     193         256 :                 std::lock_guard<std::mutex> lock (m_mutex);
     194         256 :                 KeySet & toUpdate = m_updates[c].toUpdate;
     195         128 :                 if (toUpdate.size () == 0) return;
     196             : 
     197          28 :                 c->notify (toUpdate);
     198          28 :                 toUpdate.clear ();
     199             :         }
     200             : 
     201             :         /**
     202             :          * @brief Receive a function to be executed and remember
     203             :          * which keys need a update in the other ThreadContexts.
     204             :          */
     205         540 :         void execute (Command & c)
     206             :         {
     207        1620 :                 std::lock_guard<std::mutex> lock (m_mutex);
     208        1080 :                 Command::Pair ret = c ();
     209        1080 :                 c.oldKey = ret.first;
     210        1080 :                 c.newKey = ret.second;
     211         540 :                 if (c.hasChanged)
     212             :                 {
     213         360 :                         for (auto & i : m_updates)
     214             :                         {
     215         624 :                                 i.second.toUpdate.append (Key (c.newKey, KEY_CASCADING_NAME, KEY_END));
     216             :                         }
     217             :                 }
     218         540 :         }
     219             : 
     220          80 :         void runOnActivate (std::shared_ptr<Layer> layer)
     221             :         {
     222         240 :                 std::lock_guard<std::mutex> lock (m_mutexOnActivate);
     223         482 :                 for (auto && f : m_onActivate[layer->id ()])
     224             :                 {
     225           2 :                         f ();
     226             :                 }
     227          80 :         }
     228             : 
     229             :         /**
     230             :          * @brief Request that some layer needs to be globally
     231             :          * activated.
     232             :          *
     233             :          * @param cc requests it and already has it updated itself
     234             :          * @param layer to activate for all threads
     235             :          */
     236          80 :         void globalActivate (ThreadSubject * cc, std::shared_ptr<Layer> layer)
     237             :         {
     238         160 :                 runOnActivate (layer);
     239             : 
     240         240 :                 std::lock_guard<std::mutex> lock (m_mutex);
     241         458 :                 for (auto & c : m_updates)
     242             :                 {
     243             :                         // caller itself has it already activated
     244         218 :                         if (cc == c.first) continue;
     245         690 :                         c.second.toActivate.insert (std::make_pair (layer->id (), LayerAction (true, layer)));
     246             :                 }
     247          80 :         }
     248             : 
     249          20 :         void runOnDeactivate (std::shared_ptr<Layer> layer)
     250             :         {
     251          60 :                 std::lock_guard<std::mutex> lock (m_mutexOnDeactivate);
     252         122 :                 for (auto && f : m_onDeactivate[layer->id ()])
     253             :                 {
     254           2 :                         f ();
     255             :                 }
     256          20 :         }
     257             : 
     258             : 
     259          20 :         void globalDeactivate (ThreadSubject * cc, std::shared_ptr<Layer> layer)
     260             :         {
     261          40 :                 runOnDeactivate (layer);
     262             : 
     263          60 :                 std::lock_guard<std::mutex> lock (m_mutex);
     264         116 :                 for (auto & c : m_updates)
     265             :                 {
     266             :                         // caller itself has it already deactivated
     267          56 :                         if (cc == c.first) continue;
     268         180 :                         c.second.toActivate.insert (std::make_pair (layer->id (), LayerAction (false, layer)));
     269             :                 }
     270          20 :         }
     271             : 
     272             :         /**
     273             :          * @param cc requester of its updates
     274             :          *
     275             :          * @see globalActivate
     276             :          * @return all layers for that subject
     277             :          */
     278         128 :         LayerMap fetchGlobalActivation (ThreadSubject * cc)
     279             :         {
     280         384 :                 std::lock_guard<std::mutex> lock (m_mutex);
     281         128 :                 LayerMap ret;
     282         384 :                 ret.swap (m_updates[cc].toActivate);
     283         128 :                 return ret;
     284             :         }
     285             : 
     286             :         /// stores per context updates not yet delievered
     287             :         /// nullptr is for full history to be copied to new contexts
     288             :         std::unordered_map<ThreadSubject *, PerContext> m_updates;
     289             :         /// mutex protecting m_updates
     290             :         std::mutex m_mutex;
     291             :         FunctionMap m_onActivate;
     292             :         std::mutex m_mutexOnActivate;
     293             :         FunctionMap m_onDeactivate;
     294             :         std::mutex m_mutexOnDeactivate;
     295             : };
     296             : 
     297          54 : class ThreadContext : public ThreadSubject, public Context
     298             : {
     299             : public:
     300             :         typedef std::reference_wrapper<ValueSubject> ValueRef;
     301             : 
     302         228 :         explicit ThreadContext (Coordinator & gc) : m_gc (gc)
     303             :         {
     304          76 :                 m_gc.attach (this);
     305          76 :         }
     306             : 
     307          94 :         ~ThreadContext ()
     308         282 :         {
     309          94 :                 m_gc.detach (this);
     310             : #if DEBUG
     311         282 :                 for (auto & i : m_keys)
     312             :                 {
     313           0 :                         std::cout << "threadcontext " << this << " left over: " << i.first << std::endl;
     314             :                 }
     315             : #endif
     316          94 :         }
     317             : 
     318             :         Coordinator & global ()
     319             :         {
     320             :                 return m_gc;
     321             :         }
     322             : 
     323             :         Coordinator & g ()
     324             :         {
     325             :                 return m_gc;
     326             :         }
     327             : 
     328             :         template <typename T, typename... Args>
     329          44 :         std::shared_ptr<Layer> activate (Args &&... args)
     330             :         {
     331          44 :                 syncLayers ();
     332          44 :                 std::shared_ptr<Layer> layer = Context::activate<T> (std::forward<Args> (args)...);
     333          88 :                 m_gc.globalActivate (this, layer);
     334          44 :                 return layer;
     335             :         }
     336             : 
     337           8 :         std::shared_ptr<Layer> activate (std::string key, std::string value)
     338             :         {
     339           8 :                 syncLayers ();
     340          32 :                 std::shared_ptr<Layer> layer = Context::activate (key, value);
     341          16 :                 m_gc.globalActivate (this, layer);
     342           8 :                 return layer;
     343             :         }
     344             : 
     345          28 :         std::shared_ptr<Layer> activate (Wrapped const & value)
     346             :         {
     347          28 :                 syncLayers ();
     348          28 :                 std::shared_ptr<Layer> layer = Context::activate (value);
     349          56 :                 m_gc.globalActivate (this, layer);
     350          28 :                 return layer;
     351             :         }
     352             : 
     353             : 
     354             :         template <typename T, typename... Args>
     355          16 :         std::shared_ptr<Layer> deactivate (Args &&... args)
     356             :         {
     357          16 :                 syncLayers ();
     358          16 :                 std::shared_ptr<Layer> layer = Context::deactivate<T> (std::forward<Args> (args)...);
     359          32 :                 m_gc.globalDeactivate (this, layer);
     360          16 :                 return layer;
     361             :         }
     362             : 
     363             :         std::shared_ptr<Layer> deactivate (std::string key, std::string value)
     364             :         {
     365             :                 syncLayers ();
     366             :                 std::shared_ptr<Layer> layer = Context::deactivate (key, value);
     367             :                 m_gc.globalDeactivate (this, layer);
     368             :                 return layer;
     369             :         }
     370             : 
     371           4 :         std::shared_ptr<Layer> deactivate (Wrapped const & value)
     372             :         {
     373           4 :                 syncLayers ();
     374           4 :                 std::shared_ptr<Layer> layer = Context::deactivate (value);
     375           8 :                 m_gc.globalDeactivate (this, layer);
     376           4 :                 return layer;
     377             :         }
     378             : 
     379         128 :         void syncLayers () override
     380             :         {
     381             :                 // now activate/deactive layers
     382         256 :                 Events e;
     383         528 :                 for (auto const & l : m_gc.fetchGlobalActivation (this))
     384             :                 {
     385          16 :                         if (l.second.activate)
     386             :                         {
     387          14 :                                 activateLayer (l.second.layer);
     388             :                         }
     389             :                         else
     390             :                         {
     391           2 :                                 deactivateLayer (l.second.layer);
     392             :                         }
     393          16 :                         e.push_back (l.first);
     394             :                 }
     395         128 :                 notifyByEvents (e);
     396             : 
     397             :                 // pull in assignments from other threads
     398         128 :                 m_gc.updateNewlyAssignedValues (this);
     399         128 :         }
     400             : 
     401           0 :         virtual void sync ()
     402             :         {
     403          12 :                 syncLayers ();
     404          12 :                 notifyKeySetUpdate ();
     405           0 :         }
     406             : 
     407             :         /**
     408             :          * @brief Command dispatching
     409             :          *
     410             :          * @param c the command to execute
     411             :          */
     412         540 :         void execute (Command & c) override
     413             :         {
     414         540 :                 m_gc.execute (c);
     415        1080 :                 if (c.oldKey != c.newKey)
     416             :                 {
     417         908 :                         if (!c.oldKey.empty ())
     418             :                         {
     419         336 :                                 m_keys.erase (c.oldKey);
     420             :                         }
     421         908 :                         if (!c.newKey.empty ())
     422             :                         {
     423        1680 :                                 m_keys.insert (std::make_pair (c.newKey, ValueRef (c.v)));
     424             :                         }
     425             :                 }
     426         540 :         }
     427             : 
     428             :         /**
     429             :          * @brief notify all keys
     430             :          *
     431             :          * Locked during execution, safe to use ks
     432             :          *
     433             :          * @param ks
     434             :          */
     435          28 :         void notify (KeySet & ks) override
     436             :         {
     437         148 :                 for (auto const & k : ks)
     438             :                 {
     439          96 :                         auto const & f = m_keys.find (k.getName ());
     440          64 :                         if (f == m_keys.end ()) continue; // key already had context change
     441          60 :                         f->second.get ().notifyInThread ();
     442             :                 }
     443          28 :         }
     444             : 
     445             : private:
     446             :         Coordinator & m_gc;
     447             :         /**
     448             :          * @brief A map of values this ThreadContext is responsible for.
     449             :          */
     450             :         std::unordered_map<std::string, ValueRef> m_keys;
     451             : };
     452             : 
     453             : template <typename T, typename PolicySetter1 = DefaultPolicyArgs, typename PolicySetter2 = DefaultPolicyArgs,
     454             :           typename PolicySetter3 = DefaultPolicyArgs, typename PolicySetter4 = DefaultPolicyArgs,
     455             :           typename PolicySetter5 = DefaultPolicyArgs>
     456             : using ThreadValue = Value<T, ContextPolicyIs<ThreadContext>, PolicySetter1, PolicySetter2, PolicySetter3, PolicySetter4, PolicySetter5>;
     457             : 
     458             : typedef ThreadValue<uint32_t> ThreadInteger;
     459             : typedef ThreadValue<bool> ThreadBoolean;
     460             : typedef ThreadValue<std::string> ThreadString;
     461             : } // namespace kdb
     462             : 
     463             : #endif

Generated by: LCOV version 1.13