mbed.org local branch of microbit-dal. The real version lives in git at https://github.com/lancaster-university/microbit-dal
Dependencies: BLE_API nRF51822 mbed-dev-bin
Dependents: microbit Microbit IoTChallenge1 microbit ... more
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 * Queues the given event to be sent to all registered recipients. 00299 * 00300 * @param evt The event to send. 00301 * 00302 * @code 00303 * MicroBitMessageBus bus; 00304 * 00305 * // Creates and sends the MicroBitEvent using bus. 00306 * MicrobitEvent evt(MICROBIT_ID_BUTTON_A, MICROBIT_BUTTON_EVT_CLICK); 00307 * 00308 * // Creates the MicrobitEvent, but delays the sending of that event. 00309 * MicrobitEvent evt1(MICROBIT_ID_BUTTON_A, MICROBIT_BUTTON_EVT_CLICK, CREATE_ONLY); 00310 * 00311 * bus.send(evt1); 00312 * 00313 * // This has the same effect! 00314 * evt1.fire() 00315 * @endcode 00316 */ 00317 int MicroBitMessageBus::send(MicroBitEvent evt) 00318 { 00319 // We simply queue processing of the event until we're scheduled in normal thread context. 00320 // We do this to avoid the possibility of executing event handler code in IRQ context, which may bring 00321 // hidden race conditions to kids code. Queuing all events ensures causal ordering (total ordering in fact). 00322 this->queueEvent(evt); 00323 return MICROBIT_OK; 00324 } 00325 00326 /** 00327 * Internal function, used to deliver the given event to all relevant recipients. 00328 * Normally, this is called once an event has been removed from the event queue. 00329 * 00330 * @param evt The event to send. 00331 * 00332 * @param urgent The type of listeners to process (optional). If set to true, only listeners defined as urgent and non-blocking will be processed 00333 * otherwise, all other (standard) listeners will be processed. Defaults to false. 00334 * 00335 * @return 1 if all matching listeners were processed, 0 if further processing is required. 00336 * 00337 * @note It is recommended that all external code uses the send() function instead of this function, 00338 * or the constructors provided by MicrobitEvent. 00339 */ 00340 int MicroBitMessageBus::process(MicroBitEvent &evt, bool urgent) 00341 { 00342 MicroBitListener *l; 00343 int complete = 1; 00344 bool listenerUrgent; 00345 00346 l = listeners; 00347 while (l != NULL) 00348 { 00349 if((l->id == evt.source || l->id == MICROBIT_ID_ANY) && (l->value == evt.value || l->value == MICROBIT_EVT_ANY)) 00350 { 00351 // If we're running under the fiber scheduler, then derive the THREADING_MODE for the callback based on the 00352 // metadata in the listener itself. 00353 if (fiber_scheduler_running()) 00354 listenerUrgent = (l->flags & MESSAGE_BUS_LISTENER_IMMEDIATE) == MESSAGE_BUS_LISTENER_IMMEDIATE; 00355 else 00356 listenerUrgent = true; 00357 00358 // If we should process this event hander in this pass, then activate the listener. 00359 if(listenerUrgent == urgent && !(l->flags & MESSAGE_BUS_LISTENER_DELETING)) 00360 { 00361 l->evt = evt; 00362 00363 // OK, if this handler has regisitered itself as non-blocking, we just execute it directly... 00364 // This is normally only done for trusted system components. 00365 // Otherwise, we invoke it in a 'fork on block' context, that will automatically create a fiber 00366 // should the event handler attempt a blocking operation, but doesn't have the overhead 00367 // of creating a fiber needlessly. (cool huh?) 00368 if (l->flags & MESSAGE_BUS_LISTENER_NONBLOCKING || !fiber_scheduler_running()) 00369 async_callback(l); 00370 else 00371 invoke(async_callback, l); 00372 } 00373 else 00374 { 00375 complete = 0; 00376 } 00377 } 00378 00379 l = l->next; 00380 } 00381 00382 return complete; 00383 } 00384 00385 /** 00386 * Add the given MicroBitListener to the list of event handlers, unconditionally. 00387 * 00388 * @param listener The MicroBitListener to add. 00389 * 00390 * @return MICROBIT_OK if the listener is valid, MICROBIT_INVALID_PARAMETER otherwise. 00391 */ 00392 int MicroBitMessageBus::add(MicroBitListener *newListener) 00393 { 00394 MicroBitListener *l, *p; 00395 int methodCallback; 00396 00397 //handler can't be NULL! 00398 if (newListener == NULL) 00399 return MICROBIT_INVALID_PARAMETER; 00400 00401 l = listeners; 00402 00403 // Firstly, we treat a listener as an idempotent operation. Ensure we don't already have this handler 00404 // registered in a that will already capture these events. If we do, silently ignore. 00405 00406 // We always check the ID, VALUE and CB_METHOD fields. 00407 // If we have a callback to a method, check the cb_method class. Otherwise, the cb function point is sufficient. 00408 while (l != NULL) 00409 { 00410 methodCallback = (newListener->flags & MESSAGE_BUS_LISTENER_METHOD) && (l->flags & MESSAGE_BUS_LISTENER_METHOD); 00411 00412 if (l->id == newListener->id && l->value == newListener->value && (methodCallback ? *l->cb_method == *newListener->cb_method : l->cb == newListener->cb)) 00413 { 00414 // We have a perfect match for this event listener already registered. 00415 // If it's marked for deletion, we simply resurrect the listener, and we're done. 00416 // Either way, we return an error code, as the *new* listener should be released... 00417 if(l->flags & MESSAGE_BUS_LISTENER_DELETING) 00418 l->flags &= ~MESSAGE_BUS_LISTENER_DELETING; 00419 00420 return MICROBIT_NOT_SUPPORTED; 00421 } 00422 00423 l = l->next; 00424 } 00425 00426 // We have a valid, new event handler. Add it to the list. 00427 // if listeners is null - we can automatically add this listener to the list at the beginning... 00428 if (listeners == NULL) 00429 { 00430 listeners = newListener; 00431 MicroBitEvent(MICROBIT_ID_MESSAGE_BUS_LISTENER, newListener->id); 00432 00433 return MICROBIT_OK; 00434 } 00435 00436 // We maintain an ordered list of listeners. 00437 // The chain is held stictly in increasing order of ID (first level), then value code (second level). 00438 // Find the correct point in the chain for this event. 00439 // Adding a listener is a rare occurance, so we just walk the list... 00440 00441 p = listeners; 00442 l = listeners; 00443 00444 while (l != NULL && l->id < newListener->id) 00445 { 00446 p = l; 00447 l = l->next; 00448 } 00449 00450 while (l != NULL && l->id == newListener->id && l->value < newListener->value) 00451 { 00452 p = l; 00453 l = l->next; 00454 } 00455 00456 //add at front of list 00457 if (p == listeners && (newListener->id < p->id || (p->id == newListener->id && p->value > newListener->value))) 00458 { 00459 newListener->next = p; 00460 00461 //this new listener is now the front! 00462 listeners = newListener; 00463 } 00464 00465 //add after p 00466 else 00467 { 00468 newListener->next = p->next; 00469 p->next = newListener; 00470 } 00471 00472 MicroBitEvent(MICROBIT_ID_MESSAGE_BUS_LISTENER, newListener->id); 00473 return MICROBIT_OK; 00474 } 00475 00476 /** 00477 * Remove the given MicroBitListener from the list of event handlers. 00478 * 00479 * @param listener The MicroBitListener to remove. 00480 * 00481 * @return MICROBIT_OK if the listener is valid, MICROBIT_INVALID_PARAMETER otherwise. 00482 */ 00483 int MicroBitMessageBus::remove(MicroBitListener *listener) 00484 { 00485 MicroBitListener *l; 00486 int removed = 0; 00487 00488 //handler can't be NULL! 00489 if (listener == NULL) 00490 return MICROBIT_INVALID_PARAMETER; 00491 00492 l = listeners; 00493 00494 // Walk this list of event handlers. Delete any that match the given listener. 00495 while (l != NULL) 00496 { 00497 if ((listener->flags & MESSAGE_BUS_LISTENER_METHOD) == (l->flags & MESSAGE_BUS_LISTENER_METHOD)) 00498 { 00499 if(((listener->flags & MESSAGE_BUS_LISTENER_METHOD) && (*l->cb_method == *listener->cb_method)) || 00500 ((!(listener->flags & MESSAGE_BUS_LISTENER_METHOD) && l->cb == listener->cb))) 00501 { 00502 if ((listener->id == MICROBIT_ID_ANY || listener->id == l->id) && (listener->value == MICROBIT_EVT_ANY || listener->value == l->value)) 00503 { 00504 // Found a match. mark this to be removed from the list. 00505 l->flags |= MESSAGE_BUS_LISTENER_DELETING; 00506 removed++; 00507 } 00508 } 00509 } 00510 00511 l = l->next; 00512 } 00513 00514 if (removed > 0) 00515 return MICROBIT_OK; 00516 else 00517 return MICROBIT_INVALID_PARAMETER; 00518 } 00519 00520 /** 00521 * Returns the microBitListener with the given position in our list. 00522 * 00523 * @param n The position in the list to return. 00524 * 00525 * @return the MicroBitListener at postion n in the list, or NULL if the position is invalid. 00526 */ 00527 MicroBitListener* MicroBitMessageBus::elementAt(int n) 00528 { 00529 MicroBitListener *l = listeners; 00530 00531 while (n > 0) 00532 { 00533 if (l == NULL) 00534 return NULL; 00535 00536 n--; 00537 l = l->next; 00538 } 00539 00540 return l; 00541 } 00542 00543 /** 00544 * Destructor for MicroBitMessageBus, where we deregister this instance from the array of fiber components. 00545 */ 00546 MicroBitMessageBus::~MicroBitMessageBus() 00547 { 00548 fiber_remove_idle_component(this); 00549 }
Generated on Tue Jul 12 2022 15:22:56 by 1.7.2