Open Model Railroad Network (OpenMRN)
Loading...
Searching...
No Matches
AliasAllocator.cxx
Go to the documentation of this file.
1
36#include "nmranet_config.h"
37#include "openlcb/CanDefs.hxx"
38
39namespace openlcb
40{
41
43
45 : StateFlow<Buffer<AliasInfo>, QList<1>>(if_can)
46 , conflictHandler_(this)
47 , timer_(this)
48 , if_id_(if_id)
49 , cid_frame_sequence_(0)
50 , conflict_detected_(0)
51 , reserveUnusedAliases_(config_reserve_unused_alias_count())
52{
54 // Moves all the allocated alias buffers over to the input queue for
55 // allocation.
56}
57
59{
60 seed_ = if_id_ >> 30;
61 seed_ ^= if_id_ >> 18;
62 seed_ ^= if_id_ >> 6;
63 seed_ ^= uint16_t(if_id_ >> 42) | uint16_t(if_id_ << 6);
64}
65
73void seed_alias_allocator(AliasAllocator* aliases, Pool* pool, int n) {
74 for (int i = 0; i < n; i++)
75 {
77 pool->alloc(&b);
78 aliases->send(b);
79 }
80}
81
85{
86 unsigned cnt = 0;
88 NodeAlias found_alias = 0;
89 do
90 {
91 if (if_can()->local_aliases()->next_entry(
92 found_id, &found_id, &found_alias) &&
94 {
95 ++cnt;
96 }
97 else
98 {
99 break;
100 }
101 } while (true);
102 return cnt;
103}
104
107{
108 do
109 {
111 NodeAlias found_alias = 0;
112 if (if_can()->local_aliases()->next_entry(
114 &found_alias) &&
116 {
117 if_can()->local_aliases()->remove(found_alias);
118 }
119 else
120 {
121 break;
122 }
123 } while (true);
124}
125
127{
128 // This is synchronous allocation, which is not nice.
129 {
130 auto *b = if_can()->frame_write_flow()->alloc();
131 struct can_frame *f = b->data()->mutable_frame();
133 f->can_dlc = 6;
134 node_id_to_data(id, f->data);
136 }
137
138 add_allocated_alias(alias);
139}
140
142{
143 // Note: We leak aliases here in case of eviction by the AliasCache
144 // object. This is okay for two reasons: 1) Generally the local alias cache
145 // size should be about equal to the local nodes count. 2) OpenLCB alias
146 // allocation algorithm is able to reuse aliases that were allocated by
147 // nodes that are not on the network anymore.
150 if (!waitingClients_.empty())
151 {
152 // Wakes up exactly one executable that is waiting for an alias.
153 Executable *w = static_cast<Executable *>(waitingClients_.next().item);
154 // This schedules a state flow onto its executor.
155 w->alloc_result(nullptr);
156 }
157}
158
160 NodeID destination_id, Executable *done)
161{
162 NodeID found_id;
163 NodeAlias found_alias = 0;
164 bool allocate_new = false;
165 bool found = if_can()->local_aliases()->next_entry(
166 CanDefs::get_reserved_alias_node_id(0), &found_id, &found_alias);
167 if (found)
168 {
169 found = (found_id == CanDefs::get_reserved_alias_node_id(found_alias));
170 }
171 if (found)
172 {
173 if_can()->local_aliases()->add(destination_id, found_alias);
175 {
176 NodeID next_id;
177 NodeAlias next_alias = 0;
178 if (!if_can()->local_aliases()->next_entry(
180 &next_alias) ||
182 {
183 allocate_new = true;
184 }
185 }
186 }
187 else
188 {
189 found_alias = 0;
190 allocate_new = true;
192 }
193 if (allocate_new)
194 {
196 b->data()->do_not_reallocate();
197 this->send(b);
198 }
199 return found_alias;
200}
201
205
207{
210 HASSERT(pending_alias()->state == AliasInfo::STATE_EMPTY);
211 while (!pending_alias()->alias)
212 {
213 pending_alias()->alias = get_new_seed();
214 }
215 // Registers ourselves as a handler for incoming CAN frames to detect
216 // conflicts.
218 &conflictHandler_, pending_alias()->alias, ~0x1FFFF000U);
219
220 // Grabs an outgoing frame buffer.
221 return call_immediately(STATE(handle_allocate_for_cid_frame));
222}
223
225{
226 while (true)
227 {
228 NodeAlias ret = seed_;
229 next_seed();
230 if (!ret)
231 {
232 continue;
233 }
234 LOG(VERBOSE, "(%p) alias test seed is %03X (next %03X)", this, ret,
235 seed_);
236 if (if_can()->local_aliases()->lookup(ret))
237 {
238 continue;
239 }
240 if (if_can()->remote_aliases()->lookup(ret))
241 {
242 continue;
243 }
244 LOG(VERBOSE, "alias get seed is %03X (next %03X)", ret, seed_);
245 return ret;
246 }
247}
248
250{
251 uint16_t offset;
252 offset = if_id_ >> 36;
253 offset ^= if_id_ >> 24;
254 offset ^= if_id_ >> 12;
255 offset ^= if_id_;
256 offset <<= 1;
257 offset |= 1; // ensures offset is odd.
258 // This offset will be guaranteed different for any two node IDs that
259 // are within 2048 of each other. Therefore it is guaranteed that they
260 // will generate a different alias after a conflict. It is also
261 // guaranteed that we will generate every single possible alias before
262 // generating a duplicate (the cycle length is always 2^12).
263
264 seed_ += offset;
265}
266
267StateFlowBase::Action AliasAllocator::handle_allocate_for_cid_frame()
268{
269 if (cid_frame_sequence_ >= 4)
270 {
271 return allocate_and_call(if_can()->frame_write_flow(),
272 STATE(send_cid_frame));
273 }
274 else
275 {
276 // All CID frames are sent, let's wait.
277 return sleep_and_call(&timer_, MSEC_TO_NSEC(200), STATE(wait_done));
278 }
279}
280
281StateFlowBase::Action AliasAllocator::send_cid_frame()
282{
283 LOG(VERBOSE, "Sending CID frame %d for alias %03x", cid_frame_sequence_,
284 pending_alias()->alias);
285 auto *b = get_allocation_result(if_can()->frame_write_flow());
286 struct can_frame *f = b->data()->mutable_frame();
288 {
289 b->unref();
290 return call_immediately(STATE(handle_alias_conflict));
291 }
292 CanDefs::control_init(*f, pending_alias()->alias,
293 (if_id_ >> (12 * (cid_frame_sequence_ - 4))) & 0xfff,
295 b->set_done(n_.reset(this));
298 return wait_and_call(STATE(handle_allocate_for_cid_frame));
299}
300
301StateFlowBase::Action AliasAllocator::handle_alias_conflict()
302{
303 // Marks that we are no longer interested in frames from this alias.
305 &conflictHandler_, pending_alias()->alias, ~0x1FFFF000U);
306
307 // Burns up the alias.
308 pending_alias()->alias = 0;
309 pending_alias()->state = AliasInfo::STATE_EMPTY;
310 // Restarts the lookup.
311 return call_immediately(STATE(entry));
312}
313
314StateFlowBase::Action AliasAllocator::wait_done()
315{
317 {
318 return call_immediately(STATE(handle_alias_conflict));
319 }
320 // grab a frame buffer for the RID frame.
321 return allocate_and_call(if_can()->frame_write_flow(),
322 STATE(send_rid_frame));
323}
324
325StateFlowBase::Action AliasAllocator::send_rid_frame()
326{
327 LOG(VERBOSE, "Sending RID frame for alias %03x", pending_alias()->alias);
328 auto *b = get_allocation_result(if_can()->frame_write_flow());
329 struct can_frame *f = b->data()->mutable_frame();
330 CanDefs::control_init(*f, pending_alias()->alias, CanDefs::RID_FRAME, 0);
332 {
333 b->unref();
334 return call_immediately(STATE(handle_alias_conflict));
335 }
337 // The alias is reserved, put it into the freelist.
338 pending_alias()->state = AliasInfo::STATE_RESERVED;
340 &conflictHandler_, pending_alias()->alias, ~0x1FFFF000U);
341 add_allocated_alias(pending_alias()->alias);
342 return release_and_exit();
343}
344
346 unsigned priority)
347{
348 if (parent_->conflict_detected_) {
349 message->unref();
350 return;
351 }
352 parent_->conflict_detected_ = 1;
354 if (parent_->is_state(static_cast<StateFlowBase::Callback>(&AliasAllocator::wait_done))) {
355 /* Wakes up the actual flow to not have to wait all the 200 ms of
356 * sleep. This will request the timer callback to be issued
357 * immediately, which avoids race condition between the trigger and the
358 * regular timeout call. */
359 parent_->timer_.trigger();
360 }
361 message->unref();
362}
363
364#ifdef GTEST
365
366void AliasAllocator::TEST_finish_pending_allocation() {
367 if (is_state(STATE(wait_done))) {
368 timer_.trigger();
369 }
370}
371
372void AliasAllocator::TEST_add_allocated_alias(NodeAlias alias)
373{
374 add_allocated_alias(alias);
375}
376
377#endif
378
379} // namespace openlcb
#define STATE(_fn)
Turns a function name into an argument to be supplied to functions expecting a state.
Definition StateFlow.hxx:61
BarrierNotifiable * reset(Notifiable *done)
Resets the barrier. Returns &*this. Asserts that is_done().
Base class for all QMember types that hold data in an expandable format.
Definition Buffer.hxx:195
T * data()
get a pointer to the start of the data.
Definition Buffer.hxx:215
FrameDispatchFlow * frame_dispatcher()
Definition CanIf.hxx:208
OutgoingFrameHandler * frame_write_flow()
Definition CanIf.hxx:214
void register_handler(HandlerType *handler, ID id, ID mask)
Adds a new handler to this dispatcher.
void unregister_handler(HandlerType *handler, ID id, ID mask)
Removes a specific instance of a handler from this dispatcher.
An object that can be scheduled on an executor to run.
virtual void alloc_result(QMember *item)
Return the result of an alloc_async() from a memory Pool.
virtual void send(MessageType *message, unsigned priority=UINT_MAX)=0
Entry point to the flow.
MessageType * alloc()
Synchronously allocates a message buffer from the pool of this flow.
Pool of previously allocated, but currently unused, items.
Definition Buffer.hxx:278
void alloc(Buffer< BufferType > **result, Executable *flow=NULL)
Get a free item out of the pool.
Definition Buffer.hxx:292
A list of queues.
Definition Queue.hxx:466
void insert(QMember *item, unsigned index=0)
Add an item to the back of the queue.
Definition Queue.hxx:124
bool empty(unsigned index)
Test if the queue is empty.
Definition Queue.hxx:225
QMember * next(unsigned index)
Get an item from the front of the queue.
Definition Queue.hxx:167
Return type for a state flow callback.
Action(StateFlowBase::* Callback)()
State Flow callback prototype.
State flow with a given typed input queue.
void send(MessageType *msg, unsigned priority=UINT_MAX) OVERRIDE
Sends a message to the state flow for processing.
MessageType * message()
void send(Buffer< CanMessageData > *message, unsigned priority) override
Entry point to the flow.
This state flow is responsible for reserving node ID aliases.
unsigned cid_frame_sequence_
Which CID frame are we trying to send out. Valid values: 7..4.
NodeID if_id_
48-bit nodeID that we will use for alias reservations.
void clear_reserved_aliases()
Removes all aliases that are reserved but not yet used.
unsigned conflict_detected_
Set to 1 if an incoming frame signals an alias conflict.
Q waitingClients_
Set of client flows that are waiting for allocating an alias.
unsigned reserveUnusedAliases_
How many unused aliases we should reserve.
AliasAllocator(NodeID if_id, IfCan *if_can)
Constructs a new AliasAllocator flow.
void return_alias(NodeID id, NodeAlias alias)
Releases a given alias.
BarrierNotifiable n_
Notifiable used for tracking outgoing frames.
void add_allocated_alias(NodeAlias alias)
Call from an alternate alias allocator.
NodeAlias get_new_seed()
Returns a new alias to check from the random sequence.
IfCan * if_can()
Physical interface for sending packets and assigning handlers to received packets.
unsigned seed_
Seed for generating random-looking alias numbers.
void reinit_seed()
Resets the alias allocator to the state it was at construction.
Action entry() override
Entry into the StateFlow activity.
NodeAlias get_allocated_alias(NodeID destination_id, Executable *done)
Allocates an alias from the reserved but unused aliases list.
void next_seed()
Generates the next alias to check in the seed_ variable.
virtual ~AliasAllocator()
Destructor.
bool next_entry(NodeID bound, NodeID *node, NodeAlias *alias)
Retrieves the next entry by increasing node ID.
void remove(NodeAlias alias)
Remove an alias from an alias cache.
void add(NodeID id, NodeAlias alias)
Add an alias to an alias cache.
Implementation of the OpenLCB interface abstraction for the CAN-bus interface standard.
Definition IfCan.hxx:65
AliasCache * local_aliases()
Definition IfCan.hxx:96
#define LOG(level, message...)
Conditionally write a message to the logging output.
Definition logging.h:99
static const int VERBOSE
Loglevel that is usually not printed, reporting debugging information.
Definition logging.h:59
#define HASSERT(x)
Checks that the value of expression x is true, else terminates the current process.
Definition macros.h:138
void seed_alias_allocator(AliasAllocator *aliases, Pool *pool, int n)
Helper function to instruct the async alias allocator to pre-allocate N aliases.
void node_id_to_data(NodeID id, void *data)
Convenience function to render a 48-bit NMRAnet node ID into an existing buffer.
Definition If.cxx:52
size_t g_alias_test_conflicts
Counts the number of aliases that were given up because a conflict has arisen during the allocation.
uint64_t NodeID
48-bit NMRAnet Node ID type
uint16_t NodeAlias
Alias to a 48-bit NMRAnet Node ID type.
#define MSEC_TO_NSEC(_msec)
Convert a millisecond value to a nanosecond value.
Definition os.h:268
Information we know locally about an NMRAnet CAN alias.
uint16_t alias
The current alias.
static NodeID get_reserved_alias_node_id(NodeAlias alias)
Computes a reserved alias node ID for the local alias cache map.
Definition CanDefs.hxx:389
static void control_init(struct can_frame &frame, NodeAlias src, uint16_t field, int sequence)
Initialize a control frame CAN ID and set DLC to 0.
Definition CanDefs.hxx:379
@ RID_FRAME
Reserve ID Frame.
Definition CanDefs.hxx:126
@ AMR_FRAME
Alias Map Reset.
Definition CanDefs.hxx:129
static bool is_reserved_alias_node_id(NodeID id)
Tests if a node ID is a reserved alias Node ID.
Definition CanDefs.hxx:397