Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Fork of MicroBitDALImageRewrite by
MicroBitMessageBus.cpp@2:6597fe50dc94, 2015-04-28 (annotated)
- Committer:
- finneyj
- Date:
- Tue Apr 28 18:32:34 2015 +0000
- Revision:
- 2:6597fe50dc94
- Parent:
- 1:3e0360107f98
Integrated a Cortex M0 Fiber scheduler.; Updated MessageBus to generated events to decouple event handlers from interrupt context through use of scheduled fibers.
Who changed what in which revision?
| User | Revision | Line number | New contents of line |
|---|---|---|---|
| finneyj | 1:3e0360107f98 | 1 | /** |
| finneyj | 1:3e0360107f98 | 2 | * Class definition for a MicroBitImage. |
| finneyj | 1:3e0360107f98 | 3 | * |
| finneyj | 1:3e0360107f98 | 4 | * An MicroBitImage is a simple bitmap representation of an image. |
| finneyj | 1:3e0360107f98 | 5 | */ |
| finneyj | 1:3e0360107f98 | 6 | |
| finneyj | 1:3e0360107f98 | 7 | #include "MicroBitMessageBus.h" |
| finneyj | 2:6597fe50dc94 | 8 | #include "MicroBitFiber.h" |
| finneyj | 1:3e0360107f98 | 9 | |
| finneyj | 1:3e0360107f98 | 10 | /** |
| finneyj | 1:3e0360107f98 | 11 | * Constructor. |
| finneyj | 1:3e0360107f98 | 12 | * Create a new Message Bus Listener. |
| finneyj | 1:3e0360107f98 | 13 | */ |
| finneyj | 2:6597fe50dc94 | 14 | MicroBitListener::MicroBitListener(int id, int value, void (*handler)(void)) |
| finneyj | 1:3e0360107f98 | 15 | { |
| finneyj | 1:3e0360107f98 | 16 | this->id = id; |
| finneyj | 1:3e0360107f98 | 17 | this->value = value; |
| finneyj | 1:3e0360107f98 | 18 | this->cb = handler; |
| finneyj | 1:3e0360107f98 | 19 | this->next = NULL; |
| finneyj | 1:3e0360107f98 | 20 | } |
| finneyj | 1:3e0360107f98 | 21 | |
| finneyj | 1:3e0360107f98 | 22 | /** |
| finneyj | 1:3e0360107f98 | 23 | * Constructor. |
| finneyj | 1:3e0360107f98 | 24 | * Create a new Message Bus. |
| finneyj | 1:3e0360107f98 | 25 | */ |
| finneyj | 1:3e0360107f98 | 26 | MicroBitMessageBus::MicroBitMessageBus() |
| finneyj | 1:3e0360107f98 | 27 | { |
| finneyj | 1:3e0360107f98 | 28 | this->listeners = NULL; |
| finneyj | 1:3e0360107f98 | 29 | this->seq = 0; |
| finneyj | 1:3e0360107f98 | 30 | } |
| finneyj | 1:3e0360107f98 | 31 | |
| finneyj | 1:3e0360107f98 | 32 | /** |
| finneyj | 1:3e0360107f98 | 33 | * Send the given event to all regstered recipients. |
| finneyj | 1:3e0360107f98 | 34 | * |
| finneyj | 1:3e0360107f98 | 35 | * @param The event to send. This structure is assumed to be heap allocated, and will |
| finneyj | 1:3e0360107f98 | 36 | * be automatically freed once all recipients have been notified. |
| finneyj | 1:3e0360107f98 | 37 | */ |
| finneyj | 1:3e0360107f98 | 38 | void MicroBitMessageBus::send(MicroBitEvent *evt) |
| finneyj | 1:3e0360107f98 | 39 | { |
| finneyj | 1:3e0360107f98 | 40 | this->send(evt, NULL); |
| finneyj | 1:3e0360107f98 | 41 | } |
| finneyj | 1:3e0360107f98 | 42 | |
| finneyj | 1:3e0360107f98 | 43 | /** |
| finneyj | 1:3e0360107f98 | 44 | * Send the given event to all regstered recipients, using a cached entry to minimize lookups. |
| finneyj | 1:3e0360107f98 | 45 | * This is particularly useful for optimizing sensors that frequently send to the same channel. |
| finneyj | 1:3e0360107f98 | 46 | * |
| finneyj | 1:3e0360107f98 | 47 | * @param evt The event to send. This structure is assumed to be heap allocated, and will |
| finneyj | 1:3e0360107f98 | 48 | * be automatically freed once all recipients have been notified. |
| finneyj | 1:3e0360107f98 | 49 | * @param c Cache entry to reduce lookups for commonly used channels. |
| finneyj | 1:3e0360107f98 | 50 | * |
| finneyj | 1:3e0360107f98 | 51 | * TODO: For now, this is unbuffered. We should consider scheduling events here, and operating |
| finneyj | 1:3e0360107f98 | 52 | * a different thread that empties the queue. This would perhaps provide greater opportunities |
| finneyj | 1:3e0360107f98 | 53 | * for aggregation. |
| finneyj | 1:3e0360107f98 | 54 | */ |
| finneyj | 1:3e0360107f98 | 55 | void MicroBitMessageBus::send(MicroBitEvent *evt, MicroBitMessageBusCache *c) |
| finneyj | 1:3e0360107f98 | 56 | { |
| finneyj | 1:3e0360107f98 | 57 | MicroBitListener *l; |
| finneyj | 1:3e0360107f98 | 58 | MicroBitListener *start; |
| finneyj | 1:3e0360107f98 | 59 | |
| finneyj | 1:3e0360107f98 | 60 | // Find the start of the sublist where we'll send this event. |
| finneyj | 1:3e0360107f98 | 61 | // Ideally, we'll have a valid, cached entry. Use it if we do. |
| finneyj | 1:3e0360107f98 | 62 | if ( c != NULL && c->seq == this->seq) |
| finneyj | 1:3e0360107f98 | 63 | { |
| finneyj | 1:3e0360107f98 | 64 | l = c->ptr; |
| finneyj | 1:3e0360107f98 | 65 | } |
| finneyj | 1:3e0360107f98 | 66 | else |
| finneyj | 1:3e0360107f98 | 67 | { |
| finneyj | 1:3e0360107f98 | 68 | l = listeners; |
| finneyj | 1:3e0360107f98 | 69 | while (l != NULL && l->id != evt->source) |
| finneyj | 1:3e0360107f98 | 70 | l = l->next; |
| finneyj | 1:3e0360107f98 | 71 | } |
| finneyj | 1:3e0360107f98 | 72 | |
| finneyj | 1:3e0360107f98 | 73 | start = l; |
| finneyj | 1:3e0360107f98 | 74 | |
| finneyj | 1:3e0360107f98 | 75 | // Now, send the event to all listeners registered for this event. |
| finneyj | 1:3e0360107f98 | 76 | while (l != NULL && l->id == evt->source) |
| finneyj | 1:3e0360107f98 | 77 | { |
| finneyj | 1:3e0360107f98 | 78 | if(l->value == MICROBIT_BUS_VALUE_ANY || l->value == evt->value) |
| finneyj | 2:6597fe50dc94 | 79 | //l->cb(); |
| finneyj | 2:6597fe50dc94 | 80 | create_fiber(l->cb); |
| finneyj | 1:3e0360107f98 | 81 | |
| finneyj | 1:3e0360107f98 | 82 | l = l->next; |
| finneyj | 1:3e0360107f98 | 83 | } |
| finneyj | 1:3e0360107f98 | 84 | |
| finneyj | 1:3e0360107f98 | 85 | // Next, send to any listeners registered for ALL event sources. |
| finneyj | 1:3e0360107f98 | 86 | l = listeners; |
| finneyj | 1:3e0360107f98 | 87 | while (l != NULL && l->id == MICROBIT_BUS_ID_ANY) |
| finneyj | 1:3e0360107f98 | 88 | { |
| finneyj | 2:6597fe50dc94 | 89 | //l->cb(); |
| finneyj | 2:6597fe50dc94 | 90 | create_fiber(l->cb); |
| finneyj | 1:3e0360107f98 | 91 | l = l->next; |
| finneyj | 1:3e0360107f98 | 92 | } |
| finneyj | 1:3e0360107f98 | 93 | |
| finneyj | 1:3e0360107f98 | 94 | // Finally, if we were given a cached entry that's now invalid, update it. |
| finneyj | 1:3e0360107f98 | 95 | if ( c != NULL && c->seq != this->seq) |
| finneyj | 1:3e0360107f98 | 96 | { |
| finneyj | 1:3e0360107f98 | 97 | c->ptr = start; |
| finneyj | 1:3e0360107f98 | 98 | c->seq = this->seq; |
| finneyj | 1:3e0360107f98 | 99 | } |
| finneyj | 1:3e0360107f98 | 100 | } |
| finneyj | 1:3e0360107f98 | 101 | |
| finneyj | 1:3e0360107f98 | 102 | /** |
| finneyj | 1:3e0360107f98 | 103 | * Register a listener function. |
| finneyj | 1:3e0360107f98 | 104 | * |
| finneyj | 1:3e0360107f98 | 105 | * @param id The source of messages to listen for. Events sent from any other IDs will be filtered. |
| finneyj | 1:3e0360107f98 | 106 | * Use MICROBIT_ID_ANY to receive events from all components. |
| finneyj | 1:3e0360107f98 | 107 | * |
| finneyj | 1:3e0360107f98 | 108 | * @param value The value of messages to listen for. Events with any other values will be filtered. |
| finneyj | 1:3e0360107f98 | 109 | * Use MICROBIT_VALUE_ANY to receive events of any value. |
| finneyj | 1:3e0360107f98 | 110 | * |
| finneyj | 1:3e0360107f98 | 111 | * @param hander The function to call when an event is received. |
| finneyj | 1:3e0360107f98 | 112 | * |
| finneyj | 1:3e0360107f98 | 113 | * TODO: We currently don't support C++ member functions as callbacks, which we should. |
| finneyj | 1:3e0360107f98 | 114 | */ |
| finneyj | 1:3e0360107f98 | 115 | |
| finneyj | 2:6597fe50dc94 | 116 | void MicroBitMessageBus::listen(int id, int value, void (*handler)(void)) |
| finneyj | 1:3e0360107f98 | 117 | { |
| finneyj | 1:3e0360107f98 | 118 | MicroBitListener *l, *p; |
| finneyj | 1:3e0360107f98 | 119 | MicroBitListener *newListener = new MicroBitListener(id, value, handler); |
| finneyj | 1:3e0360107f98 | 120 | |
| finneyj | 1:3e0360107f98 | 121 | // Firstly, we treat a listener as an idempotent operation. Ensure we don't already have this handler |
| finneyj | 1:3e0360107f98 | 122 | // registered in a that will already capture these events. If we do, silently ignore. |
| finneyj | 1:3e0360107f98 | 123 | l = listeners; |
| finneyj | 1:3e0360107f98 | 124 | |
| finneyj | 1:3e0360107f98 | 125 | while (l != NULL && l->id <= id) |
| finneyj | 1:3e0360107f98 | 126 | { |
| finneyj | 1:3e0360107f98 | 127 | if (l->cb == handler && (l->id == id || l->id == MICROBIT_BUS_ID_ANY) && (l->value == value || l->value == MICROBIT_BUS_VALUE_ANY)) |
| finneyj | 1:3e0360107f98 | 128 | return; |
| finneyj | 1:3e0360107f98 | 129 | |
| finneyj | 1:3e0360107f98 | 130 | l = l->next; |
| finneyj | 1:3e0360107f98 | 131 | } |
| finneyj | 1:3e0360107f98 | 132 | |
| finneyj | 1:3e0360107f98 | 133 | // Maintain an ordered list of listeners. |
| finneyj | 1:3e0360107f98 | 134 | // Chain is held stictly in increasing order of ID (first level), then value code (second level). |
| finneyj | 1:3e0360107f98 | 135 | // Find the correct point in the chain for this event. |
| finneyj | 1:3e0360107f98 | 136 | // Adding a listener is a rare occurance, so we just walk the list. |
| finneyj | 1:3e0360107f98 | 137 | p = NULL; |
| finneyj | 1:3e0360107f98 | 138 | l = listeners; |
| finneyj | 1:3e0360107f98 | 139 | |
| finneyj | 1:3e0360107f98 | 140 | while (l != NULL && l->id <= id && l->value <= value) |
| finneyj | 1:3e0360107f98 | 141 | { |
| finneyj | 1:3e0360107f98 | 142 | p=l; |
| finneyj | 1:3e0360107f98 | 143 | l = l->next; |
| finneyj | 1:3e0360107f98 | 144 | } |
| finneyj | 1:3e0360107f98 | 145 | |
| finneyj | 1:3e0360107f98 | 146 | // Add the listener at this point in the chain. |
| finneyj | 1:3e0360107f98 | 147 | if (p == NULL) |
| finneyj | 1:3e0360107f98 | 148 | listeners = newListener; |
| finneyj | 1:3e0360107f98 | 149 | else |
| finneyj | 1:3e0360107f98 | 150 | { |
| finneyj | 1:3e0360107f98 | 151 | newListener->next = p->next; |
| finneyj | 1:3e0360107f98 | 152 | p->next = newListener; |
| finneyj | 1:3e0360107f98 | 153 | } |
| finneyj | 1:3e0360107f98 | 154 | |
| finneyj | 1:3e0360107f98 | 155 | // Increase our sequence number and we're done. |
| finneyj | 1:3e0360107f98 | 156 | // This will lazily invalidate any cached entries to the listener list. |
| finneyj | 1:3e0360107f98 | 157 | this->seq++; |
| finneyj | 1:3e0360107f98 | 158 | } |
| finneyj | 1:3e0360107f98 | 159 |
