Official Sheffield ARMBand micro:bit program

Committer:
MrBedfordVan
Date:
Mon Oct 17 12:41:20 2016 +0000
Revision:
0:b9164b348919
Official Sheffield ARMBand Micro:bit program

Who changed what in which revision?

UserRevisionLine numberNew contents of line
MrBedfordVan 0:b9164b348919 1 /*
MrBedfordVan 0:b9164b348919 2 The MIT License (MIT)
MrBedfordVan 0:b9164b348919 3
MrBedfordVan 0:b9164b348919 4 Copyright (c) 2016 British Broadcasting Corporation.
MrBedfordVan 0:b9164b348919 5 This software is provided by Lancaster University by arrangement with the BBC.
MrBedfordVan 0:b9164b348919 6
MrBedfordVan 0:b9164b348919 7 Permission is hereby granted, free of charge, to any person obtaining a
MrBedfordVan 0:b9164b348919 8 copy of this software and associated documentation files (the "Software"),
MrBedfordVan 0:b9164b348919 9 to deal in the Software without restriction, including without limitation
MrBedfordVan 0:b9164b348919 10 the rights to use, copy, modify, merge, publish, distribute, sublicense,
MrBedfordVan 0:b9164b348919 11 and/or sell copies of the Software, and to permit persons to whom the
MrBedfordVan 0:b9164b348919 12 Software is furnished to do so, subject to the following conditions:
MrBedfordVan 0:b9164b348919 13
MrBedfordVan 0:b9164b348919 14 The above copyright notice and this permission notice shall be included in
MrBedfordVan 0:b9164b348919 15 all copies or substantial portions of the Software.
MrBedfordVan 0:b9164b348919 16
MrBedfordVan 0:b9164b348919 17 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
MrBedfordVan 0:b9164b348919 18 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
MrBedfordVan 0:b9164b348919 19 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
MrBedfordVan 0:b9164b348919 20 THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
MrBedfordVan 0:b9164b348919 21 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
MrBedfordVan 0:b9164b348919 22 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
MrBedfordVan 0:b9164b348919 23 DEALINGS IN THE SOFTWARE.
MrBedfordVan 0:b9164b348919 24 */
MrBedfordVan 0:b9164b348919 25
MrBedfordVan 0:b9164b348919 26 /**
MrBedfordVan 0:b9164b348919 27 * Class definition for the MicroBitMessageBus.
MrBedfordVan 0:b9164b348919 28 *
MrBedfordVan 0:b9164b348919 29 * The MicroBitMessageBus is the common mechanism to deliver asynchronous events on the
MrBedfordVan 0:b9164b348919 30 * MicroBit platform. It serves a number of purposes:
MrBedfordVan 0:b9164b348919 31 *
MrBedfordVan 0:b9164b348919 32 * 1) It provides an eventing abstraction that is independent of the underlying substrate.
MrBedfordVan 0:b9164b348919 33 *
MrBedfordVan 0:b9164b348919 34 * 2) It provides a mechanism to decouple user code from trusted system code
MrBedfordVan 0:b9164b348919 35 * i.e. the basis of a message passing nano kernel.
MrBedfordVan 0:b9164b348919 36 *
MrBedfordVan 0:b9164b348919 37 * 3) It allows a common high level eventing abstraction across a range of hardware types.e.g. buttons, BLE...
MrBedfordVan 0:b9164b348919 38 *
MrBedfordVan 0:b9164b348919 39 * 4) It provides a mechanim for extensibility - new devices added via I/O pins can have OO based
MrBedfordVan 0:b9164b348919 40 * drivers and communicate via the message bus with minima impact on user level languages.
MrBedfordVan 0:b9164b348919 41 *
MrBedfordVan 0:b9164b348919 42 * 5) It allows for the possiblility of event / data aggregation, which in turn can save energy.
MrBedfordVan 0:b9164b348919 43 *
MrBedfordVan 0:b9164b348919 44 * It has the following design principles:
MrBedfordVan 0:b9164b348919 45 *
MrBedfordVan 0:b9164b348919 46 * 1) Maintain a low RAM footprint where possible
MrBedfordVan 0:b9164b348919 47 *
MrBedfordVan 0:b9164b348919 48 * 2) Make few assumptions about the underlying platform, but allow optimizations where possible.
MrBedfordVan 0:b9164b348919 49 */
MrBedfordVan 0:b9164b348919 50 #include "MicroBitConfig.h"
MrBedfordVan 0:b9164b348919 51 #include "MicroBitMessageBus.h"
MrBedfordVan 0:b9164b348919 52 #include "MicroBitFiber.h"
MrBedfordVan 0:b9164b348919 53 #include "ErrorNo.h"
MrBedfordVan 0:b9164b348919 54
MrBedfordVan 0:b9164b348919 55 /**
MrBedfordVan 0:b9164b348919 56 * Default constructor.
MrBedfordVan 0:b9164b348919 57 *
MrBedfordVan 0:b9164b348919 58 * Adds itself as a fiber component, and also configures itself to be the
MrBedfordVan 0:b9164b348919 59 * default EventModel if defaultEventBus is NULL.
MrBedfordVan 0:b9164b348919 60 */
MrBedfordVan 0:b9164b348919 61 MicroBitMessageBus::MicroBitMessageBus()
MrBedfordVan 0:b9164b348919 62 {
MrBedfordVan 0:b9164b348919 63 this->listeners = NULL;
MrBedfordVan 0:b9164b348919 64 this->evt_queue_head = NULL;
MrBedfordVan 0:b9164b348919 65 this->evt_queue_tail = NULL;
MrBedfordVan 0:b9164b348919 66 this->queueLength = 0;
MrBedfordVan 0:b9164b348919 67
MrBedfordVan 0:b9164b348919 68 fiber_add_idle_component(this);
MrBedfordVan 0:b9164b348919 69
MrBedfordVan 0:b9164b348919 70 if(EventModel::defaultEventBus == NULL)
MrBedfordVan 0:b9164b348919 71 EventModel::defaultEventBus = this;
MrBedfordVan 0:b9164b348919 72 }
MrBedfordVan 0:b9164b348919 73
MrBedfordVan 0:b9164b348919 74 /**
MrBedfordVan 0:b9164b348919 75 * Invokes a callback on a given MicroBitListener
MrBedfordVan 0:b9164b348919 76 *
MrBedfordVan 0:b9164b348919 77 * Internal wrapper function, used to enable
MrBedfordVan 0:b9164b348919 78 * parameterised callbacks through the fiber scheduler.
MrBedfordVan 0:b9164b348919 79 */
MrBedfordVan 0:b9164b348919 80 void async_callback(void *param)
MrBedfordVan 0:b9164b348919 81 {
MrBedfordVan 0:b9164b348919 82 MicroBitListener *listener = (MicroBitListener *)param;
MrBedfordVan 0:b9164b348919 83
MrBedfordVan 0:b9164b348919 84 // OK, now we need to decide how to behave depending on our configuration.
MrBedfordVan 0:b9164b348919 85 // If this a fiber f already active within this listener then check our
MrBedfordVan 0:b9164b348919 86 // configuration to determine the correct course of action.
MrBedfordVan 0:b9164b348919 87 //
MrBedfordVan 0:b9164b348919 88
MrBedfordVan 0:b9164b348919 89 if (listener->flags & MESSAGE_BUS_LISTENER_BUSY)
MrBedfordVan 0:b9164b348919 90 {
MrBedfordVan 0:b9164b348919 91 // Drop this event, if that's how we've been configured.
MrBedfordVan 0:b9164b348919 92 if (listener->flags & MESSAGE_BUS_LISTENER_DROP_IF_BUSY)
MrBedfordVan 0:b9164b348919 93 return;
MrBedfordVan 0:b9164b348919 94
MrBedfordVan 0:b9164b348919 95 // Queue this event up for later, if that's how we've been configured.
MrBedfordVan 0:b9164b348919 96 if (listener->flags & MESSAGE_BUS_LISTENER_QUEUE_IF_BUSY)
MrBedfordVan 0:b9164b348919 97 {
MrBedfordVan 0:b9164b348919 98 listener->queue(listener->evt);
MrBedfordVan 0:b9164b348919 99 return;
MrBedfordVan 0:b9164b348919 100 }
MrBedfordVan 0:b9164b348919 101 }
MrBedfordVan 0:b9164b348919 102
MrBedfordVan 0:b9164b348919 103 // Determine the calling convention for the callback, and invoke...
MrBedfordVan 0:b9164b348919 104 // C++ is really bad at this! Especially as the ARM compiler is yet to support C++ 11 :-/
MrBedfordVan 0:b9164b348919 105
MrBedfordVan 0:b9164b348919 106 // Record that we have a fiber going into this listener...
MrBedfordVan 0:b9164b348919 107 listener->flags |= MESSAGE_BUS_LISTENER_BUSY;
MrBedfordVan 0:b9164b348919 108
MrBedfordVan 0:b9164b348919 109 while (1)
MrBedfordVan 0:b9164b348919 110 {
MrBedfordVan 0:b9164b348919 111 // Firstly, check for a method callback into an object.
MrBedfordVan 0:b9164b348919 112 if (listener->flags & MESSAGE_BUS_LISTENER_METHOD)
MrBedfordVan 0:b9164b348919 113 listener->cb_method->fire(listener->evt);
MrBedfordVan 0:b9164b348919 114
MrBedfordVan 0:b9164b348919 115 // Now a parameterised C function
MrBedfordVan 0:b9164b348919 116 else if (listener->flags & MESSAGE_BUS_LISTENER_PARAMETERISED)
MrBedfordVan 0:b9164b348919 117 listener->cb_param(listener->evt, listener->cb_arg);
MrBedfordVan 0:b9164b348919 118
MrBedfordVan 0:b9164b348919 119 // We must have a plain C function
MrBedfordVan 0:b9164b348919 120 else
MrBedfordVan 0:b9164b348919 121 listener->cb(listener->evt);
MrBedfordVan 0:b9164b348919 122
MrBedfordVan 0:b9164b348919 123 // If there are more events to process, dequeue the next one and process it.
MrBedfordVan 0:b9164b348919 124 if ((listener->flags & MESSAGE_BUS_LISTENER_QUEUE_IF_BUSY) && listener->evt_queue)
MrBedfordVan 0:b9164b348919 125 {
MrBedfordVan 0:b9164b348919 126 MicroBitEventQueueItem *item = listener->evt_queue;
MrBedfordVan 0:b9164b348919 127
MrBedfordVan 0:b9164b348919 128 listener->evt = item->evt;
MrBedfordVan 0:b9164b348919 129 listener->evt_queue = listener->evt_queue->next;
MrBedfordVan 0:b9164b348919 130 delete item;
MrBedfordVan 0:b9164b348919 131
MrBedfordVan 0:b9164b348919 132 // We spin the scheduler here, to preven any particular event handler from continuously holding onto resources.
MrBedfordVan 0:b9164b348919 133 schedule();
MrBedfordVan 0:b9164b348919 134 }
MrBedfordVan 0:b9164b348919 135 else
MrBedfordVan 0:b9164b348919 136 break;
MrBedfordVan 0:b9164b348919 137 }
MrBedfordVan 0:b9164b348919 138
MrBedfordVan 0:b9164b348919 139 // The fiber of exiting... clear our state.
MrBedfordVan 0:b9164b348919 140 listener->flags &= ~MESSAGE_BUS_LISTENER_BUSY;
MrBedfordVan 0:b9164b348919 141 }
MrBedfordVan 0:b9164b348919 142
MrBedfordVan 0:b9164b348919 143 /**
MrBedfordVan 0:b9164b348919 144 * Queue the given event for processing at a later time.
MrBedfordVan 0:b9164b348919 145 * Add the given event at the tail of our queue.
MrBedfordVan 0:b9164b348919 146 *
MrBedfordVan 0:b9164b348919 147 * @param The event to queue.
MrBedfordVan 0:b9164b348919 148 */
MrBedfordVan 0:b9164b348919 149 void MicroBitMessageBus::queueEvent(MicroBitEvent &evt)
MrBedfordVan 0:b9164b348919 150 {
MrBedfordVan 0:b9164b348919 151 int processingComplete;
MrBedfordVan 0:b9164b348919 152
MrBedfordVan 0:b9164b348919 153 MicroBitEventQueueItem *prev = evt_queue_tail;
MrBedfordVan 0:b9164b348919 154
MrBedfordVan 0:b9164b348919 155 // Now process all handler regsitered as URGENT.
MrBedfordVan 0:b9164b348919 156 // These pre-empt the queue, and are useful for fast, high priority services.
MrBedfordVan 0:b9164b348919 157 processingComplete = this->process(evt, true);
MrBedfordVan 0:b9164b348919 158
MrBedfordVan 0:b9164b348919 159 // If we've already processed all event handlers, we're all done.
MrBedfordVan 0:b9164b348919 160 // No need to queue the event.
MrBedfordVan 0:b9164b348919 161 if (processingComplete)
MrBedfordVan 0:b9164b348919 162 return;
MrBedfordVan 0:b9164b348919 163
MrBedfordVan 0:b9164b348919 164 // If we need to queue, but there is no space, then there's nothg we can do.
MrBedfordVan 0:b9164b348919 165 if (queueLength >= MESSAGE_BUS_LISTENER_MAX_QUEUE_DEPTH)
MrBedfordVan 0:b9164b348919 166 return;
MrBedfordVan 0:b9164b348919 167
MrBedfordVan 0:b9164b348919 168 // Otherwise, we need to queue this event for later processing...
MrBedfordVan 0:b9164b348919 169 // We queue this event at the tail of the queue at the point where we entered queueEvent()
MrBedfordVan 0:b9164b348919 170 // This is important as the processing above *may* have generated further events, and
MrBedfordVan 0:b9164b348919 171 // we want to maintain ordering of events.
MrBedfordVan 0:b9164b348919 172 MicroBitEventQueueItem *item = new MicroBitEventQueueItem(evt);
MrBedfordVan 0:b9164b348919 173
MrBedfordVan 0:b9164b348919 174 // The queue was empty when we entered this function, so queue our event at the start of the queue.
MrBedfordVan 0:b9164b348919 175 __disable_irq();
MrBedfordVan 0:b9164b348919 176
MrBedfordVan 0:b9164b348919 177 if (prev == NULL)
MrBedfordVan 0:b9164b348919 178 {
MrBedfordVan 0:b9164b348919 179 item->next = evt_queue_head;
MrBedfordVan 0:b9164b348919 180 evt_queue_head = item;
MrBedfordVan 0:b9164b348919 181 }
MrBedfordVan 0:b9164b348919 182 else
MrBedfordVan 0:b9164b348919 183 {
MrBedfordVan 0:b9164b348919 184 item->next = prev->next;
MrBedfordVan 0:b9164b348919 185 prev->next = item;
MrBedfordVan 0:b9164b348919 186 }
MrBedfordVan 0:b9164b348919 187
MrBedfordVan 0:b9164b348919 188 if (item->next == NULL)
MrBedfordVan 0:b9164b348919 189 evt_queue_tail = item;
MrBedfordVan 0:b9164b348919 190
MrBedfordVan 0:b9164b348919 191 queueLength++;
MrBedfordVan 0:b9164b348919 192
MrBedfordVan 0:b9164b348919 193 __enable_irq();
MrBedfordVan 0:b9164b348919 194 }
MrBedfordVan 0:b9164b348919 195
MrBedfordVan 0:b9164b348919 196 /**
MrBedfordVan 0:b9164b348919 197 * Extract the next event from the front of the event queue (if present).
MrBedfordVan 0:b9164b348919 198 *
MrBedfordVan 0:b9164b348919 199 * @return a pointer to the MicroBitEventQueueItem that is at the head of the list.
MrBedfordVan 0:b9164b348919 200 */
MrBedfordVan 0:b9164b348919 201 MicroBitEventQueueItem* MicroBitMessageBus::dequeueEvent()
MrBedfordVan 0:b9164b348919 202 {
MrBedfordVan 0:b9164b348919 203 MicroBitEventQueueItem *item = NULL;
MrBedfordVan 0:b9164b348919 204
MrBedfordVan 0:b9164b348919 205 __disable_irq();
MrBedfordVan 0:b9164b348919 206
MrBedfordVan 0:b9164b348919 207 if (evt_queue_head != NULL)
MrBedfordVan 0:b9164b348919 208 {
MrBedfordVan 0:b9164b348919 209 item = evt_queue_head;
MrBedfordVan 0:b9164b348919 210 evt_queue_head = item->next;
MrBedfordVan 0:b9164b348919 211
MrBedfordVan 0:b9164b348919 212 if (evt_queue_head == NULL)
MrBedfordVan 0:b9164b348919 213 evt_queue_tail = NULL;
MrBedfordVan 0:b9164b348919 214
MrBedfordVan 0:b9164b348919 215 queueLength--;
MrBedfordVan 0:b9164b348919 216 }
MrBedfordVan 0:b9164b348919 217
MrBedfordVan 0:b9164b348919 218 __enable_irq();
MrBedfordVan 0:b9164b348919 219
MrBedfordVan 0:b9164b348919 220
MrBedfordVan 0:b9164b348919 221 return item;
MrBedfordVan 0:b9164b348919 222 }
MrBedfordVan 0:b9164b348919 223
MrBedfordVan 0:b9164b348919 224 /**
MrBedfordVan 0:b9164b348919 225 * Cleanup any MicroBitListeners marked for deletion from the list.
MrBedfordVan 0:b9164b348919 226 *
MrBedfordVan 0:b9164b348919 227 * @return The number of listeners removed from the list.
MrBedfordVan 0:b9164b348919 228 */
MrBedfordVan 0:b9164b348919 229 int MicroBitMessageBus::deleteMarkedListeners()
MrBedfordVan 0:b9164b348919 230 {
MrBedfordVan 0:b9164b348919 231 MicroBitListener *l, *p;
MrBedfordVan 0:b9164b348919 232 int removed = 0;
MrBedfordVan 0:b9164b348919 233
MrBedfordVan 0:b9164b348919 234 l = listeners;
MrBedfordVan 0:b9164b348919 235 p = NULL;
MrBedfordVan 0:b9164b348919 236
MrBedfordVan 0:b9164b348919 237 // Walk this list of event handlers. Delete any that match the given listener.
MrBedfordVan 0:b9164b348919 238 while (l != NULL)
MrBedfordVan 0:b9164b348919 239 {
MrBedfordVan 0:b9164b348919 240 if ((l->flags & MESSAGE_BUS_LISTENER_DELETING) && !(l->flags & MESSAGE_BUS_LISTENER_BUSY))
MrBedfordVan 0:b9164b348919 241 {
MrBedfordVan 0:b9164b348919 242 if (p == NULL)
MrBedfordVan 0:b9164b348919 243 listeners = l->next;
MrBedfordVan 0:b9164b348919 244 else
MrBedfordVan 0:b9164b348919 245 p->next = l->next;
MrBedfordVan 0:b9164b348919 246
MrBedfordVan 0:b9164b348919 247 // delete the listener.
MrBedfordVan 0:b9164b348919 248 MicroBitListener *t = l;
MrBedfordVan 0:b9164b348919 249 l = l->next;
MrBedfordVan 0:b9164b348919 250
MrBedfordVan 0:b9164b348919 251 delete t;
MrBedfordVan 0:b9164b348919 252 removed++;
MrBedfordVan 0:b9164b348919 253
MrBedfordVan 0:b9164b348919 254 continue;
MrBedfordVan 0:b9164b348919 255 }
MrBedfordVan 0:b9164b348919 256
MrBedfordVan 0:b9164b348919 257 p = l;
MrBedfordVan 0:b9164b348919 258 l = l->next;
MrBedfordVan 0:b9164b348919 259 }
MrBedfordVan 0:b9164b348919 260
MrBedfordVan 0:b9164b348919 261 return removed;
MrBedfordVan 0:b9164b348919 262 }
MrBedfordVan 0:b9164b348919 263
MrBedfordVan 0:b9164b348919 264 /**
MrBedfordVan 0:b9164b348919 265 * Periodic callback from MicroBit.
MrBedfordVan 0:b9164b348919 266 *
MrBedfordVan 0:b9164b348919 267 * Process at least one event from the event queue, if it is not empty.
MrBedfordVan 0:b9164b348919 268 * We then continue processing events until something appears on the runqueue.
MrBedfordVan 0:b9164b348919 269 */
MrBedfordVan 0:b9164b348919 270 void MicroBitMessageBus::idleTick()
MrBedfordVan 0:b9164b348919 271 {
MrBedfordVan 0:b9164b348919 272 // Clear out any listeners marked for deletion
MrBedfordVan 0:b9164b348919 273 this->deleteMarkedListeners();
MrBedfordVan 0:b9164b348919 274
MrBedfordVan 0:b9164b348919 275 MicroBitEventQueueItem *item = this->dequeueEvent();
MrBedfordVan 0:b9164b348919 276
MrBedfordVan 0:b9164b348919 277 // Whilst there are events to process and we have no useful other work to do, pull them off the queue and process them.
MrBedfordVan 0:b9164b348919 278 while (item)
MrBedfordVan 0:b9164b348919 279 {
MrBedfordVan 0:b9164b348919 280 // send the event to all standard event listeners.
MrBedfordVan 0:b9164b348919 281 this->process(item->evt);
MrBedfordVan 0:b9164b348919 282
MrBedfordVan 0:b9164b348919 283 // Free the queue item.
MrBedfordVan 0:b9164b348919 284 delete item;
MrBedfordVan 0:b9164b348919 285
MrBedfordVan 0:b9164b348919 286 // If we have created some useful work to do, we stop processing.
MrBedfordVan 0:b9164b348919 287 // This helps to minimise the number of blocked fibers we create at any point in time, therefore
MrBedfordVan 0:b9164b348919 288 // also reducing the RAM footprint.
MrBedfordVan 0:b9164b348919 289 if(!scheduler_runqueue_empty())
MrBedfordVan 0:b9164b348919 290 break;
MrBedfordVan 0:b9164b348919 291
MrBedfordVan 0:b9164b348919 292 // Pull the next event to process, if there is one.
MrBedfordVan 0:b9164b348919 293 item = this->dequeueEvent();
MrBedfordVan 0:b9164b348919 294 }
MrBedfordVan 0:b9164b348919 295 }
MrBedfordVan 0:b9164b348919 296
MrBedfordVan 0:b9164b348919 297 /**
MrBedfordVan 0:b9164b348919 298 * Queues the given event to be sent to all registered recipients.
MrBedfordVan 0:b9164b348919 299 *
MrBedfordVan 0:b9164b348919 300 * @param evt The event to send.
MrBedfordVan 0:b9164b348919 301 *
MrBedfordVan 0:b9164b348919 302 * @code
MrBedfordVan 0:b9164b348919 303 * MicroBitMessageBus bus;
MrBedfordVan 0:b9164b348919 304 *
MrBedfordVan 0:b9164b348919 305 * // Creates and sends the MicroBitEvent using bus.
MrBedfordVan 0:b9164b348919 306 * MicrobitEvent evt(MICROBIT_ID_BUTTON_A, MICROBIT_BUTTON_EVT_CLICK);
MrBedfordVan 0:b9164b348919 307 *
MrBedfordVan 0:b9164b348919 308 * // Creates the MicrobitEvent, but delays the sending of that event.
MrBedfordVan 0:b9164b348919 309 * MicrobitEvent evt1(MICROBIT_ID_BUTTON_A, MICROBIT_BUTTON_EVT_CLICK, CREATE_ONLY);
MrBedfordVan 0:b9164b348919 310 *
MrBedfordVan 0:b9164b348919 311 * bus.send(evt1);
MrBedfordVan 0:b9164b348919 312 *
MrBedfordVan 0:b9164b348919 313 * // This has the same effect!
MrBedfordVan 0:b9164b348919 314 * evt1.fire()
MrBedfordVan 0:b9164b348919 315 * @endcode
MrBedfordVan 0:b9164b348919 316 */
MrBedfordVan 0:b9164b348919 317 int MicroBitMessageBus::send(MicroBitEvent evt)
MrBedfordVan 0:b9164b348919 318 {
MrBedfordVan 0:b9164b348919 319 // We simply queue processing of the event until we're scheduled in normal thread context.
MrBedfordVan 0:b9164b348919 320 // We do this to avoid the possibility of executing event handler code in IRQ context, which may bring
MrBedfordVan 0:b9164b348919 321 // hidden race conditions to kids code. Queuing all events ensures causal ordering (total ordering in fact).
MrBedfordVan 0:b9164b348919 322 this->queueEvent(evt);
MrBedfordVan 0:b9164b348919 323 return MICROBIT_OK;
MrBedfordVan 0:b9164b348919 324 }
MrBedfordVan 0:b9164b348919 325
MrBedfordVan 0:b9164b348919 326 /**
MrBedfordVan 0:b9164b348919 327 * Internal function, used to deliver the given event to all relevant recipients.
MrBedfordVan 0:b9164b348919 328 * Normally, this is called once an event has been removed from the event queue.
MrBedfordVan 0:b9164b348919 329 *
MrBedfordVan 0:b9164b348919 330 * @param evt The event to send.
MrBedfordVan 0:b9164b348919 331 *
MrBedfordVan 0:b9164b348919 332 * @param urgent The type of listeners to process (optional). If set to true, only listeners defined as urgent and non-blocking will be processed
MrBedfordVan 0:b9164b348919 333 * otherwise, all other (standard) listeners will be processed. Defaults to false.
MrBedfordVan 0:b9164b348919 334 *
MrBedfordVan 0:b9164b348919 335 * @return 1 if all matching listeners were processed, 0 if further processing is required.
MrBedfordVan 0:b9164b348919 336 *
MrBedfordVan 0:b9164b348919 337 * @note It is recommended that all external code uses the send() function instead of this function,
MrBedfordVan 0:b9164b348919 338 * or the constructors provided by MicrobitEvent.
MrBedfordVan 0:b9164b348919 339 */
MrBedfordVan 0:b9164b348919 340 int MicroBitMessageBus::process(MicroBitEvent &evt, bool urgent)
MrBedfordVan 0:b9164b348919 341 {
MrBedfordVan 0:b9164b348919 342 MicroBitListener *l;
MrBedfordVan 0:b9164b348919 343 int complete = 1;
MrBedfordVan 0:b9164b348919 344 bool listenerUrgent;
MrBedfordVan 0:b9164b348919 345
MrBedfordVan 0:b9164b348919 346 l = listeners;
MrBedfordVan 0:b9164b348919 347 while (l != NULL)
MrBedfordVan 0:b9164b348919 348 {
MrBedfordVan 0:b9164b348919 349 if((l->id == evt.source || l->id == MICROBIT_ID_ANY) && (l->value == evt.value || l->value == MICROBIT_EVT_ANY))
MrBedfordVan 0:b9164b348919 350 {
MrBedfordVan 0:b9164b348919 351 // If we're running under the fiber scheduler, then derive the THREADING_MODE for the callback based on the
MrBedfordVan 0:b9164b348919 352 // metadata in the listener itself.
MrBedfordVan 0:b9164b348919 353 if (fiber_scheduler_running())
MrBedfordVan 0:b9164b348919 354 listenerUrgent = (l->flags & MESSAGE_BUS_LISTENER_IMMEDIATE) == MESSAGE_BUS_LISTENER_IMMEDIATE;
MrBedfordVan 0:b9164b348919 355 else
MrBedfordVan 0:b9164b348919 356 listenerUrgent = true;
MrBedfordVan 0:b9164b348919 357
MrBedfordVan 0:b9164b348919 358 // If we should process this event hander in this pass, then activate the listener.
MrBedfordVan 0:b9164b348919 359 if(listenerUrgent == urgent && !(l->flags & MESSAGE_BUS_LISTENER_DELETING))
MrBedfordVan 0:b9164b348919 360 {
MrBedfordVan 0:b9164b348919 361 l->evt = evt;
MrBedfordVan 0:b9164b348919 362
MrBedfordVan 0:b9164b348919 363 // OK, if this handler has regisitered itself as non-blocking, we just execute it directly...
MrBedfordVan 0:b9164b348919 364 // This is normally only done for trusted system components.
MrBedfordVan 0:b9164b348919 365 // Otherwise, we invoke it in a 'fork on block' context, that will automatically create a fiber
MrBedfordVan 0:b9164b348919 366 // should the event handler attempt a blocking operation, but doesn't have the overhead
MrBedfordVan 0:b9164b348919 367 // of creating a fiber needlessly. (cool huh?)
MrBedfordVan 0:b9164b348919 368 if (l->flags & MESSAGE_BUS_LISTENER_NONBLOCKING || !fiber_scheduler_running())
MrBedfordVan 0:b9164b348919 369 async_callback(l);
MrBedfordVan 0:b9164b348919 370 else
MrBedfordVan 0:b9164b348919 371 invoke(async_callback, l);
MrBedfordVan 0:b9164b348919 372 }
MrBedfordVan 0:b9164b348919 373 else
MrBedfordVan 0:b9164b348919 374 {
MrBedfordVan 0:b9164b348919 375 complete = 0;
MrBedfordVan 0:b9164b348919 376 }
MrBedfordVan 0:b9164b348919 377 }
MrBedfordVan 0:b9164b348919 378
MrBedfordVan 0:b9164b348919 379 l = l->next;
MrBedfordVan 0:b9164b348919 380 }
MrBedfordVan 0:b9164b348919 381
MrBedfordVan 0:b9164b348919 382 return complete;
MrBedfordVan 0:b9164b348919 383 }
MrBedfordVan 0:b9164b348919 384
MrBedfordVan 0:b9164b348919 385 /**
MrBedfordVan 0:b9164b348919 386 * Add the given MicroBitListener to the list of event handlers, unconditionally.
MrBedfordVan 0:b9164b348919 387 *
MrBedfordVan 0:b9164b348919 388 * @param listener The MicroBitListener to add.
MrBedfordVan 0:b9164b348919 389 *
MrBedfordVan 0:b9164b348919 390 * @return MICROBIT_OK if the listener is valid, MICROBIT_INVALID_PARAMETER otherwise.
MrBedfordVan 0:b9164b348919 391 */
MrBedfordVan 0:b9164b348919 392 int MicroBitMessageBus::add(MicroBitListener *newListener)
MrBedfordVan 0:b9164b348919 393 {
MrBedfordVan 0:b9164b348919 394 MicroBitListener *l, *p;
MrBedfordVan 0:b9164b348919 395 int methodCallback;
MrBedfordVan 0:b9164b348919 396
MrBedfordVan 0:b9164b348919 397 //handler can't be NULL!
MrBedfordVan 0:b9164b348919 398 if (newListener == NULL)
MrBedfordVan 0:b9164b348919 399 return MICROBIT_INVALID_PARAMETER;
MrBedfordVan 0:b9164b348919 400
MrBedfordVan 0:b9164b348919 401 l = listeners;
MrBedfordVan 0:b9164b348919 402
MrBedfordVan 0:b9164b348919 403 // Firstly, we treat a listener as an idempotent operation. Ensure we don't already have this handler
MrBedfordVan 0:b9164b348919 404 // registered in a that will already capture these events. If we do, silently ignore.
MrBedfordVan 0:b9164b348919 405
MrBedfordVan 0:b9164b348919 406 // We always check the ID, VALUE and CB_METHOD fields.
MrBedfordVan 0:b9164b348919 407 // If we have a callback to a method, check the cb_method class. Otherwise, the cb function point is sufficient.
MrBedfordVan 0:b9164b348919 408 while (l != NULL)
MrBedfordVan 0:b9164b348919 409 {
MrBedfordVan 0:b9164b348919 410 methodCallback = (newListener->flags & MESSAGE_BUS_LISTENER_METHOD) && (l->flags & MESSAGE_BUS_LISTENER_METHOD);
MrBedfordVan 0:b9164b348919 411
MrBedfordVan 0:b9164b348919 412 if (l->id == newListener->id && l->value == newListener->value && (methodCallback ? *l->cb_method == *newListener->cb_method : l->cb == newListener->cb))
MrBedfordVan 0:b9164b348919 413 {
MrBedfordVan 0:b9164b348919 414 // We have a perfect match for this event listener already registered.
MrBedfordVan 0:b9164b348919 415 // If it's marked for deletion, we simply resurrect the listener, and we're done.
MrBedfordVan 0:b9164b348919 416 // Either way, we return an error code, as the *new* listener should be released...
MrBedfordVan 0:b9164b348919 417 if(l->flags & MESSAGE_BUS_LISTENER_DELETING)
MrBedfordVan 0:b9164b348919 418 l->flags &= ~MESSAGE_BUS_LISTENER_DELETING;
MrBedfordVan 0:b9164b348919 419
MrBedfordVan 0:b9164b348919 420 return MICROBIT_NOT_SUPPORTED;
MrBedfordVan 0:b9164b348919 421 }
MrBedfordVan 0:b9164b348919 422
MrBedfordVan 0:b9164b348919 423 l = l->next;
MrBedfordVan 0:b9164b348919 424 }
MrBedfordVan 0:b9164b348919 425
MrBedfordVan 0:b9164b348919 426 // We have a valid, new event handler. Add it to the list.
MrBedfordVan 0:b9164b348919 427 // if listeners is null - we can automatically add this listener to the list at the beginning...
MrBedfordVan 0:b9164b348919 428 if (listeners == NULL)
MrBedfordVan 0:b9164b348919 429 {
MrBedfordVan 0:b9164b348919 430 listeners = newListener;
MrBedfordVan 0:b9164b348919 431 MicroBitEvent(MICROBIT_ID_MESSAGE_BUS_LISTENER, newListener->id);
MrBedfordVan 0:b9164b348919 432
MrBedfordVan 0:b9164b348919 433 return MICROBIT_OK;
MrBedfordVan 0:b9164b348919 434 }
MrBedfordVan 0:b9164b348919 435
MrBedfordVan 0:b9164b348919 436 // We maintain an ordered list of listeners.
MrBedfordVan 0:b9164b348919 437 // The chain is held stictly in increasing order of ID (first level), then value code (second level).
MrBedfordVan 0:b9164b348919 438 // Find the correct point in the chain for this event.
MrBedfordVan 0:b9164b348919 439 // Adding a listener is a rare occurance, so we just walk the list...
MrBedfordVan 0:b9164b348919 440
MrBedfordVan 0:b9164b348919 441 p = listeners;
MrBedfordVan 0:b9164b348919 442 l = listeners;
MrBedfordVan 0:b9164b348919 443
MrBedfordVan 0:b9164b348919 444 while (l != NULL && l->id < newListener->id)
MrBedfordVan 0:b9164b348919 445 {
MrBedfordVan 0:b9164b348919 446 p = l;
MrBedfordVan 0:b9164b348919 447 l = l->next;
MrBedfordVan 0:b9164b348919 448 }
MrBedfordVan 0:b9164b348919 449
MrBedfordVan 0:b9164b348919 450 while (l != NULL && l->id == newListener->id && l->value < newListener->value)
MrBedfordVan 0:b9164b348919 451 {
MrBedfordVan 0:b9164b348919 452 p = l;
MrBedfordVan 0:b9164b348919 453 l = l->next;
MrBedfordVan 0:b9164b348919 454 }
MrBedfordVan 0:b9164b348919 455
MrBedfordVan 0:b9164b348919 456 //add at front of list
MrBedfordVan 0:b9164b348919 457 if (p == listeners && (newListener->id < p->id || (p->id == newListener->id && p->value > newListener->value)))
MrBedfordVan 0:b9164b348919 458 {
MrBedfordVan 0:b9164b348919 459 newListener->next = p;
MrBedfordVan 0:b9164b348919 460
MrBedfordVan 0:b9164b348919 461 //this new listener is now the front!
MrBedfordVan 0:b9164b348919 462 listeners = newListener;
MrBedfordVan 0:b9164b348919 463 }
MrBedfordVan 0:b9164b348919 464
MrBedfordVan 0:b9164b348919 465 //add after p
MrBedfordVan 0:b9164b348919 466 else
MrBedfordVan 0:b9164b348919 467 {
MrBedfordVan 0:b9164b348919 468 newListener->next = p->next;
MrBedfordVan 0:b9164b348919 469 p->next = newListener;
MrBedfordVan 0:b9164b348919 470 }
MrBedfordVan 0:b9164b348919 471
MrBedfordVan 0:b9164b348919 472 MicroBitEvent(MICROBIT_ID_MESSAGE_BUS_LISTENER, newListener->id);
MrBedfordVan 0:b9164b348919 473 return MICROBIT_OK;
MrBedfordVan 0:b9164b348919 474 }
MrBedfordVan 0:b9164b348919 475
MrBedfordVan 0:b9164b348919 476 /**
MrBedfordVan 0:b9164b348919 477 * Remove the given MicroBitListener from the list of event handlers.
MrBedfordVan 0:b9164b348919 478 *
MrBedfordVan 0:b9164b348919 479 * @param listener The MicroBitListener to remove.
MrBedfordVan 0:b9164b348919 480 *
MrBedfordVan 0:b9164b348919 481 * @return MICROBIT_OK if the listener is valid, MICROBIT_INVALID_PARAMETER otherwise.
MrBedfordVan 0:b9164b348919 482 */
MrBedfordVan 0:b9164b348919 483 int MicroBitMessageBus::remove(MicroBitListener *listener)
MrBedfordVan 0:b9164b348919 484 {
MrBedfordVan 0:b9164b348919 485 MicroBitListener *l;
MrBedfordVan 0:b9164b348919 486 int removed = 0;
MrBedfordVan 0:b9164b348919 487
MrBedfordVan 0:b9164b348919 488 //handler can't be NULL!
MrBedfordVan 0:b9164b348919 489 if (listener == NULL)
MrBedfordVan 0:b9164b348919 490 return MICROBIT_INVALID_PARAMETER;
MrBedfordVan 0:b9164b348919 491
MrBedfordVan 0:b9164b348919 492 l = listeners;
MrBedfordVan 0:b9164b348919 493
MrBedfordVan 0:b9164b348919 494 // Walk this list of event handlers. Delete any that match the given listener.
MrBedfordVan 0:b9164b348919 495 while (l != NULL)
MrBedfordVan 0:b9164b348919 496 {
MrBedfordVan 0:b9164b348919 497 if ((listener->flags & MESSAGE_BUS_LISTENER_METHOD) == (l->flags & MESSAGE_BUS_LISTENER_METHOD))
MrBedfordVan 0:b9164b348919 498 {
MrBedfordVan 0:b9164b348919 499 if(((listener->flags & MESSAGE_BUS_LISTENER_METHOD) && (*l->cb_method == *listener->cb_method)) ||
MrBedfordVan 0:b9164b348919 500 ((!(listener->flags & MESSAGE_BUS_LISTENER_METHOD) && l->cb == listener->cb)))
MrBedfordVan 0:b9164b348919 501 {
MrBedfordVan 0:b9164b348919 502 if ((listener->id == MICROBIT_ID_ANY || listener->id == l->id) && (listener->value == MICROBIT_EVT_ANY || listener->value == l->value))
MrBedfordVan 0:b9164b348919 503 {
MrBedfordVan 0:b9164b348919 504 // Found a match. mark this to be removed from the list.
MrBedfordVan 0:b9164b348919 505 l->flags |= MESSAGE_BUS_LISTENER_DELETING;
MrBedfordVan 0:b9164b348919 506 removed++;
MrBedfordVan 0:b9164b348919 507 }
MrBedfordVan 0:b9164b348919 508 }
MrBedfordVan 0:b9164b348919 509 }
MrBedfordVan 0:b9164b348919 510
MrBedfordVan 0:b9164b348919 511 l = l->next;
MrBedfordVan 0:b9164b348919 512 }
MrBedfordVan 0:b9164b348919 513
MrBedfordVan 0:b9164b348919 514 if (removed > 0)
MrBedfordVan 0:b9164b348919 515 return MICROBIT_OK;
MrBedfordVan 0:b9164b348919 516 else
MrBedfordVan 0:b9164b348919 517 return MICROBIT_INVALID_PARAMETER;
MrBedfordVan 0:b9164b348919 518 }
MrBedfordVan 0:b9164b348919 519
MrBedfordVan 0:b9164b348919 520 /**
MrBedfordVan 0:b9164b348919 521 * Returns the microBitListener with the given position in our list.
MrBedfordVan 0:b9164b348919 522 *
MrBedfordVan 0:b9164b348919 523 * @param n The position in the list to return.
MrBedfordVan 0:b9164b348919 524 *
MrBedfordVan 0:b9164b348919 525 * @return the MicroBitListener at postion n in the list, or NULL if the position is invalid.
MrBedfordVan 0:b9164b348919 526 */
MrBedfordVan 0:b9164b348919 527 MicroBitListener* MicroBitMessageBus::elementAt(int n)
MrBedfordVan 0:b9164b348919 528 {
MrBedfordVan 0:b9164b348919 529 MicroBitListener *l = listeners;
MrBedfordVan 0:b9164b348919 530
MrBedfordVan 0:b9164b348919 531 while (n > 0)
MrBedfordVan 0:b9164b348919 532 {
MrBedfordVan 0:b9164b348919 533 if (l == NULL)
MrBedfordVan 0:b9164b348919 534 return NULL;
MrBedfordVan 0:b9164b348919 535
MrBedfordVan 0:b9164b348919 536 n--;
MrBedfordVan 0:b9164b348919 537 l = l->next;
MrBedfordVan 0:b9164b348919 538 }
MrBedfordVan 0:b9164b348919 539
MrBedfordVan 0:b9164b348919 540 return l;
MrBedfordVan 0:b9164b348919 541 }
MrBedfordVan 0:b9164b348919 542
MrBedfordVan 0:b9164b348919 543 /**
MrBedfordVan 0:b9164b348919 544 * Destructor for MicroBitMessageBus, where we deregister this instance from the array of fiber components.
MrBedfordVan 0:b9164b348919 545 */
MrBedfordVan 0:b9164b348919 546 MicroBitMessageBus::~MicroBitMessageBus()
MrBedfordVan 0:b9164b348919 547 {
MrBedfordVan 0:b9164b348919 548 fiber_remove_idle_component(this);
MrBedfordVan 0:b9164b348919 549 }