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