Falcon
Loading...
Searching...
No Matches
sharedstate.hpp
1// ---------------------------------------------------------------------
2// This file is part of falcon-core.
3//
4// Copyright (C) 2015, 2016, 2017 Neuro-Electronics Research Flanders
5//
6// Falcon-server is free software: you can redistribute it and/or modify
7// it under the terms of the GNU General Public License as published by
8// the Free Software Foundation, either version 3 of the License, or
9// (at your option) any later version.
10//
11// Falcon-server is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14// GNU General Public License for more details.
15//
16// You should have received a copy of the GNU General Public License
17// along with falcon-core. If not, see <http://www.gnu.org/licenses/>.
18// ---------------------------------------------------------------------
19
20#pragma once
21
22#include <algorithm>
23#include <atomic>
24#include <iostream>
25#include <map>
26#include <memory>
27#include <set>
28#include <sstream>
29#include <string>
30#include <typeinfo>
31#include <vector>
32
33#include "logging/log.hpp"
34#include "yaml-cpp/yaml.h"
35
36// a state wraps a value that is possibly shared between threads
37// access to the value should be protected (atomic or lock)
38// a state has a non-shared cached value that is synchronized with every set/get
39// operation (optional) on the state the cache is useful for checking if the
40// value was changed outside of the state since the last set/get operation
41
42// TODO: take state retrieval/update out of IProcessor and make it
43// responsibility of ProcessorGraph IProcessors should only be allowed to
44// set/get state value (and not change permissions, (un)share, etc.)
45// ProcessorGraph is responsible for moderating external access (including
46// permission check) ProcessorGraph is responsible for (un)linking states
47
48// ProcessorGraph owns a LinkedStateMap that manages LinkedStateGroups
49// shared states are linked to each other (managed by a LinkedStateGroup)
50// processor.state can be added/removed (by name) to/from LinkedStateGroup
51
52// ProcessorGraph::Update/Retrieve
53// global-state: value
54// processor: {state: value}
55
56enum class Permission : std::uint8_t {
57 NONE = 0,
58 READ,
59 WRITE
60}; // in order of least permissive to most permissive
61
62Permission permission_from_string(std::string s);
63std::string permission_to_string(Permission p, bool shorthand = false);
64
65class ExternalPermissionTracker {
66 public:
67 ExternalPermissionTracker() : none_(0), read_(0), write_(0) {}
68 ExternalPermissionTracker(Permission permission) : ExternalPermissionTracker() {
69 add(permission);
70 }
71
72 Permission permission() {
73 if (none_.load() > 0) {
74 return Permission::NONE;
75 }
76 if (read_.load() > 0) {
77 return Permission::READ;
78 }
79 if (write_.load() > 0) {
80 return Permission::WRITE;
81 }
82 return Permission::NONE;
83 }
84
85 void add(Permission permission) {
86 if (permission == Permission::NONE) {
87 ++none_;
88 } else if (permission == Permission::READ) {
89 ++read_;
90 } else if (permission == Permission::WRITE) {
91 ++write_;
92 }
93 }
94
95 void subtract(Permission permission) {
96 if (permission == Permission::NONE) {
97 --none_;
98 } else if (permission == Permission::READ) {
99 --read_;
100 } else if (permission == Permission::WRITE) {
101 --write_;
102 }
103 }
104
105 protected:
106 std::atomic<int> none_;
107 std::atomic<int> read_;
108 std::atomic<int> write_;
109};
110
111class Permissions {
112 public:
113 Permissions(Permission self = Permission::WRITE, Permission others = Permission::READ,
114 Permission external = Permission::NONE);
115
116 Permission self() const;
117 Permission others() const;
118 Permission external() const;
119
120 void set_self(const Permission p);
121 void set_others(const Permission p);
122 void set_external(const Permission p);
123
124 std::string to_string(bool shorthand = true) const;
125 bool IsCompatible(const Permissions& p);
126
127 protected:
128 Permission self_;
129 Permission others_;
130 Permission external_;
131};
132
133class SharedStateAlias;
134namespace graph {
135class ProcessorGraph;
136}
137
138class IState {
139 friend class SharedStateAlias;
140 friend class graph::ProcessorGraph;
141
142 public:
143 IState(const Permissions& permissions, const std::string& description = "");
144 IState(const IState& other);
145 virtual ~IState() {}
146 virtual IState* clone() const = 0;
147
148 bool IsCompatible(const Permissions& permissions);
149 const Permissions& permissions() const;
150 Permission external_permission();
151
152 std::string description();
153 virtual std::string get_string(bool cache = true) = 0;
154
155 protected: // for friends only
156 virtual bool IsLikeMe(const std::shared_ptr<IState>& other) = 0;
157
158 virtual void Share(const std::shared_ptr<IState>& other) = 0;
159 virtual void UnShare() = 0;
160
161 virtual bool IsShared();
162
163 void set_description(const std::string& value);
164
165 virtual bool set_string(const std::string& value, bool cache = true) = 0;
166
167 void set_external_permission(Permission permission);
168
169 protected:
170 void lock();
171 void unlock();
172
173 protected:
174 Permissions permissions_;
175 std::string description_;
176 bool shared_;
177 std::shared_ptr<ExternalPermissionTracker> external_permission_;
178
179 private:
180 std::atomic_flag lock_ = ATOMIC_FLAG_INIT;
181};
182
183template <typename Base, typename Derived>
184class StateCloneable : public Base {
185 public:
186 using Base::Base;
187
188 virtual Base* clone() const { return new Derived(static_cast<Derived const&>(*this)); }
189};
190
191template <typename T>
192class ReadableState : public StateCloneable<IState, ReadableState<T>> {
193 public:
194 ReadableState(T default_value, std::string description = "",
195 Permission peers = Permission::WRITE, Permission external = Permission::NONE)
196 : StateCloneable<IState, ReadableState<T>>(Permissions(Permission::READ, peers, external),
197 description),
198 default_(default_value),
199 cache_(default_value),
200 state_(std::make_shared<std::atomic<T>>(default_value)) {}
201
202 ReadableState(const ReadableState& other)
203 : StateCloneable<IState, ReadableState<T>>(other.permissions_, other.description_),
204 default_(other.default_),
205 cache_(other.cache_),
206 state_(std::make_shared<std::atomic<T>>(other.state_->load())) {
207 // note that we are creating our own (unshared) state
208 // and that we do not share other's state
209 }
210
211 T get(bool cache = true) {
212 T val;
213 this->lock();
214 val = state_->load();
215 if (cache) {
216 cache_ = val;
217 }
218 this->unlock();
219
220 return val;
221 }
222
223 bool changed_get(T& val, bool cache = true) {
224 bool ret;
225 this->lock();
226 val = state_->load();
227 ret = cache_ == val;
228 if (cache) {
229 cache_ = val;
230 }
231 this->unlock();
232
233 return ret;
234 }
235
236 std::string get_string(bool cache = true) override {
237 if constexpr (std::is_convertible_v<T, std::string>) {
238 T value = get(cache);
239 if (std::is_same_v<T, bool>) {
240 return value ? "true" : "false";
241 }
242 return std::to_string(value);
243 }
244 throw std::runtime_error(
245 "This state should not have external permission to be read "
246 "because it cannot be serialized via string transformation.");
247 }
248
249 protected: // for friends only
250 void set(T value, bool cache = true) {
251 this->lock();
252 state_->store(value);
253 if (cache) {
254 cache_ = value;
255 }
256 this->unlock();
257 }
258
259 T exchange(T value, bool cache = true) {
260 this->lock();
261 value = state_->exchange(value);
262 if (cache) {
263 cache_ = value;
264 }
265 this->unlock();
266
267 return value;
268 }
269
270 bool set_string(const std::string& value, bool cache = true) override {
271 if constexpr (std::is_convertible_v<T, std::string>) {
272 std::stringstream ss(value);
273 T result;
274 if ((std::is_same_v<T, bool> and ss >> std::boolalpha >> result) or ss >> result) {
275 set(result, cache);
276 return true;
277 }
278 }
279 return false;
280 }
281
282 void reset() { set(default_); }
283
284 void Share(const std::shared_ptr<IState>& other) override {
285 if (other.get() == this) {
286 return;
287 }
288
289 auto cast = dynamic_cast<ReadableState<T>*>(other.get());
290 if (cast) {
291 this->lock();
292 this->state_ = cast->state_;
293 this->external_permission_ = cast->external_permission_;
294 this->external_permission_->add(this->permissions_.external());
295 this->shared_ = true;
296 this->unlock();
297 } else {
298 throw std::runtime_error("Cannot delegate to incompatible state.");
299 }
300 }
301
302 void UnShare() override {
303 this->lock();
304 this->state_ = std::make_shared<std::atomic<T>>(this->state_->load());
305 this->external_permission_->subtract(this->permissions_.external());
306 this->external_permission_ =
307 std::make_shared<ExternalPermissionTracker>(this->permissions_.external());
308 this->shared_ = false;
309 this->unlock();
310 }
311
312 bool IsLikeMe(const std::shared_ptr<IState>& other) override {
313 try {
314 auto cast = dynamic_cast<const ReadableState<T>*>(other.get());
315 if (cast) {
316 return true;
317 } else {
318 return false;
319 }
320 } catch (const std::bad_cast& e) {
321 return false;
322 }
323 }
324
325 private:
326 T default_;
327 T cache_;
328 std::shared_ptr<std::atomic<T>> state_; // our own state, that may be shared with others
329};
330
331template <typename T>
332class WritableState : public ReadableState<T> {
333 public:
334 WritableState(T default_value, std::string description = "",
335 Permission peers = Permission::READ, Permission external = Permission::NONE)
336 : ReadableState<T>(default_value, description, peers, external) {
337 this->permissions_.set_self(Permission::WRITE);
338 }
339
340 // make set methods publicly available
341 void set(T value, bool cache = true) { ReadableState<T>::set(value, cache); }
342 T exchange(T value, bool cache = true) { return ReadableState<T>::exchange(value, cache); }
343 bool set_string(const std::string& value, bool cache = true) override {
344 return ReadableState<T>::set_string(value, cache);
345 }
346 void reset() { ReadableState<T>::reset(); }
347};
348
349class SharedStateAlias {
350 public:
351 SharedStateAlias(Permission external = Permission::WRITE, const std::string& description = "");
352 ~SharedStateAlias();
353 void AddState(const std::string& name, const std::shared_ptr<IState>& dependent);
354 void RemoveState(const std::string& name);
355 void RemoveAllStates();
356 bool Update(const std::string& value);
357 std::string Retrieve();
358 YAML::Node ExportYAML();
359
360 private:
361 Permission external_;
362 std::string description_;
363 std::shared_ptr<IState> master_;
364 std::map<std::string, std::shared_ptr<IState>> dependents_;
365};
366
367template <typename T>
368using StaticState = ReadableState<T>;
369template <typename T>
370using FollowerState = ReadableState<T>;
371template <typename T>
372using ProducerState = WritableState<T>;
373template <typename T>
374using BroadcasterState = WritableState<T>;
375
376class SharedStateMap {
377 public:
378 SharedStateMap() {}
379 ~SharedStateMap();
380 void AddAlias(const std::string& alias, Permission permission = Permission::WRITE,
381 const std::string& description = "");
382 void RemoveAlias(const std::string& alias);
383 void ShareState(const std::string& alias, const std::string& name,
384 const std::shared_ptr<IState>& state);
385 void UnShareState(const std::string& name);
386 void UnShareAll();
387 void clear();
388 bool IsShared(const std::string& name);
389 std::vector<std::string> ListSharedStates(const std::string& alias);
390 bool UpdateAlias(const std::string& alias, const std::string& value);
391 std::string RetrieveAlias(const std::string& alias);
392 YAML::Node ExportYAML();
393
394 protected:
395 std::map<std::string, SharedStateAlias> aliases_;
396 std::map<std::string, std::string> shared_states_;
397};
Definition sharedstate.hpp:111
Definition sharedstate.hpp:192
Definition sharedstate.hpp:349
Definition sharedstate.hpp:184
Definition sharedstate.hpp:332
Definition processorgraph.hpp:62