-
Notifications
You must be signed in to change notification settings - Fork 3
/
SynchrotronComponent.hpp
361 lines (315 loc) · 11 KB
/
SynchrotronComponent.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
/**
* Synchronous component with combined Signals and Slots functionality.
* Loosely based on https://github.com/winglot/Signals
*/
#ifndef SYNCHROTRONCOMPONENT_HPP
#define SYNCHROTRONCOMPONENT_HPP
#include <iostream> // For testing for now
#include <bitset>
#include <set>
#include <initializer_list>
#include <mutex>
namespace Synchrotron {
/** \brief Mutex class to lock the current working thread.
*
* Includes a `static size_t` with an increment when a new instance is created.
* This is used in a custom compare method `Mutex::compare`.
*/
class Mutex {
protected:
static size_t mutex_id;
private:
const size_t idx;
std::mutex m_mutex;
public:
Mutex() : idx(mutex_id++) {}
Mutex(const Mutex&) : Mutex() {}
virtual ~Mutex() {}
virtual void lock() { m_mutex.lock(); }
virtual void unlock() { m_mutex.unlock(); }
struct compare {
inline bool operator() (const Mutex* lhs, const Mutex* rhs) const {
return lhs->idx < rhs->idx;
}
};
};
/** \brief Default `Mutex::mutex_id` to 0.
*/
size_t Mutex::mutex_id = 0;
/** \brief
* Creating a new LockBlock(this) locks the current thread,
* while leaving the scope conveniently unlocks the thread.
*/
class LockBlock {
public:
Mutex *m_mutex;
LockBlock(Mutex *mtx)
: m_mutex(mtx) { m_mutex->lock(); }
~LockBlock() { m_mutex->unlock(); }
};
/** \brief
* SynchrotronComponent is the base for all components,
* offering in and output connections to other SynchrotronComponent.
*
* \tparam bit_width
* This template argument specifies the width of the internal bitset state.
*/
template <size_t bit_width>
class SynchrotronComponent : public Mutex {
protected:
/** \brief
* The current internal state of bits in this component (default output).
*/
std::bitset<bit_width> state;
private:
/** \brief
* **Slots == outputs**
*
* Emit this.signal to subscribers in slotOutput.
*/
std::set<SynchrotronComponent*, Mutex::compare> slotOutput;
/** \brief
* **Signals == inputs**
*
* Receive tick()s from these subscriptions in signalInput.
*/
std::set<SynchrotronComponent*, Mutex::compare> signalInput;
/** \brief Connect a new slot s:
* * Add s to this SynchrotronComponent's outputs.
* * Add this to s's inputs.
*
* \param s
* The SynchrotronComponent to connect.
*/
inline void connectSlot(SynchrotronComponent* s) {
//LockBlock lock(this);
this->slotOutput.insert(s);
s->signalInput.insert(this);
}
/** \brief Disconnect a slot s:
* * Remove s from this SynchrotronComponent's outputs.
* * Remove this from s's inputs.
*
* \param s
* The SynchrotronComponent to disconnect.
*/
inline void disconnectSlot(SynchrotronComponent* s) {
//LockBlock lock(this);
this->slotOutput.erase(s);
s->signalInput.erase(this);
}
public:
/** \brief Default constructor
*
* \param initial_value
* The initial state of the internal bitset.
*/
SynchrotronComponent(size_t initial_value = 0) : state(initial_value) {}
/** \brief **[Thread safe]**
* Copy constructor
* * Duplicates signal subscriptions (inputs)
* * Optionally also duplicates slot connections (outputs)
*
* \param sc const
* The other SynchrotronComponent to duplicate the connections from.
* \param duplicateAll_IO
* Specifies whether to only copy inputs (false) or outputs as well (true).
*/
SynchrotronComponent(const SynchrotronComponent& sc, bool duplicateAll_IO = false) : SynchrotronComponent() {
//LockBlock lock(this);
// Copy subscriptions
for(auto& sender : sc.signalInput) {
this->addInput(*sender);
}
if (duplicateAll_IO) {
// Copy subscribers
for(auto& connection : sc.slotOutput) {
this->addOutput(*connection);
}
}
}
/** \brief
* Connection constructor
* * Adds signal subscriptions from inputList
* * Optionally adds slot subscribers from outputList
*
* \param inputList
* The list of SynchrotronComponents to connect as input.
* \param outputList
* The list of SynchrotronComponents to connect as output..
*/
SynchrotronComponent(std::initializer_list<SynchrotronComponent*> inputList,
std::initializer_list<SynchrotronComponent*> outputList = {} )
: SynchrotronComponent() {
this->addInput(inputList);
this->addOutput(outputList);
}
/** \brief **[Thread safe]** Default destructor
*
* When called, will disconnect all in and output connections to this SynchrotronComponent.
*/
~SynchrotronComponent() {
LockBlock lock(this);
// Disconnect all Slots
for(auto& connection : this->slotOutput) {
connection->signalInput.erase(this);
//delete connection; //?
}
// Disconnect all Signals
for(auto &sender: this->signalInput) {
sender->slotOutput.erase(this);
}
this->slotOutput.clear();
this->signalInput.clear();
}
/** \brief Gets this SynchrotronComponent's bit width.
*
* \return size_t
* Returns the bit width of the internal bitset.
*/
size_t getBitWidth() const {
return bit_width;
}
// /* No real use since function cannot be called with different size SynchrotronComponents */
// /* Maybe viable when SynchrotronComponent has different in and output sizes */
// /* \brief Compare this bit width to that of other.
// *
// * \param other
// * The other SynchrotronComponent to check.
// *
// * \return bool
// * Returns whether the widths match.
// */
// inline bool hasSameWidth(SynchrotronComponent& other) {
// return this->getBitWidth() == other.getBitWidth();
// }
/** \brief Gets this SynchrotronComponent's state.
*
* \return std::bitset<bit_width>
* Returns the internal bitset.
*/
inline std::bitset<bit_width> getState() const {
return this->state;
}
/** \brief Gets the SynchrotronComponent's input connections.
*
* \return std::set<SynchrotronComponent*>&
* Returns a reference set to this SynchrotronComponent's inputs.
*/
const std::set<SynchrotronComponent*, Mutex::compare>& getInputs() const {
return this->signalInput;
}
/** \brief Gets the SynchrotronComponent's output connections.
*
* \return std::set<SynchrotronComponent*>&
* Returns a reference set to this SynchrotronComponent's outputs.
*/
const std::set<SynchrotronComponent*, Mutex::compare>& getOutputs() const {
return this->slotOutput;
}
/** \brief **[Thread safe]** Adds/Connects a new input to this SynchrotronComponent.
*
* **Ensures both way connection will be made:**
* This will have input added to its inputs and input will have this added to its outputs.
*
* \param input
* The SynchrotronComponent to connect as input.
*/
virtual void addInput(SynchrotronComponent& input) {
LockBlock lock(this);
// deprecated? //if (!this->hasSameWidth(input)) return false;
input.connectSlot(this);
}
/** \brief Adds/Connects a list of new inputs to this SynchrotronComponent.
*
* Calls addInput() on each SynchrotronComponent* in inputList.
*
* \param inputList
* The list of SynchrotronComponents to connect as input.
*/
void addInput(std::initializer_list<SynchrotronComponent*> inputList) {
for(auto connection : inputList)
this->addInput(*connection);
}
/** \brief **[Thread safe]** Removes/Disconnects an input to this SynchrotronComponent.
*
* **Ensures both way connection will be removed:**
* This will have input removed from its inputs and input will have this removed from its outputs.
*
* \param input
* The SynchrotronComponent to disconnect as input.
*/
void removeInput(SynchrotronComponent& input) {
LockBlock lock(this);
input.disconnectSlot(this);
}
/** \brief **[Thread safe]** Adds/Connects a new output to this SynchrotronComponent.
*
* **Ensures both way connection will be made:**
* This will have output added to its outputs and output will have this added to its inputs.
*
* \param output
* The SynchrotronComponent to connect as output.
*/
void addOutput(SynchrotronComponent& output) {
LockBlock lock(this);
// deprecated? //if (!this->hasSameWidth(*output)) return false;
this->connectSlot(&output);
}
/** \brief Adds/Connects a list of new outputs to this SynchrotronComponent.
*
* Calls addOutput() on each SynchrotronComponent* in outputList.
*
* \param outputList
* The list of SynchrotronComponents to connect as output.
*/
void addOutput(std::initializer_list<SynchrotronComponent*> outputList) {
for(auto connection : outputList)
this->addOutput(*connection);
}
/** \brief **[Thread safe]** Removes/Disconnects an output to this SynchrotronComponent.
*
* **Ensures both way connection will be removed:**
* This will have output removed from its output and output will have this removed from its inputs.
*
* \param output
* The SynchrotronComponent to disconnect as output.
*/
void removeOutput(SynchrotronComponent& output) {
LockBlock lock(this);
this->disconnectSlot(&output);
}
/** \brief The tick() method will be called when one of this SynchrotronComponent's inputs issues an emit().
*
* \return virtual void
* This method should be implemented by a derived class.
*/
virtual void tick() {
//LockBlock lock(this);
std::bitset<bit_width> prevState = this->state;
//std::cout << "Ticked\n";
for(auto& connection : this->signalInput) {
// Change this line to change the logic applied on the states:
this->state |= ((SynchrotronComponent*) connection)->getState();
}
// Directly emit changes to subscribers on change
if (prevState != this->state)
this->emit();
}
/** \brief The emit() method will be called after a tick() completes to ensure the flow of new data.
*
* Loops over all outputs and calls tick().
*
* \return virtual void
* This method can be re-implemented by a derived class.
*/
virtual inline void emit() {
//LockBlock lock(this);
for(auto& connection : this->slotOutput) {
connection->tick();
}
//std::cout << "Emitted\n";
}
};
}
#endif // SYNCHROTRONCOMPONENT_HPP