Open Model Railroad Network (OpenMRN)
Loading...
Searching...
No Matches
Dispatcher.hxx
Go to the documentation of this file.
1
35#ifndef _EXECUTOR_DISPATCHER_HXX_
36#define _EXECUTOR_DISPATCHER_HXX_
37
38#include <vector>
39
42
50template <int NUM_PRIO>
51class DispatchFlowBase : public UntypedStateFlow<QList<NUM_PRIO>>
52{
53public:
58
62
64
66 size_t size();
67
68protected:
70 typedef uint32_t ID;
73 typedef void UntypedHandler;
87 void register_handler(UntypedHandler *handler, ID id, ID mask);
88
95 void unregister_handler(UntypedHandler *handler, ID id, ID mask);
96
100
105 {
107 fallbackHandler_ = handler;
108 }
109
111 virtual ID get_message_id() = 0;
112
117
120 virtual void send_transfer() = 0;
121
122 /*typedef typename StateFlow<MessageType, QList<NUM_PRIO>>::Callback Callback;
123 using StateFlow<MessageType, QList<NUM_PRIO>>::again;
124 using StateFlow<MessageType, QList<NUM_PRIO>>::allocate_and_call;
125 using StateFlow<MessageType, QList<NUM_PRIO>>::get_allocation_result;
126 using StateFlow<MessageType, QList<NUM_PRIO>>::message;
127 using StateFlow<MessageType, QList<NUM_PRIO>>::release_and_exit;
128 using StateFlow<MessageType, QList<NUM_PRIO>>::transfer_message;*/
132
134
135 /* Action entry()
136 {
137 currentIndex_ = 0;
138 lastHandlerToCall_ = nullptr;
139 return call_immediately(STATE(iterate));
140 }*/
141
149
150private:
153 template<class T>
154 friend class GenericHubFlow;
155
159 {
160 HandlerInfo() : handler(nullptr)
161 {
162 }
167
178 {
179 return (this->id == id && this->mask == mask &&
180 this->handler == handler);
181 }
182 };
183
185 vector<HandlerInfo> handlers_;
186
189
190protected:
196private:
199};
200
201
202// ==================== TEMPLATED =====================
203
204#ifdef TARGET_LPC11Cxx
205#define BASE_NUM_PRIO 4
206#else
209#define BASE_NUM_PRIO NUM_PRIO
210#endif
211
214template <class MessageType, int NUM_PRIO>
215class DispatchFlow : public TypedStateFlow<MessageType, DispatchFlowBase<BASE_NUM_PRIO> > {
216public:
219
224
230
235 typedef typename MessageType::value_type::id_type ID;
236
242
256 void register_handler(HandlerType *handler, ID id, ID mask) {
257 Base::register_handler(handler, id, mask);
258 }
259
266 void unregister_handler(HandlerType *handler, ID id, ID mask) {
267 Base::unregister_handler(handler, id, mask);
268 }
269
272 {
274 }
275
283
284protected:
287 return this->message()->data()->id();
288 }
289
292 if (!this->lastHandlerToCall_) {
293 // got unregistered.
295 }
296 HandlerType* h = static_cast<HandlerType *>(this->lastHandlerToCall_);
297 return allocate_and_call(h, STATE(clone));
298 }
299
303 HandlerType* h = static_cast<HandlerType *>(this->lastHandlerToCall_);
304 if (!this->lastHandlerToCall_) { // got unregistered
305 BufferBase* b;
306 this->cast_allocation_result(&b);
307 if (b) this->get_allocation_result(h)->unref();
309 }
310 MessageType *copy = this->get_allocation_result(h);
311 copy->set_done(this->message()->new_child());
312 *copy->data() = *this->message()->data();
313 h->send(copy);
315 }
316
320 HandlerType* h = static_cast<HandlerType *>(this->lastHandlerToCall_);
321 h->send(this->transfer_message());
322 }
323};
324
325
326// ================== IMPLEMENTATION ==================
327
328template <int NUM_PRIO>
330 : UntypedStateFlow<QList<NUM_PRIO>>(service)
331 , negateMatch_(false)
332 , lastHandlerToCall_(nullptr)
333{
334}
335
336template<int NUM_PRIO>
338{
339 HASSERT(this->is_waiting());
340}
341
342template<int NUM_PRIO>
344{
345 OSMutexLock h(&lock_);
346 size_t ret = 0;
347 for (auto &h : handlers_)
348 {
349 if (h.handler)
350 {
351 ++ret;
352 }
353 }
354 return ret;
355}
356
357template<int NUM_PRIO>
359 ID id, ID mask)
360{
361 OSMutexLock h(&lock_);
362 size_t idx = 0;
363 while (idx < handlers_.size() && handlers_[idx].handler)
364 {
365 ++idx;
366 }
367 if (idx >= handlers_.size())
368 {
369 handlers_.resize(handlers_.size() + 1);
370 }
371 handlers_[idx].handler = handler;
372 handlers_[idx].id = id;
373 handlers_[idx].mask = mask;
374}
375
376template<int NUM_PRIO>
377void
379 ID id, ID mask)
380{
381 OSMutexLock h(&lock_);
383 size_t idx = 0;
384 while (idx < handlers_.size() && !handlers_[idx].Equals(id, mask, handler))
385 {
386 ++idx;
387 }
388 // Checks that we found the thing to unregister.
389 HASSERT(idx < handlers_.size() &&
390 "Tried to unregister a handler not previously registered.");
391 if (lastHandlerToCall_ == handlers_[idx].handler) {
392 lastHandlerToCall_ = nullptr;
393 }
394 handlers_[idx].handler = nullptr;
395 if (idx == handlers_.size() - 1)
396 {
397 handlers_.resize(handlers_.size() - 1);
398 }
399}
400
401template<int NUM_PRIO>
403 UntypedHandler *handler)
404{
405 OSMutexLock h(&lock_);
406 for (size_t i = 0; i < handlers_.size(); ++i)
407 {
408 if (handlers_[i].handler == handler)
409 {
410 handlers_[i].handler = nullptr;
411 }
412 }
413 while (!handlers_.empty() && handlers_.back().handler == nullptr)
414 {
415 handlers_.pop_back();
416 }
417}
418
419template<int NUM_PRIO>
421{
422 currentIndex_ = 0;
423 lastHandlerToCall_ = nullptr;
424 return call_immediately(STATE(iterate));
425}
426
427template<int NUM_PRIO>
429{
430 ID id = get_message_id();
431 {
432 // @todo(balazs.racz) make the registered handlers structure for the
433 // dispatcher lock-free. This mutex here is very expensive.
434 OSMutexLock l(&lock_);
435 for (; currentIndex_ < handlers_.size(); ++currentIndex_)
436 {
437 auto &h = handlers_[currentIndex_];
438 if (!h.handler)
439 {
440 continue;
441 }
442 if (negateMatch_ && (id & h.mask) == (h.id & h.mask))
443 {
444 continue;
445 }
446 if ((!negateMatch_) && (id & h.mask) != (h.id & h.mask))
447 {
448 continue;
449 }
450 // At this point: we have another handler.
451 if (!lastHandlerToCall_)
452 {
453 // This was the first we found.
454 lastHandlerToCall_ = handlers_[currentIndex_].handler;
455 continue;
456 }
457 break;
458 }
459 }
460 if (currentIndex_ >= handlers_.size())
461 {
462 return iteration_done();
463 }
464 // Now: we have at least two different handler. We need to clone the
465 // message. We use the pool of the last handler to call by default.
466 return allocate_and_clone();
467}
468
469template<int NUM_PRIO>
471{
472 lastHandlerToCall_ = handlers_[currentIndex_].handler;
473 ++currentIndex_;
474 return call_immediately(STATE(iterate));
475}
476
477template<int NUM_PRIO>
479{
480 if (lastHandlerToCall_)
481 {
482 send_transfer();
483 }
484 else if (fallbackHandler_)
485 {
486 // Nothing handled this message, and we have a fallbac handler
487 // registered. Gives the message to the fallback handler.
488 lastHandlerToCall_ = fallbackHandler_;
489 send_transfer();
490 }
491 return release_and_exit();
492}
493
494#endif // _EXECUTOR_DISPATCHER_HXX_
#define STATE_FLOW_STATE(_state)
Declare a state callback in a StateFlow.
Definition StateFlow.hxx:68
#define STATE(_fn)
Turns a function name into an argument to be supplied to functions expecting a state.
Definition StateFlow.hxx:61
Abstract base class for all Buffers.
Definition Buffer.hxx:85
This class takes registrations of StateFlows for incoming messages.
UntypedHandler * fallbackHandler_
Handler to give all messages that were not matched by any other handler registration.
virtual void send_transfer()=0
Sends the current message to lastHandlerToCall, transferring ownership.
Action iteration_done()
State when the entire iteration is done.
void unregister_handler_all(UntypedHandler *handler)
Removes all instances of a handler from this dispatcher.
void UntypedHandler
Generic handler type.
StateFlowBase::Action Action
Allows using Action without having StateFlowBase:: prefix in front of it.
Action clone_done()
State after a clone-and-send operation is complete.
void register_fallback_handler(UntypedHandler *handler)
Sets one handler to receive all messages that no other handler has matched.
OSMutex lock_
Protects handler add / remove against iteration.
bool negateMatch_
true if this flow should negate the match condition.
vector< HandlerInfo > handlers_
Registered handlers.
virtual ID get_message_id()=0
Returns the current message's ID.
Action entry() override
Entry into the StateFlow activity.
Action iterate()
Iterate on potential handlers, matching the ID of the incoming message to the handlers' masks.
DispatchFlowBase(Service *service)
Construct a dispatchflow.
void unregister_handler(UntypedHandler *handler, ID id, ID mask)
Removes a specific instance of a handler from this dispatcher.
void register_handler(UntypedHandler *handler, ID id, ID mask)
Adds a new handler to this dispatcher.
virtual Action allocate_and_clone()=0
Allocates an entry from lastHandlerToCall_, invoking clone() when done: return allocate_and_call(last...
UntypedHandler * lastHandlerToCall_
If non-NULL we still need to call this handler.
size_t currentIndex_
Index of the next handler to look at.
uint32_t ID
Proxy the identifier type for customers to use.
Type-specific implementations of the DispatchFlow methods.
MessageType::value_type::id_type ID
Maskable type of the dispatched messages upon which handlers can configure to trigger.
Base::ID get_message_id() OVERRIDE
void register_handler(HandlerType *handler, ID id, ID mask)
Adds a new handler to this dispatcher.
Action allocate_and_call(FlowInterface< Buffer< T > > *target_flow, Callback c, Pool *pool=nullptr)
Allocates a buffer from a pool and proceed to the next state when allocation is successful.
Action entry() OVERRIDE
Override method that will be called after each incoming message is dequeued.
void register_fallback_handler(HandlerType *handler)
Sets one handler to receive all messages that no other handler has matched.
void send_transfer() OVERRIDE
Takes the existing buffer and sends off to the target flow.
DispatchFlow(Service *service)
Constructor.
FlowInterface< MessageType > HandlerType
Interface type for handlers that can be registered.
Action allocate_and_clone() OVERRIDE
Requests allocating a new buffer for sending off a clone.
StateFlowBase::Action Action
Imports types and functions to allow less typing for the implementation.
Action clone()
Takes the allocated new buffer, copies the message into it and sends off to the clone target.
Buffer< T > * get_allocation_result(FlowInterface< Buffer< T > > *target_flow)
Takes the result of the asynchronous allocation.
void unregister_handler_all(HandlerType *handler)
Removes all instances of a handler from this dispatcher.
Action call_immediately(Callback c)
Imediately call the next state upon return.
void unregister_handler(HandlerType *handler, ID id, ID mask)
Removes a specific instance of a handler from this dispatcher.
TypedStateFlow< MessageType, DispatchFlowBase< BASE_NUM_PRIO > > Base
Helper typedef of the base class of *this.
Abstract class for message recipients.
virtual void send(MessageType *message, unsigned priority=UINT_MAX)=0
Entry point to the flow.
Templated implementation of the HubFlow.
Definition Hub.hxx:150
Class to allow convenient locking and unlocking of mutexes in a C context.
Definition OS.hxx:494
This class provides a mutex API.
Definition OS.hxx:427
A list of queues.
Definition Queue.hxx:466
Collection of related state machines that pend on incoming messages.
Return type for a state flow callback.
Service * service()
Return a pointer to the service I am bound to.
Action allocate_and_call(FlowInterface< Buffer< T > > *target_flow, Callback c, Pool *pool=nullptr)
Allocates a buffer from a pool and proceed to the next state when allocation is successful.
Buffer< T > * get_allocation_result(FlowInterface< Buffer< T > > *target_flow)
Takes the result of the asynchronous allocation.
Action again()
Call the current state again via call_immediately.
Action call_immediately(Callback c)
Imediately call the next state upon return.
void cast_allocation_result(T **member)
Takes the result of the asynchronous allocation without resetting the object.
Action release_and_exit()
Terminates the processing of the current message.
Helper class in the StateFlow hierarchy.
MessageType * transfer_message()
Releases ownership of the current message.
State flow base class with queue but generic message type.
#define OVERRIDE
Function attribute for virtual functions declaring that this funciton is overriding a funciton that s...
Definition macros.h:180
#define HASSERT(x)
Checks that the value of expression x is true, else terminates the current process.
Definition macros.h:138
Internal information we store about each registered handler: identifier, mask, handler pointer.
ID mask
Mask that should be applied for the bits check.
ID id
Bits that this handler is registered for.
bool Equals(ID id, ID mask, UntypedHandler *handler)
Equality comparison function on the handlers.
UntypedHandler * handler
Handler to call. NULL if the handler has been removed.