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
|