Attempting to publish a tree

Dependencies:   BLE_API mbed-dev-bin nRF51822

Fork of microbit-dal by Lancaster University

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers MicroBitMessageBus.cpp Source File

MicroBitMessageBus.cpp

00001 /*
00002 The MIT License (MIT)
00003 
00004 Copyright (c) 2016 British Broadcasting Corporation.
00005 This software is provided by Lancaster University by arrangement with the BBC.
00006 
00007 Permission is hereby granted, free of charge, to any person obtaining a
00008 copy of this software and associated documentation files (the "Software"),
00009 to deal in the Software without restriction, including without limitation
00010 the rights to use, copy, modify, merge, publish, distribute, sublicense,
00011 and/or sell copies of the Software, and to permit persons to whom the
00012 Software is furnished to do so, subject to the following conditions:
00013 
00014 The above copyright notice and this permission notice shall be included in
00015 all copies or substantial portions of the Software.
00016 
00017 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
00018 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
00019 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
00020 THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
00021 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
00022 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
00023 DEALINGS IN THE SOFTWARE.
00024 */
00025 
00026 /**
00027   * Class definition for the MicroBitMessageBus.
00028   *
00029   * The MicroBitMessageBus is the common mechanism to deliver asynchronous events on the
00030   * MicroBit platform. It serves a number of purposes:
00031   *
00032   * 1) It provides an eventing abstraction that is independent of the underlying substrate.
00033   *
00034   * 2) It provides a mechanism to decouple user code from trusted system code
00035   *    i.e. the basis of a message passing nano kernel.
00036   *
00037   * 3) It allows a common high level eventing abstraction across a range of hardware types.e.g. buttons, BLE...
00038   *
00039   * 4) It provides a mechanim for extensibility - new devices added via I/O pins can have OO based
00040   *    drivers and communicate via the message bus with minima impact on user level languages.
00041   *
00042   * 5) It allows for the possiblility of event / data aggregation, which in turn can save energy.
00043   *
00044   * It has the following design principles:
00045   *
00046   * 1) Maintain a low RAM footprint where possible
00047   *
00048   * 2) Make few assumptions about the underlying platform, but allow optimizations where possible.
00049   */
00050 #include "MicroBitConfig.h"
00051 #include "MicroBitMessageBus.h"
00052 #include "MicroBitFiber.h"
00053 #include "ErrorNo.h"
00054 
00055 /**
00056   * Default constructor.
00057   *
00058   * Adds itself as a fiber component, and also configures itself to be the
00059   * default EventModel if defaultEventBus is NULL.
00060   */
00061 MicroBitMessageBus::MicroBitMessageBus()
00062 {
00063     this->listeners = NULL;
00064     this->evt_queue_head = NULL;
00065     this->evt_queue_tail = NULL;
00066     this->queueLength = 0;
00067 
00068     fiber_add_idle_component(this);
00069 
00070     if(EventModel::defaultEventBus == NULL)
00071         EventModel::defaultEventBus = this;
00072 }
00073 
00074 /**
00075   * Invokes a callback on a given MicroBitListener
00076   *
00077   * Internal wrapper function, used to enable
00078   * parameterised callbacks through the fiber scheduler.
00079   */
00080 void async_callback(void *param)
00081 {
00082     MicroBitListener *listener = (MicroBitListener *)param;
00083 
00084     // OK, now we need to decide how to behave depending on our configuration.
00085     // If this a fiber f already active within this listener then check our
00086     // configuration to determine the correct course of action.
00087     //
00088 
00089     if (listener->flags & MESSAGE_BUS_LISTENER_BUSY)
00090     {
00091         // Drop this event, if that's how we've been configured.
00092         if (listener->flags & MESSAGE_BUS_LISTENER_DROP_IF_BUSY)
00093             return;
00094 
00095         // Queue this event up for later, if that's how we've been configured.
00096         if (listener->flags & MESSAGE_BUS_LISTENER_QUEUE_IF_BUSY)
00097         {
00098             listener->queue(listener->evt);
00099             return;
00100         }
00101     }
00102 
00103     // Determine the calling convention for the callback, and invoke...
00104     // C++ is really bad at this! Especially as the ARM compiler is yet to support C++ 11 :-/
00105 
00106     // Record that we have a fiber going into this listener...
00107     listener->flags |= MESSAGE_BUS_LISTENER_BUSY;
00108 
00109     while (1)
00110     {
00111         // Firstly, check for a method callback into an object.
00112         if (listener->flags & MESSAGE_BUS_LISTENER_METHOD)
00113             listener->cb_method->fire(listener->evt);
00114 
00115         // Now a parameterised C function
00116         else if (listener->flags & MESSAGE_BUS_LISTENER_PARAMETERISED)
00117             listener->cb_param(listener->evt, listener->cb_arg);
00118 
00119         // We must have a plain C function
00120         else
00121             listener->cb(listener->evt);
00122 
00123         // If there are more events to process, dequeue the next one and process it.
00124         if ((listener->flags & MESSAGE_BUS_LISTENER_QUEUE_IF_BUSY) && listener->evt_queue)
00125         {
00126             MicroBitEventQueueItem *item = listener->evt_queue;
00127 
00128             listener->evt = item->evt;
00129             listener->evt_queue = listener->evt_queue->next;
00130             delete item;
00131 
00132             // We spin the scheduler here, to preven any particular event handler from continuously holding onto resources.
00133             schedule();
00134         }
00135         else
00136             break;
00137     }
00138 
00139     // The fiber of exiting... clear our state.
00140     listener->flags &= ~MESSAGE_BUS_LISTENER_BUSY;
00141 }
00142 
00143 /**
00144   * Queue the given event for processing at a later time.
00145   * Add the given event at the tail of our queue.
00146   *
00147   * @param The event to queue.
00148   */
00149 void MicroBitMessageBus::queueEvent(MicroBitEvent &evt)
00150 {
00151     int processingComplete;
00152 
00153     MicroBitEventQueueItem *prev = evt_queue_tail;
00154 
00155     // Now process all handler regsitered as URGENT.
00156     // These pre-empt the queue, and are useful for fast, high priority services.
00157     processingComplete = this->process(evt, true);
00158 
00159     // If we've already processed all event handlers, we're all done.
00160     // No need to queue the event.
00161     if (processingComplete)
00162         return;
00163 
00164     // If we need to queue, but there is no space, then there's nothg we can do.
00165     if (queueLength >= MESSAGE_BUS_LISTENER_MAX_QUEUE_DEPTH)
00166         return;
00167 
00168     // Otherwise, we need to queue this event for later processing...
00169     // We queue this event at the tail of the queue at the point where we entered queueEvent()
00170     // This is important as the processing above *may* have generated further events, and
00171     // we want to maintain ordering of events.
00172     MicroBitEventQueueItem *item = new MicroBitEventQueueItem(evt);
00173 
00174     // The queue was empty when we entered this function, so queue our event at the start of the queue.
00175     __disable_irq();
00176 
00177     if (prev == NULL)
00178     {
00179         item->next = evt_queue_head;
00180         evt_queue_head = item;
00181     }
00182     else
00183     {
00184         item->next = prev->next;
00185         prev->next = item;
00186     }
00187 
00188     if (item->next == NULL)
00189         evt_queue_tail = item;
00190 
00191     queueLength++;
00192 
00193     __enable_irq();
00194 }
00195 
00196 /**
00197   * Extract the next event from the front of the event queue (if present).
00198   *
00199   * @return a pointer to the MicroBitEventQueueItem that is at the head of the list.
00200   */
00201 MicroBitEventQueueItem* MicroBitMessageBus::dequeueEvent()
00202 {
00203     MicroBitEventQueueItem *item = NULL;
00204 
00205     __disable_irq();
00206 
00207     if (evt_queue_head != NULL)
00208     {
00209         item = evt_queue_head;
00210         evt_queue_head = item->next;
00211 
00212         if (evt_queue_head == NULL)
00213             evt_queue_tail = NULL;
00214 
00215         queueLength--;
00216     }
00217 
00218     __enable_irq();
00219 
00220 
00221     return item;
00222 }
00223 
00224 /**
00225   * Cleanup any MicroBitListeners marked for deletion from the list.
00226   *
00227   * @return The number of listeners removed from the list.
00228   */
00229 int MicroBitMessageBus::deleteMarkedListeners()
00230 {
00231     MicroBitListener *l, *p;
00232     int removed = 0;
00233 
00234     l = listeners;
00235     p = NULL;
00236 
00237     // Walk this list of event handlers. Delete any that match the given listener.
00238     while (l != NULL)
00239     {
00240         if (l->flags & MESSAGE_BUS_LISTENER_DELETING && !l->flags & MESSAGE_BUS_LISTENER_BUSY)
00241         {
00242             if (p == NULL)
00243                 listeners = l->next;
00244             else
00245                 p->next = l->next;
00246 
00247             // delete the listener.
00248             MicroBitListener *t = l;
00249             l = l->next;
00250 
00251             delete t;
00252             removed++;
00253 
00254             continue;
00255         }
00256 
00257         p = l;
00258         l = l->next;
00259     }
00260 
00261     return removed;
00262 }
00263 
00264 /**
00265   * Periodic callback from MicroBit.
00266   *
00267   * Process at least one event from the event queue, if it is not empty.
00268   * We then continue processing events until something appears on the runqueue.
00269   */
00270 void MicroBitMessageBus::idleTick()
00271 {
00272     // Clear out any listeners marked for deletion
00273     this->deleteMarkedListeners();
00274 
00275     MicroBitEventQueueItem *item = this->dequeueEvent();
00276 
00277     // Whilst there are events to process and we have no useful other work to do, pull them off the queue and process them.
00278     while (item)
00279     {
00280         // send the event to all standard event listeners.
00281         this->process(item->evt);
00282 
00283         // Free the queue item.
00284         delete item;
00285 
00286         // If we have created some useful work to do, we stop processing.
00287         // This helps to minimise the number of blocked fibers we create at any point in time, therefore
00288         // also reducing the RAM footprint.
00289         if(!scheduler_runqueue_empty())
00290             break;
00291 
00292         // Pull the next event to process, if there is one.
00293         item = this->dequeueEvent();
00294     }
00295 }
00296 
00297 /**
00298   * Indicates whether or not we have any background work to do.
00299   *
00300   * @return 1 if there are any events waitingto be processed, 0 otherwise.
00301   */
00302 int MicroBitMessageBus::isIdleCallbackNeeded()
00303 {
00304     return !(evt_queue_head == NULL);
00305 }
00306 
00307 /**
00308   * Queues the given event to be sent to all registered recipients.
00309   *
00310   * @param evt The event to send.
00311   *
00312   * @code
00313   * MicroBitMessageBus bus;
00314   *
00315   * // Creates and sends the MicroBitEvent using bus.
00316   * MicrobitEvent evt(MICROBIT_ID_BUTTON_A, MICROBIT_BUTTON_EVT_CLICK);
00317   *
00318   * // Creates the MicrobitEvent, but delays the sending of that event.
00319   * MicrobitEvent evt1(MICROBIT_ID_BUTTON_A, MICROBIT_BUTTON_EVT_CLICK, CREATE_ONLY);
00320   *
00321   * bus.send(evt1);
00322   *
00323   * // This has the same effect!
00324   * evt1.fire()
00325   * @endcode
00326   */
00327 int MicroBitMessageBus::send(MicroBitEvent evt)
00328 {
00329     // We simply queue processing of the event until we're scheduled in normal thread context.
00330     // We do this to avoid the possibility of executing event handler code in IRQ context, which may bring
00331     // hidden race conditions to kids code. Queuing all events ensures causal ordering (total ordering in fact).
00332     this->queueEvent(evt);
00333     return MICROBIT_OK;
00334 }
00335 
00336 /**
00337   * Internal function, used to deliver the given event to all relevant recipients.
00338   * Normally, this is called once an event has been removed from the event queue.
00339   *
00340   * @param evt The event to send.
00341   *
00342   * @param urgent The type of listeners to process (optional). If set to true, only listeners defined as urgent and non-blocking will be processed
00343   *               otherwise, all other (standard) listeners will be processed. Defaults to false.
00344   *
00345   * @return 1 if all matching listeners were processed, 0 if further processing is required.
00346   *
00347   * @note It is recommended that all external code uses the send() function instead of this function,
00348   *       or the constructors provided by MicrobitEvent.
00349   */
00350 int MicroBitMessageBus::process(MicroBitEvent &evt, bool urgent)
00351 {
00352     MicroBitListener *l;
00353     int complete = 1;
00354     bool listenerUrgent;
00355 
00356     l = listeners;
00357     while (l != NULL)
00358     {
00359         if((l->id == evt.source || l->id == MICROBIT_ID_ANY) && (l->value == evt.value || l->value == MICROBIT_EVT_ANY))
00360         {
00361             // If we're running under the fiber scheduler, then derive the THREADING_MODE for the callback based on the
00362             // metadata in the listener itself.
00363             if (fiber_scheduler_running())
00364                 listenerUrgent = (l->flags & MESSAGE_BUS_LISTENER_IMMEDIATE) == MESSAGE_BUS_LISTENER_IMMEDIATE;
00365             else
00366                 listenerUrgent = true;
00367 
00368             // If we should process this event hander in this pass, then activate the listener.
00369             if(listenerUrgent == urgent && !(l->flags & MESSAGE_BUS_LISTENER_DELETING))
00370             {
00371                 l->evt = evt;
00372 
00373                 // OK, if this handler has regisitered itself as non-blocking, we just execute it directly...
00374                 // This is normally only done for trusted system components.
00375                 // Otherwise, we invoke it in a 'fork on block' context, that will automatically create a fiber
00376                 // should the event handler attempt a blocking operation, but doesn't have the overhead
00377                 // of creating a fiber needlessly. (cool huh?)
00378                 if (l->flags & MESSAGE_BUS_LISTENER_NONBLOCKING || !fiber_scheduler_running())
00379                     async_callback(l);
00380                 else
00381                     invoke(async_callback, l);
00382             }
00383             else
00384             {
00385                 complete = 0;
00386             }
00387         }
00388 
00389         l = l->next;
00390     }
00391 
00392     return complete;
00393 }
00394 
00395 /**
00396   * Add the given MicroBitListener to the list of event handlers, unconditionally.
00397   *
00398   * @param listener The MicroBitListener to add.
00399   *
00400   * @return MICROBIT_OK if the listener is valid, MICROBIT_INVALID_PARAMETER otherwise.
00401   */
00402 int MicroBitMessageBus::add(MicroBitListener *newListener)
00403 {
00404     MicroBitListener *l, *p;
00405     int methodCallback;
00406 
00407     //handler can't be NULL!
00408     if (newListener == NULL)
00409         return MICROBIT_INVALID_PARAMETER;
00410 
00411     l = listeners;
00412 
00413     // Firstly, we treat a listener as an idempotent operation. Ensure we don't already have this handler
00414     // registered in a that will already capture these events. If we do, silently ignore.
00415 
00416     // We always check the ID, VALUE and CB_METHOD fields.
00417     // If we have a callback to a method, check the cb_method class. Otherwise, the cb function point is sufficient.
00418     while (l != NULL)
00419     {
00420         methodCallback = (newListener->flags & MESSAGE_BUS_LISTENER_METHOD) && (l->flags & MESSAGE_BUS_LISTENER_METHOD);
00421 
00422         if (l->id == newListener->id && l->value == newListener->value && (methodCallback ? *l->cb_method == *newListener->cb_method : l->cb == newListener->cb))
00423         {
00424             // We have a perfect match for this event listener already registered.
00425             // If it's marked for deletion, we simply resurrect the listener, and we're done.
00426             // Either way, we return an error code, as the *new* listener should be released...
00427             if(l->flags & MESSAGE_BUS_LISTENER_DELETING)
00428                 l->flags &= ~MESSAGE_BUS_LISTENER_DELETING;
00429 
00430             return MICROBIT_NOT_SUPPORTED;
00431         }
00432 
00433         l = l->next;
00434     }
00435 
00436     // We have a valid, new event handler. Add it to the list.
00437     // if listeners is null - we can automatically add this listener to the list at the beginning...
00438     if (listeners == NULL)
00439     {
00440         listeners = newListener;
00441         MicroBitEvent(MICROBIT_ID_MESSAGE_BUS_LISTENER, newListener->id);
00442 
00443         return MICROBIT_OK;
00444     }
00445 
00446     // We maintain an ordered list of listeners.
00447     // The chain is held stictly in increasing order of ID (first level), then value code (second level).
00448     // Find the correct point in the chain for this event.
00449     // Adding a listener is a rare occurance, so we just walk the list...
00450 
00451     p = listeners;
00452     l = listeners;
00453 
00454     while (l != NULL && l->id < newListener->id)
00455     {
00456         p = l;
00457         l = l->next;
00458     }
00459 
00460     while (l != NULL && l->id == newListener->id && l->value < newListener->value)
00461     {
00462         p = l;
00463         l = l->next;
00464     }
00465 
00466     //add at front of list
00467     if (p == listeners && (newListener->id < p->id || (p->id == newListener->id && p->value > newListener->value)))
00468     {
00469         newListener->next = p;
00470 
00471         //this new listener is now the front!
00472         listeners = newListener;
00473     }
00474 
00475     //add after p
00476     else
00477     {
00478         newListener->next = p->next;
00479         p->next = newListener;
00480     }
00481 
00482     MicroBitEvent(MICROBIT_ID_MESSAGE_BUS_LISTENER, newListener->id);
00483     return MICROBIT_OK;
00484 }
00485 
00486 /**
00487   * Remove the given MicroBitListener from the list of event handlers.
00488   *
00489   * @param listener The MicroBitListener to remove.
00490   *
00491   * @return MICROBIT_OK if the listener is valid, MICROBIT_INVALID_PARAMETER otherwise.
00492   */
00493 int MicroBitMessageBus::remove(MicroBitListener *listener)
00494 {
00495     MicroBitListener *l;
00496     int removed = 0;
00497 
00498     //handler can't be NULL!
00499     if (listener == NULL)
00500         return MICROBIT_INVALID_PARAMETER;
00501 
00502     l = listeners;
00503 
00504     // Walk this list of event handlers. Delete any that match the given listener.
00505     while (l != NULL)
00506     {
00507         if ((listener->flags & MESSAGE_BUS_LISTENER_METHOD) == (l->flags & MESSAGE_BUS_LISTENER_METHOD))
00508         {
00509             if(((listener->flags & MESSAGE_BUS_LISTENER_METHOD) && (*l->cb_method == *listener->cb_method)) ||
00510               ((!(listener->flags & MESSAGE_BUS_LISTENER_METHOD) && l->cb == listener->cb)))
00511             {
00512                 if ((listener->id == MICROBIT_ID_ANY || listener->id == l->id) && (listener->value == MICROBIT_EVT_ANY || listener->value == l->value))
00513                 {
00514                     // Found a match. mark this to be removed from the list.
00515                     l->flags |= MESSAGE_BUS_LISTENER_DELETING;
00516                     removed++;
00517                 }
00518             }
00519         }
00520 
00521         l = l->next;
00522     }
00523 
00524     if (removed > 0)
00525         return MICROBIT_OK;
00526     else
00527         return MICROBIT_INVALID_PARAMETER;
00528 }
00529 
00530 /**
00531   * Returns the microBitListener with the given position in our list.
00532   *
00533   * @param n The position in the list to return.
00534   *
00535   * @return the MicroBitListener at postion n in the list, or NULL if the position is invalid.
00536   */
00537 MicroBitListener* MicroBitMessageBus::elementAt(int n)
00538 {
00539     MicroBitListener *l = listeners;
00540 
00541     while (n > 0)
00542     {
00543         if (l == NULL)
00544             return NULL;
00545 
00546         n--;
00547         l = l->next;
00548     }
00549 
00550     return l;
00551 }
00552 
00553 /**
00554   * Destructor for MicroBitMessageBus, where we deregister this instance from the array of fiber components.
00555   */
00556 MicroBitMessageBus::~MicroBitMessageBus()
00557 {
00558     fiber_remove_idle_component(this);
00559 }