fork
Dependencies: BLE_API mbed-dev-bin nRF51822
Fork of microbit-dal by
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 }
Generated on Tue Jul 12 2022 19:47:35 by 1.7.2