Solution for Bluetooth SIG hands-on training course

Dependencies:   BLE_API mbed-dev-bin nRF51822-bluetooth-mdw

Dependents:   microbit

Fork of microbit-dal-bluetooth-mdw_starter by Martin Woolley

Committer:
bluetooth_mdw
Date:
Fri Mar 24 06:20:38 2017 +0000
Revision:
82:91e085d6ad72
Parent:
41:da05ec75cd5d
Removed comment re: writing initial values to the Attribute Table. Added comments to onDataWritten. Removed solution details.

Who changed what in which revision?

UserRevisionLine numberNew 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 * Functionality definitions for the MicroBit Fiber scheduler.
Jonathan Austin 1:8aa5cdb4ab67 28 *
Jonathan Austin 1:8aa5cdb4ab67 29 * This lightweight, non-preemptive scheduler provides a simple threading mechanism for two main purposes:
Jonathan Austin 1:8aa5cdb4ab67 30 *
Jonathan Austin 1:8aa5cdb4ab67 31 * 1) To provide a clean abstraction for application languages to use when building async behaviour (callbacks).
Jonathan Austin 1:8aa5cdb4ab67 32 * 2) To provide ISR decoupling for EventModel events generated in an ISR context.
Jonathan Austin 1:8aa5cdb4ab67 33 */
Jonathan Austin 1:8aa5cdb4ab67 34 #include "MicroBitConfig.h"
Jonathan Austin 1:8aa5cdb4ab67 35 #include "MicroBitFiber.h"
Jonathan Austin 1:8aa5cdb4ab67 36 #include "MicroBitSystemTimer.h"
Jonathan Austin 1:8aa5cdb4ab67 37
Jonathan Austin 1:8aa5cdb4ab67 38 /*
Jonathan Austin 1:8aa5cdb4ab67 39 * Statically allocated values used to create and destroy Fibers.
Jonathan Austin 1:8aa5cdb4ab67 40 * required to be defined here to allow persistence during context switches.
Jonathan Austin 1:8aa5cdb4ab67 41 */
Jonathan Austin 1:8aa5cdb4ab67 42 Fiber *currentFiber = NULL; // The context in which the current fiber is executing.
Jonathan Austin 1:8aa5cdb4ab67 43 static Fiber *forkedFiber = NULL; // The context in which a newly created child fiber is executing.
Jonathan Austin 1:8aa5cdb4ab67 44 static Fiber *idleFiber = NULL; // the idle task - performs a power efficient sleep, and system maintenance tasks.
Jonathan Austin 1:8aa5cdb4ab67 45
Jonathan Austin 1:8aa5cdb4ab67 46 /*
Jonathan Austin 1:8aa5cdb4ab67 47 * Scheduler state.
Jonathan Austin 1:8aa5cdb4ab67 48 */
Jonathan Austin 1:8aa5cdb4ab67 49 static Fiber *runQueue = NULL; // The list of runnable fibers.
Jonathan Austin 1:8aa5cdb4ab67 50 static Fiber *sleepQueue = NULL; // The list of blocked fibers waiting on a fiber_sleep() operation.
Jonathan Austin 1:8aa5cdb4ab67 51 static Fiber *waitQueue = NULL; // The list of blocked fibers waiting on an event.
Jonathan Austin 1:8aa5cdb4ab67 52 static Fiber *fiberPool = NULL; // Pool of unused fibers, just waiting for a job to do.
Jonathan Austin 1:8aa5cdb4ab67 53
Jonathan Austin 1:8aa5cdb4ab67 54 /*
Jonathan Austin 1:8aa5cdb4ab67 55 * Scheduler wide flags
Jonathan Austin 1:8aa5cdb4ab67 56 */
Jonathan Austin 1:8aa5cdb4ab67 57 static uint8_t fiber_flags = 0;
Jonathan Austin 1:8aa5cdb4ab67 58
Jonathan Austin 1:8aa5cdb4ab67 59
Jonathan Austin 1:8aa5cdb4ab67 60 /*
Jonathan Austin 1:8aa5cdb4ab67 61 * Fibers may perform wait/notify semantics on events. If set, these operations will be permitted on this EventModel.
Jonathan Austin 1:8aa5cdb4ab67 62 */
Jonathan Austin 1:8aa5cdb4ab67 63 static EventModel *messageBus = NULL;
Jonathan Austin 1:8aa5cdb4ab67 64
LancasterUniversity 41:da05ec75cd5d 65 // Array of components which are iterated during idle thread execution.
Jonathan Austin 1:8aa5cdb4ab67 66 static MicroBitComponent* idleThreadComponents[MICROBIT_IDLE_COMPONENTS];
Jonathan Austin 1:8aa5cdb4ab67 67
Jonathan Austin 1:8aa5cdb4ab67 68 /**
Jonathan Austin 1:8aa5cdb4ab67 69 * Utility function to add the currenty running fiber to the given queue.
Jonathan Austin 1:8aa5cdb4ab67 70 *
Jonathan Austin 1:8aa5cdb4ab67 71 * Perform a simple add at the head, to avoid complexity,
Jonathan Austin 1:8aa5cdb4ab67 72 *
Jonathan Austin 1:8aa5cdb4ab67 73 * Queues are normally very short, so maintaining a doubly linked, sorted list typically outweighs the cost of
Jonathan Austin 1:8aa5cdb4ab67 74 * brute force searching.
Jonathan Austin 1:8aa5cdb4ab67 75 *
Jonathan Austin 1:8aa5cdb4ab67 76 * @param f The fiber to add to the queue
Jonathan Austin 1:8aa5cdb4ab67 77 *
Jonathan Austin 1:8aa5cdb4ab67 78 * @param queue The run queue to add the fiber to.
Jonathan Austin 1:8aa5cdb4ab67 79 */
Jonathan Austin 1:8aa5cdb4ab67 80 void queue_fiber(Fiber *f, Fiber **queue)
Jonathan Austin 1:8aa5cdb4ab67 81 {
Jonathan Austin 1:8aa5cdb4ab67 82 __disable_irq();
Jonathan Austin 1:8aa5cdb4ab67 83
Jonathan Austin 1:8aa5cdb4ab67 84 // Record which queue this fiber is on.
Jonathan Austin 1:8aa5cdb4ab67 85 f->queue = queue;
Jonathan Austin 1:8aa5cdb4ab67 86
Jonathan Austin 1:8aa5cdb4ab67 87 // Add the fiber to the tail of the queue. Although this involves scanning the
Jonathan Austin 1:8aa5cdb4ab67 88 // list, it results in fairer scheduling.
Jonathan Austin 1:8aa5cdb4ab67 89 if (*queue == NULL)
Jonathan Austin 1:8aa5cdb4ab67 90 {
Jonathan Austin 1:8aa5cdb4ab67 91 f->next = NULL;
Jonathan Austin 1:8aa5cdb4ab67 92 f->prev = NULL;
Jonathan Austin 1:8aa5cdb4ab67 93 *queue = f;
Jonathan Austin 1:8aa5cdb4ab67 94 }
Jonathan Austin 1:8aa5cdb4ab67 95 else
Jonathan Austin 1:8aa5cdb4ab67 96 {
Jonathan Austin 1:8aa5cdb4ab67 97 // Scan to the end of the queue.
Jonathan Austin 1:8aa5cdb4ab67 98 // We don't maintain a tail pointer to save RAM (queues are nrmally very short).
Jonathan Austin 1:8aa5cdb4ab67 99 Fiber *last = *queue;
Jonathan Austin 1:8aa5cdb4ab67 100
Jonathan Austin 1:8aa5cdb4ab67 101 while (last->next != NULL)
Jonathan Austin 1:8aa5cdb4ab67 102 last = last->next;
Jonathan Austin 1:8aa5cdb4ab67 103
Jonathan Austin 1:8aa5cdb4ab67 104 last->next = f;
Jonathan Austin 1:8aa5cdb4ab67 105 f->prev = last;
Jonathan Austin 1:8aa5cdb4ab67 106 f->next = NULL;
Jonathan Austin 1:8aa5cdb4ab67 107 }
Jonathan Austin 1:8aa5cdb4ab67 108
Jonathan Austin 1:8aa5cdb4ab67 109 __enable_irq();
Jonathan Austin 1:8aa5cdb4ab67 110 }
Jonathan Austin 1:8aa5cdb4ab67 111
Jonathan Austin 1:8aa5cdb4ab67 112 /**
Jonathan Austin 1:8aa5cdb4ab67 113 * Utility function to the given fiber from whichever queue it is currently stored on.
Jonathan Austin 1:8aa5cdb4ab67 114 *
Jonathan Austin 1:8aa5cdb4ab67 115 * @param f the fiber to remove.
Jonathan Austin 1:8aa5cdb4ab67 116 */
Jonathan Austin 1:8aa5cdb4ab67 117 void dequeue_fiber(Fiber *f)
Jonathan Austin 1:8aa5cdb4ab67 118 {
Jonathan Austin 1:8aa5cdb4ab67 119 // If this fiber is already dequeued, nothing the there's nothing to do.
Jonathan Austin 1:8aa5cdb4ab67 120 if (f->queue == NULL)
Jonathan Austin 1:8aa5cdb4ab67 121 return;
Jonathan Austin 1:8aa5cdb4ab67 122
Jonathan Austin 1:8aa5cdb4ab67 123 // Remove this fiber fromm whichever queue it is on.
Jonathan Austin 1:8aa5cdb4ab67 124 __disable_irq();
Jonathan Austin 1:8aa5cdb4ab67 125
Jonathan Austin 1:8aa5cdb4ab67 126 if (f->prev != NULL)
Jonathan Austin 1:8aa5cdb4ab67 127 f->prev->next = f->next;
Jonathan Austin 1:8aa5cdb4ab67 128 else
Jonathan Austin 1:8aa5cdb4ab67 129 *(f->queue) = f->next;
Jonathan Austin 1:8aa5cdb4ab67 130
Jonathan Austin 1:8aa5cdb4ab67 131 if(f->next)
Jonathan Austin 1:8aa5cdb4ab67 132 f->next->prev = f->prev;
Jonathan Austin 1:8aa5cdb4ab67 133
Jonathan Austin 1:8aa5cdb4ab67 134 f->next = NULL;
Jonathan Austin 1:8aa5cdb4ab67 135 f->prev = NULL;
Jonathan Austin 1:8aa5cdb4ab67 136 f->queue = NULL;
Jonathan Austin 1:8aa5cdb4ab67 137
Jonathan Austin 1:8aa5cdb4ab67 138 __enable_irq();
Jonathan Austin 1:8aa5cdb4ab67 139
Jonathan Austin 1:8aa5cdb4ab67 140 }
Jonathan Austin 1:8aa5cdb4ab67 141
Jonathan Austin 1:8aa5cdb4ab67 142 /**
Jonathan Austin 1:8aa5cdb4ab67 143 * Allocates a fiber from the fiber pool if availiable. Otherwise, allocates a new one from the heap.
Jonathan Austin 1:8aa5cdb4ab67 144 */
Jonathan Austin 1:8aa5cdb4ab67 145 Fiber *getFiberContext()
Jonathan Austin 1:8aa5cdb4ab67 146 {
Jonathan Austin 1:8aa5cdb4ab67 147 Fiber *f;
Jonathan Austin 1:8aa5cdb4ab67 148
Jonathan Austin 1:8aa5cdb4ab67 149 __disable_irq();
Jonathan Austin 1:8aa5cdb4ab67 150
Jonathan Austin 1:8aa5cdb4ab67 151 if (fiberPool != NULL)
Jonathan Austin 1:8aa5cdb4ab67 152 {
Jonathan Austin 1:8aa5cdb4ab67 153 f = fiberPool;
Jonathan Austin 1:8aa5cdb4ab67 154 dequeue_fiber(f);
Jonathan Austin 1:8aa5cdb4ab67 155 // dequeue_fiber() exits with irqs enabled, so no need to do this again!
Jonathan Austin 1:8aa5cdb4ab67 156 }
Jonathan Austin 1:8aa5cdb4ab67 157 else
Jonathan Austin 1:8aa5cdb4ab67 158 {
Jonathan Austin 1:8aa5cdb4ab67 159 __enable_irq();
Jonathan Austin 1:8aa5cdb4ab67 160
Jonathan Austin 1:8aa5cdb4ab67 161 f = new Fiber();
Jonathan Austin 1:8aa5cdb4ab67 162
Jonathan Austin 1:8aa5cdb4ab67 163 if (f == NULL)
Jonathan Austin 1:8aa5cdb4ab67 164 return NULL;
Jonathan Austin 1:8aa5cdb4ab67 165
Jonathan Austin 1:8aa5cdb4ab67 166 f->stack_bottom = 0;
Jonathan Austin 1:8aa5cdb4ab67 167 f->stack_top = 0;
Jonathan Austin 1:8aa5cdb4ab67 168 }
Jonathan Austin 1:8aa5cdb4ab67 169
Jonathan Austin 1:8aa5cdb4ab67 170 // Ensure this fiber is in suitable state for reuse.
Jonathan Austin 1:8aa5cdb4ab67 171 f->flags = 0;
Jonathan Austin 1:8aa5cdb4ab67 172 f->tcb.stack_base = CORTEX_M0_STACK_BASE;
Jonathan Austin 1:8aa5cdb4ab67 173
Jonathan Austin 1:8aa5cdb4ab67 174 return f;
Jonathan Austin 1:8aa5cdb4ab67 175 }
Jonathan Austin 1:8aa5cdb4ab67 176
Jonathan Austin 1:8aa5cdb4ab67 177
Jonathan Austin 1:8aa5cdb4ab67 178 /**
Jonathan Austin 1:8aa5cdb4ab67 179 * Initialises the Fiber scheduler.
Jonathan Austin 1:8aa5cdb4ab67 180 * Creates a Fiber context around the calling thread, and adds it to the run queue as the current thread.
Jonathan Austin 1:8aa5cdb4ab67 181 *
Jonathan Austin 1:8aa5cdb4ab67 182 * This function must be called once only from the main thread, and before any other Fiber operation.
Jonathan Austin 1:8aa5cdb4ab67 183 *
Jonathan Austin 1:8aa5cdb4ab67 184 * @param _messageBus An event model, used to direct the priorities of the scheduler.
Jonathan Austin 1:8aa5cdb4ab67 185 */
Jonathan Austin 1:8aa5cdb4ab67 186 void scheduler_init(EventModel &_messageBus)
Jonathan Austin 1:8aa5cdb4ab67 187 {
Jonathan Austin 1:8aa5cdb4ab67 188 // If we're already initialised, then nothing to do.
Jonathan Austin 1:8aa5cdb4ab67 189 if (fiber_scheduler_running())
Jonathan Austin 1:8aa5cdb4ab67 190 return;
Jonathan Austin 1:8aa5cdb4ab67 191
Jonathan Austin 1:8aa5cdb4ab67 192 // Store a reference to the messageBus provided.
Jonathan Austin 1:8aa5cdb4ab67 193 // This parameter will be NULL if we're being run without a message bus.
Jonathan Austin 1:8aa5cdb4ab67 194 messageBus = &_messageBus;
Jonathan Austin 1:8aa5cdb4ab67 195
Jonathan Austin 1:8aa5cdb4ab67 196 // Create a new fiber context
Jonathan Austin 1:8aa5cdb4ab67 197 currentFiber = getFiberContext();
Jonathan Austin 1:8aa5cdb4ab67 198
Jonathan Austin 1:8aa5cdb4ab67 199 // Add ourselves to the run queue.
Jonathan Austin 1:8aa5cdb4ab67 200 queue_fiber(currentFiber, &runQueue);
Jonathan Austin 1:8aa5cdb4ab67 201
Jonathan Austin 1:8aa5cdb4ab67 202 // Create the IDLE fiber.
Jonathan Austin 1:8aa5cdb4ab67 203 // Configure the fiber to directly enter the idle task.
Jonathan Austin 1:8aa5cdb4ab67 204 idleFiber = getFiberContext();
Jonathan Austin 1:8aa5cdb4ab67 205 idleFiber->tcb.SP = CORTEX_M0_STACK_BASE - 0x04;
Jonathan Austin 1:8aa5cdb4ab67 206 idleFiber->tcb.LR = (uint32_t) &idle_task;
Jonathan Austin 1:8aa5cdb4ab67 207
Jonathan Austin 1:8aa5cdb4ab67 208 if (messageBus)
Jonathan Austin 1:8aa5cdb4ab67 209 {
Jonathan Austin 1:8aa5cdb4ab67 210 // Register to receive events in the NOTIFY channel - this is used to implement wait-notify semantics
Jonathan Austin 1:8aa5cdb4ab67 211 messageBus->listen(MICROBIT_ID_NOTIFY, MICROBIT_EVT_ANY, scheduler_event, MESSAGE_BUS_LISTENER_IMMEDIATE);
Jonathan Austin 1:8aa5cdb4ab67 212 messageBus->listen(MICROBIT_ID_NOTIFY_ONE, MICROBIT_EVT_ANY, scheduler_event, MESSAGE_BUS_LISTENER_IMMEDIATE);
Jonathan Austin 1:8aa5cdb4ab67 213 }
Jonathan Austin 1:8aa5cdb4ab67 214
Jonathan Austin 1:8aa5cdb4ab67 215 // register a period callback to drive the scheduler and any other registered components.
Jonathan Austin 1:8aa5cdb4ab67 216 new MicroBitSystemTimerCallback(scheduler_tick);
Jonathan Austin 1:8aa5cdb4ab67 217
Jonathan Austin 1:8aa5cdb4ab67 218 fiber_flags |= MICROBIT_SCHEDULER_RUNNING;
Jonathan Austin 1:8aa5cdb4ab67 219 }
Jonathan Austin 1:8aa5cdb4ab67 220
Jonathan Austin 1:8aa5cdb4ab67 221 /**
Jonathan Austin 1:8aa5cdb4ab67 222 * Determines if the fiber scheduler is operational.
Jonathan Austin 1:8aa5cdb4ab67 223 *
Jonathan Austin 1:8aa5cdb4ab67 224 * @return 1 if the fber scheduler is running, 0 otherwise.
Jonathan Austin 1:8aa5cdb4ab67 225 */
Jonathan Austin 1:8aa5cdb4ab67 226 int fiber_scheduler_running()
Jonathan Austin 1:8aa5cdb4ab67 227 {
Jonathan Austin 1:8aa5cdb4ab67 228 if (fiber_flags & MICROBIT_SCHEDULER_RUNNING)
Jonathan Austin 1:8aa5cdb4ab67 229 return 1;
Jonathan Austin 1:8aa5cdb4ab67 230
Jonathan Austin 1:8aa5cdb4ab67 231 return 0;
Jonathan Austin 1:8aa5cdb4ab67 232 }
Jonathan Austin 1:8aa5cdb4ab67 233
Jonathan Austin 1:8aa5cdb4ab67 234 /**
Jonathan Austin 1:8aa5cdb4ab67 235 * The timer callback, called from interrupt context once every SYSTEM_TICK_PERIOD_MS milliseconds.
Jonathan Austin 1:8aa5cdb4ab67 236 * This function checks to determine if any fibers blocked on the sleep queue need to be woken up
Jonathan Austin 1:8aa5cdb4ab67 237 * and made runnable.
Jonathan Austin 1:8aa5cdb4ab67 238 */
Jonathan Austin 1:8aa5cdb4ab67 239 void scheduler_tick()
Jonathan Austin 1:8aa5cdb4ab67 240 {
Jonathan Austin 1:8aa5cdb4ab67 241 Fiber *f = sleepQueue;
Jonathan Austin 1:8aa5cdb4ab67 242 Fiber *t;
Jonathan Austin 1:8aa5cdb4ab67 243
Jonathan Austin 1:8aa5cdb4ab67 244 // Check the sleep queue, and wake up any fibers as necessary.
Jonathan Austin 1:8aa5cdb4ab67 245 while (f != NULL)
Jonathan Austin 1:8aa5cdb4ab67 246 {
Jonathan Austin 1:8aa5cdb4ab67 247 t = f->next;
Jonathan Austin 1:8aa5cdb4ab67 248
Jonathan Austin 1:8aa5cdb4ab67 249 if (system_timer_current_time() >= f->context)
Jonathan Austin 1:8aa5cdb4ab67 250 {
Jonathan Austin 1:8aa5cdb4ab67 251 // Wakey wakey!
Jonathan Austin 1:8aa5cdb4ab67 252 dequeue_fiber(f);
Jonathan Austin 1:8aa5cdb4ab67 253 queue_fiber(f,&runQueue);
Jonathan Austin 1:8aa5cdb4ab67 254 }
Jonathan Austin 1:8aa5cdb4ab67 255
Jonathan Austin 1:8aa5cdb4ab67 256 f = t;
Jonathan Austin 1:8aa5cdb4ab67 257 }
Jonathan Austin 1:8aa5cdb4ab67 258 }
Jonathan Austin 1:8aa5cdb4ab67 259
Jonathan Austin 1:8aa5cdb4ab67 260 /**
Jonathan Austin 1:8aa5cdb4ab67 261 * Event callback. Called from an instance of MicroBitMessageBus whenever an event is raised.
Jonathan Austin 1:8aa5cdb4ab67 262 *
Jonathan Austin 1:8aa5cdb4ab67 263 * This function checks to determine if any fibers blocked on the wait queue need to be woken up
Jonathan Austin 1:8aa5cdb4ab67 264 * and made runnable due to the event.
Jonathan Austin 1:8aa5cdb4ab67 265 *
Jonathan Austin 1:8aa5cdb4ab67 266 * @param evt the event that has just been raised on an instance of MicroBitMessageBus.
Jonathan Austin 1:8aa5cdb4ab67 267 */
Jonathan Austin 1:8aa5cdb4ab67 268 void scheduler_event(MicroBitEvent evt)
Jonathan Austin 1:8aa5cdb4ab67 269 {
Jonathan Austin 1:8aa5cdb4ab67 270 Fiber *f = waitQueue;
Jonathan Austin 1:8aa5cdb4ab67 271 Fiber *t;
Jonathan Austin 1:8aa5cdb4ab67 272 int notifyOneComplete = 0;
Jonathan Austin 1:8aa5cdb4ab67 273
Jonathan Austin 1:8aa5cdb4ab67 274 // This should never happen.
Jonathan Austin 1:8aa5cdb4ab67 275 // It is however, safe to simply ignore any events provided, as if no messageBus if recorded,
Jonathan Austin 1:8aa5cdb4ab67 276 // no fibers are permitted to block on events.
Jonathan Austin 1:8aa5cdb4ab67 277 if (messageBus == NULL)
Jonathan Austin 1:8aa5cdb4ab67 278 return;
Jonathan Austin 1:8aa5cdb4ab67 279
Jonathan Austin 1:8aa5cdb4ab67 280 // Check the wait queue, and wake up any fibers as necessary.
Jonathan Austin 1:8aa5cdb4ab67 281 while (f != NULL)
Jonathan Austin 1:8aa5cdb4ab67 282 {
Jonathan Austin 1:8aa5cdb4ab67 283 t = f->next;
Jonathan Austin 1:8aa5cdb4ab67 284
Jonathan Austin 1:8aa5cdb4ab67 285 // extract the event data this fiber is blocked on.
Jonathan Austin 1:8aa5cdb4ab67 286 uint16_t id = f->context & 0xFFFF;
Jonathan Austin 1:8aa5cdb4ab67 287 uint16_t value = (f->context & 0xFFFF0000) >> 16;
Jonathan Austin 1:8aa5cdb4ab67 288
Jonathan Austin 1:8aa5cdb4ab67 289 // Special case for the NOTIFY_ONE channel...
Jonathan Austin 1:8aa5cdb4ab67 290 if ((evt.source == MICROBIT_ID_NOTIFY_ONE && id == MICROBIT_ID_NOTIFY) && (value == MICROBIT_EVT_ANY || value == evt.value))
Jonathan Austin 1:8aa5cdb4ab67 291 {
Jonathan Austin 1:8aa5cdb4ab67 292 if (!notifyOneComplete)
Jonathan Austin 1:8aa5cdb4ab67 293 {
Jonathan Austin 1:8aa5cdb4ab67 294 // Wakey wakey!
Jonathan Austin 1:8aa5cdb4ab67 295 dequeue_fiber(f);
Jonathan Austin 1:8aa5cdb4ab67 296 queue_fiber(f,&runQueue);
Jonathan Austin 1:8aa5cdb4ab67 297 notifyOneComplete = 1;
Jonathan Austin 1:8aa5cdb4ab67 298 }
Jonathan Austin 1:8aa5cdb4ab67 299 }
Jonathan Austin 1:8aa5cdb4ab67 300
Jonathan Austin 1:8aa5cdb4ab67 301 // Normal case.
Jonathan Austin 1:8aa5cdb4ab67 302 else if ((id == MICROBIT_ID_ANY || id == evt.source) && (value == MICROBIT_EVT_ANY || value == evt.value))
Jonathan Austin 1:8aa5cdb4ab67 303 {
Jonathan Austin 1:8aa5cdb4ab67 304 // Wakey wakey!
Jonathan Austin 1:8aa5cdb4ab67 305 dequeue_fiber(f);
Jonathan Austin 1:8aa5cdb4ab67 306 queue_fiber(f,&runQueue);
Jonathan Austin 1:8aa5cdb4ab67 307 }
Jonathan Austin 1:8aa5cdb4ab67 308
Jonathan Austin 1:8aa5cdb4ab67 309 f = t;
Jonathan Austin 1:8aa5cdb4ab67 310 }
Jonathan Austin 1:8aa5cdb4ab67 311
Jonathan Austin 1:8aa5cdb4ab67 312 // Unregister this event, as we've woken up all the fibers with this match.
Jonathan Austin 1:8aa5cdb4ab67 313 if (evt.source != MICROBIT_ID_NOTIFY && evt.source != MICROBIT_ID_NOTIFY_ONE)
Jonathan Austin 1:8aa5cdb4ab67 314 messageBus->ignore(evt.source, evt.value, scheduler_event);
Jonathan Austin 1:8aa5cdb4ab67 315 }
Jonathan Austin 1:8aa5cdb4ab67 316
Jonathan Austin 1:8aa5cdb4ab67 317
Jonathan Austin 1:8aa5cdb4ab67 318 /**
Jonathan Austin 1:8aa5cdb4ab67 319 * Blocks the calling thread for the given period of time.
Jonathan Austin 1:8aa5cdb4ab67 320 * The calling thread will be immediateley descheduled, and placed onto a
Jonathan Austin 1:8aa5cdb4ab67 321 * wait queue until the requested amount of time has elapsed.
Jonathan Austin 1:8aa5cdb4ab67 322 *
Jonathan Austin 1:8aa5cdb4ab67 323 * @param t The period of time to sleep, in milliseconds.
Jonathan Austin 1:8aa5cdb4ab67 324 *
Jonathan Austin 1:8aa5cdb4ab67 325 * @note the fiber will not be be made runnable until after the elapsed time, but there
Jonathan Austin 1:8aa5cdb4ab67 326 * are no guarantees precisely when the fiber will next be scheduled.
Jonathan Austin 1:8aa5cdb4ab67 327 */
Jonathan Austin 1:8aa5cdb4ab67 328 void fiber_sleep(unsigned long t)
Jonathan Austin 1:8aa5cdb4ab67 329 {
Jonathan Austin 1:8aa5cdb4ab67 330 Fiber *f = currentFiber;
Jonathan Austin 1:8aa5cdb4ab67 331
Jonathan Austin 1:8aa5cdb4ab67 332 // If the scheduler is not running, then simply perform a spin wait and exit.
Jonathan Austin 1:8aa5cdb4ab67 333 if (!fiber_scheduler_running())
Jonathan Austin 1:8aa5cdb4ab67 334 {
Jonathan Austin 1:8aa5cdb4ab67 335 wait_ms(t);
Jonathan Austin 1:8aa5cdb4ab67 336 return;
Jonathan Austin 1:8aa5cdb4ab67 337 }
Jonathan Austin 1:8aa5cdb4ab67 338
Jonathan Austin 1:8aa5cdb4ab67 339 // Sleep is a blocking call, so if we're in a fork on block context,
Jonathan Austin 1:8aa5cdb4ab67 340 // it's time to spawn a new fiber...
Jonathan Austin 1:8aa5cdb4ab67 341 if (currentFiber->flags & MICROBIT_FIBER_FLAG_FOB)
Jonathan Austin 1:8aa5cdb4ab67 342 {
Jonathan Austin 1:8aa5cdb4ab67 343 // Allocate a new fiber. This will come from the fiber pool if availiable,
Jonathan Austin 1:8aa5cdb4ab67 344 // else a new one will be allocated on the heap.
Jonathan Austin 1:8aa5cdb4ab67 345 forkedFiber = getFiberContext();
Jonathan Austin 1:8aa5cdb4ab67 346
Jonathan Austin 1:8aa5cdb4ab67 347 // If we're out of memory, there's nothing we can do.
Jonathan Austin 1:8aa5cdb4ab67 348 // keep running in the context of the current thread as a best effort.
Jonathan Austin 1:8aa5cdb4ab67 349 if (forkedFiber != NULL)
Jonathan Austin 1:8aa5cdb4ab67 350 f = forkedFiber;
Jonathan Austin 1:8aa5cdb4ab67 351 }
Jonathan Austin 1:8aa5cdb4ab67 352
Jonathan Austin 1:8aa5cdb4ab67 353 // Calculate and store the time we want to wake up.
Jonathan Austin 1:8aa5cdb4ab67 354 f->context = system_timer_current_time() + t;
Jonathan Austin 1:8aa5cdb4ab67 355
Jonathan Austin 1:8aa5cdb4ab67 356 // Remove fiber from the run queue
Jonathan Austin 1:8aa5cdb4ab67 357 dequeue_fiber(f);
Jonathan Austin 1:8aa5cdb4ab67 358
Jonathan Austin 1:8aa5cdb4ab67 359 // Add fiber to the sleep queue. We maintain strict ordering here to reduce lookup times.
Jonathan Austin 1:8aa5cdb4ab67 360 queue_fiber(f, &sleepQueue);
Jonathan Austin 1:8aa5cdb4ab67 361
Jonathan Austin 1:8aa5cdb4ab67 362 // Finally, enter the scheduler.
Jonathan Austin 1:8aa5cdb4ab67 363 schedule();
Jonathan Austin 1:8aa5cdb4ab67 364 }
Jonathan Austin 1:8aa5cdb4ab67 365
Jonathan Austin 1:8aa5cdb4ab67 366 /**
Jonathan Austin 1:8aa5cdb4ab67 367 * Blocks the calling thread until the specified event is raised.
Jonathan Austin 1:8aa5cdb4ab67 368 * The calling thread will be immediateley descheduled, and placed onto a
Jonathan Austin 1:8aa5cdb4ab67 369 * wait queue until the requested event is received.
Jonathan Austin 1:8aa5cdb4ab67 370 *
Jonathan Austin 1:8aa5cdb4ab67 371 * @param id The ID field of the event to listen for (e.g. MICROBIT_ID_BUTTON_A)
Jonathan Austin 1:8aa5cdb4ab67 372 *
Jonathan Austin 1:8aa5cdb4ab67 373 * @param value The value of the event to listen for (e.g. MICROBIT_BUTTON_EVT_CLICK)
Jonathan Austin 1:8aa5cdb4ab67 374 *
Jonathan Austin 1:8aa5cdb4ab67 375 * @return MICROBIT_OK, or MICROBIT_NOT_SUPPORTED if the fiber scheduler is not running, or associated with an EventModel.
Jonathan Austin 1:8aa5cdb4ab67 376 *
Jonathan Austin 1:8aa5cdb4ab67 377 * @code
Jonathan Austin 1:8aa5cdb4ab67 378 * fiber_wait_for_event(MICROBIT_ID_BUTTON_A, MICROBIT_BUTTON_EVT_CLICK);
Jonathan Austin 1:8aa5cdb4ab67 379 * @endcode
Jonathan Austin 1:8aa5cdb4ab67 380 *
Jonathan Austin 1:8aa5cdb4ab67 381 * @note the fiber will not be be made runnable until after the event is raised, but there
Jonathan Austin 1:8aa5cdb4ab67 382 * are no guarantees precisely when the fiber will next be scheduled.
Jonathan Austin 1:8aa5cdb4ab67 383 */
Jonathan Austin 1:8aa5cdb4ab67 384 int fiber_wait_for_event(uint16_t id, uint16_t value)
Jonathan Austin 1:8aa5cdb4ab67 385 {
Jonathan Austin 1:8aa5cdb4ab67 386 int ret = fiber_wake_on_event(id, value);
Jonathan Austin 1:8aa5cdb4ab67 387
Jonathan Austin 1:8aa5cdb4ab67 388 if(ret == MICROBIT_OK)
Jonathan Austin 1:8aa5cdb4ab67 389 schedule();
Jonathan Austin 1:8aa5cdb4ab67 390
Jonathan Austin 1:8aa5cdb4ab67 391 return ret;
Jonathan Austin 1:8aa5cdb4ab67 392 }
Jonathan Austin 1:8aa5cdb4ab67 393
Jonathan Austin 1:8aa5cdb4ab67 394 /**
Jonathan Austin 1:8aa5cdb4ab67 395 * Configures the fiber context for the current fiber to block on an event ID
Jonathan Austin 1:8aa5cdb4ab67 396 * and value, but does not deschedule the fiber.
Jonathan Austin 1:8aa5cdb4ab67 397 *
Jonathan Austin 1:8aa5cdb4ab67 398 * @param id The ID field of the event to listen for (e.g. MICROBIT_ID_BUTTON_A)
Jonathan Austin 1:8aa5cdb4ab67 399 *
Jonathan Austin 1:8aa5cdb4ab67 400 * @param value The value of the event to listen for (e.g. MICROBIT_BUTTON_EVT_CLICK)
Jonathan Austin 1:8aa5cdb4ab67 401 *
Jonathan Austin 1:8aa5cdb4ab67 402 * @return MICROBIT_OK, or MICROBIT_NOT_SUPPORTED if the fiber scheduler is not running, or associated with an EventModel.
Jonathan Austin 1:8aa5cdb4ab67 403 *
Jonathan Austin 1:8aa5cdb4ab67 404 * @code
Jonathan Austin 1:8aa5cdb4ab67 405 * fiber_wake_on_event(MICROBIT_ID_BUTTON_A, MICROBIT_BUTTON_EVT_CLICK);
Jonathan Austin 1:8aa5cdb4ab67 406 *
Jonathan Austin 1:8aa5cdb4ab67 407 * //perform some time critical operation.
Jonathan Austin 1:8aa5cdb4ab67 408 *
Jonathan Austin 1:8aa5cdb4ab67 409 * //deschedule the current fiber manually, waiting for the previously configured event.
Jonathan Austin 1:8aa5cdb4ab67 410 * schedule();
Jonathan Austin 1:8aa5cdb4ab67 411 * @endcode
Jonathan Austin 1:8aa5cdb4ab67 412 */
Jonathan Austin 1:8aa5cdb4ab67 413 int fiber_wake_on_event(uint16_t id, uint16_t value)
Jonathan Austin 1:8aa5cdb4ab67 414 {
Jonathan Austin 1:8aa5cdb4ab67 415 Fiber *f = currentFiber;
Jonathan Austin 1:8aa5cdb4ab67 416
Jonathan Austin 1:8aa5cdb4ab67 417 if (messageBus == NULL || !fiber_scheduler_running())
Jonathan Austin 1:8aa5cdb4ab67 418 return MICROBIT_NOT_SUPPORTED;
Jonathan Austin 1:8aa5cdb4ab67 419
LancasterUniversity 25:27299423d813 420 // Sleep is a blocking call, so if we're in a fork on block context,
Jonathan Austin 1:8aa5cdb4ab67 421 // it's time to spawn a new fiber...
Jonathan Austin 1:8aa5cdb4ab67 422 if (currentFiber->flags & MICROBIT_FIBER_FLAG_FOB)
Jonathan Austin 1:8aa5cdb4ab67 423 {
Jonathan Austin 1:8aa5cdb4ab67 424 // Allocate a TCB from the new fiber. This will come from the tread pool if availiable,
Jonathan Austin 1:8aa5cdb4ab67 425 // else a new one will be allocated on the heap.
Jonathan Austin 1:8aa5cdb4ab67 426 forkedFiber = getFiberContext();
Jonathan Austin 1:8aa5cdb4ab67 427
Jonathan Austin 1:8aa5cdb4ab67 428 // If we're out of memory, there's nothing we can do.
Jonathan Austin 1:8aa5cdb4ab67 429 // keep running in the context of the current thread as a best effort.
Jonathan Austin 1:8aa5cdb4ab67 430 if (forkedFiber != NULL)
LancasterUniversity 25:27299423d813 431 {
LancasterUniversity 25:27299423d813 432 f = forkedFiber;
LancasterUniversity 25:27299423d813 433 dequeue_fiber(f);
LancasterUniversity 25:27299423d813 434 queue_fiber(f, &runQueue);
LancasterUniversity 25:27299423d813 435 schedule();
LancasterUniversity 25:27299423d813 436 }
Jonathan Austin 1:8aa5cdb4ab67 437 }
Jonathan Austin 1:8aa5cdb4ab67 438
Jonathan Austin 1:8aa5cdb4ab67 439 // Encode the event data in the context field. It's handy having a 32 bit core. :-)
Jonathan Austin 1:8aa5cdb4ab67 440 f->context = value << 16 | id;
Jonathan Austin 1:8aa5cdb4ab67 441
LancasterUniversity 25:27299423d813 442 // Remove ourselves from the run queue
Jonathan Austin 1:8aa5cdb4ab67 443 dequeue_fiber(f);
Jonathan Austin 1:8aa5cdb4ab67 444
Jonathan Austin 1:8aa5cdb4ab67 445 // Add ourselves to the sleep queue. We maintain strict ordering here to reduce lookup times.
Jonathan Austin 1:8aa5cdb4ab67 446 queue_fiber(f, &waitQueue);
Jonathan Austin 1:8aa5cdb4ab67 447
Jonathan Austin 1:8aa5cdb4ab67 448 // Register to receive this event, so we can wake up the fiber when it happens.
LancasterUniversity 25:27299423d813 449 // Special case for the notify channel, as we always stay registered for that.
Jonathan Austin 1:8aa5cdb4ab67 450 if (id != MICROBIT_ID_NOTIFY && id != MICROBIT_ID_NOTIFY_ONE)
Jonathan Austin 1:8aa5cdb4ab67 451 messageBus->listen(id, value, scheduler_event, MESSAGE_BUS_LISTENER_IMMEDIATE);
Jonathan Austin 1:8aa5cdb4ab67 452
Jonathan Austin 1:8aa5cdb4ab67 453 return MICROBIT_OK;
Jonathan Austin 1:8aa5cdb4ab67 454 }
Jonathan Austin 1:8aa5cdb4ab67 455
Jonathan Austin 1:8aa5cdb4ab67 456 /**
Jonathan Austin 1:8aa5cdb4ab67 457 * Executes the given function asynchronously if necessary.
Jonathan Austin 1:8aa5cdb4ab67 458 *
Jonathan Austin 1:8aa5cdb4ab67 459 * Fibers are often used to run event handlers, however many of these event handlers are very simple functions
Jonathan Austin 1:8aa5cdb4ab67 460 * that complete very quickly, bringing unecessary RAM overhead.
Jonathan Austin 1:8aa5cdb4ab67 461 *
Jonathan Austin 1:8aa5cdb4ab67 462 * This function takes a snapshot of the current processor context, then attempts to optimistically call the given function directly.
Jonathan Austin 1:8aa5cdb4ab67 463 * We only create an additional fiber if that function performs a block operation.
Jonathan Austin 1:8aa5cdb4ab67 464 *
Jonathan Austin 1:8aa5cdb4ab67 465 * @param entry_fn The function to execute.
Jonathan Austin 1:8aa5cdb4ab67 466 *
Jonathan Austin 1:8aa5cdb4ab67 467 * @return MICROBIT_OK, or MICROBIT_INVALID_PARAMETER.
Jonathan Austin 1:8aa5cdb4ab67 468 */
Jonathan Austin 1:8aa5cdb4ab67 469 int invoke(void (*entry_fn)(void))
Jonathan Austin 1:8aa5cdb4ab67 470 {
Jonathan Austin 1:8aa5cdb4ab67 471 // Validate our parameters.
Jonathan Austin 1:8aa5cdb4ab67 472 if (entry_fn == NULL)
Jonathan Austin 1:8aa5cdb4ab67 473 return MICROBIT_INVALID_PARAMETER;
Jonathan Austin 1:8aa5cdb4ab67 474
Jonathan Austin 1:8aa5cdb4ab67 475 if (!fiber_scheduler_running())
Jonathan Austin 1:8aa5cdb4ab67 476 return MICROBIT_NOT_SUPPORTED;
Jonathan Austin 1:8aa5cdb4ab67 477
Jonathan Austin 1:8aa5cdb4ab67 478 if (currentFiber->flags & MICROBIT_FIBER_FLAG_FOB)
Jonathan Austin 1:8aa5cdb4ab67 479 {
Jonathan Austin 1:8aa5cdb4ab67 480 // If we attempt a fork on block whilst already in fork n block context,
Jonathan Austin 1:8aa5cdb4ab67 481 // simply launch a fiber to deal with the request and we're done.
Jonathan Austin 1:8aa5cdb4ab67 482 create_fiber(entry_fn);
Jonathan Austin 1:8aa5cdb4ab67 483 return MICROBIT_OK;
Jonathan Austin 1:8aa5cdb4ab67 484 }
Jonathan Austin 1:8aa5cdb4ab67 485
Jonathan Austin 1:8aa5cdb4ab67 486 // Snapshot current context, but also update the Link Register to
Jonathan Austin 1:8aa5cdb4ab67 487 // refer to our calling function.
Jonathan Austin 1:8aa5cdb4ab67 488 save_register_context(&currentFiber->tcb);
Jonathan Austin 1:8aa5cdb4ab67 489
Jonathan Austin 1:8aa5cdb4ab67 490 // If we're here, there are two possibilities:
Jonathan Austin 1:8aa5cdb4ab67 491 // 1) We're about to attempt to execute the user code
Jonathan Austin 1:8aa5cdb4ab67 492 // 2) We've already tried to execute the code, it blocked, and we've backtracked.
Jonathan Austin 1:8aa5cdb4ab67 493
Jonathan Austin 1:8aa5cdb4ab67 494 // If we're returning from the user function and we forked another fiber then cleanup and exit.
Jonathan Austin 1:8aa5cdb4ab67 495 if (currentFiber->flags & MICROBIT_FIBER_FLAG_PARENT)
Jonathan Austin 1:8aa5cdb4ab67 496 {
Jonathan Austin 1:8aa5cdb4ab67 497 currentFiber->flags &= ~MICROBIT_FIBER_FLAG_FOB;
Jonathan Austin 1:8aa5cdb4ab67 498 currentFiber->flags &= ~MICROBIT_FIBER_FLAG_PARENT;
Jonathan Austin 1:8aa5cdb4ab67 499 return MICROBIT_OK;
Jonathan Austin 1:8aa5cdb4ab67 500 }
Jonathan Austin 1:8aa5cdb4ab67 501
Jonathan Austin 1:8aa5cdb4ab67 502 // Otherwise, we're here for the first time. Enter FORK ON BLOCK mode, and
Jonathan Austin 1:8aa5cdb4ab67 503 // execute the function directly. If the code tries to block, we detect this and
Jonathan Austin 1:8aa5cdb4ab67 504 // spawn a thread to deal with it.
Jonathan Austin 1:8aa5cdb4ab67 505 currentFiber->flags |= MICROBIT_FIBER_FLAG_FOB;
Jonathan Austin 1:8aa5cdb4ab67 506 entry_fn();
Jonathan Austin 1:8aa5cdb4ab67 507 currentFiber->flags &= ~MICROBIT_FIBER_FLAG_FOB;
Jonathan Austin 1:8aa5cdb4ab67 508
Jonathan Austin 1:8aa5cdb4ab67 509 // If this is is an exiting fiber that for spawned to handle a blocking call, recycle it.
Jonathan Austin 1:8aa5cdb4ab67 510 // The fiber will then re-enter the scheduler, so no need for further cleanup.
Jonathan Austin 1:8aa5cdb4ab67 511 if (currentFiber->flags & MICROBIT_FIBER_FLAG_CHILD)
Jonathan Austin 1:8aa5cdb4ab67 512 release_fiber();
Jonathan Austin 1:8aa5cdb4ab67 513
Jonathan Austin 1:8aa5cdb4ab67 514 return MICROBIT_OK;
Jonathan Austin 1:8aa5cdb4ab67 515 }
Jonathan Austin 1:8aa5cdb4ab67 516
Jonathan Austin 1:8aa5cdb4ab67 517 /**
Jonathan Austin 1:8aa5cdb4ab67 518 * Executes the given function asynchronously if necessary, and offers the ability to provide a parameter.
Jonathan Austin 1:8aa5cdb4ab67 519 *
Jonathan Austin 1:8aa5cdb4ab67 520 * Fibers are often used to run event handlers, however many of these event handlers are very simple functions
Jonathan Austin 1:8aa5cdb4ab67 521 * that complete very quickly, bringing unecessary RAM. overhead
Jonathan Austin 1:8aa5cdb4ab67 522 *
Jonathan Austin 1:8aa5cdb4ab67 523 * This function takes a snapshot of the current fiber context, then attempt to optimistically call the given function directly.
Jonathan Austin 1:8aa5cdb4ab67 524 * We only create an additional fiber if that function performs a block operation.
Jonathan Austin 1:8aa5cdb4ab67 525 *
Jonathan Austin 1:8aa5cdb4ab67 526 * @param entry_fn The function to execute.
Jonathan Austin 1:8aa5cdb4ab67 527 *
Jonathan Austin 1:8aa5cdb4ab67 528 * @param param an untyped parameter passed into the entry_fn and completion_fn.
Jonathan Austin 1:8aa5cdb4ab67 529 *
Jonathan Austin 1:8aa5cdb4ab67 530 * @return MICROBIT_OK, or MICROBIT_INVALID_PARAMETER.
Jonathan Austin 1:8aa5cdb4ab67 531 */
Jonathan Austin 1:8aa5cdb4ab67 532 int invoke(void (*entry_fn)(void *), void *param)
Jonathan Austin 1:8aa5cdb4ab67 533 {
Jonathan Austin 1:8aa5cdb4ab67 534 // Validate our parameters.
Jonathan Austin 1:8aa5cdb4ab67 535 if (entry_fn == NULL)
Jonathan Austin 1:8aa5cdb4ab67 536 return MICROBIT_INVALID_PARAMETER;
Jonathan Austin 1:8aa5cdb4ab67 537
Jonathan Austin 1:8aa5cdb4ab67 538 if (!fiber_scheduler_running())
Jonathan Austin 1:8aa5cdb4ab67 539 return MICROBIT_NOT_SUPPORTED;
Jonathan Austin 1:8aa5cdb4ab67 540
Jonathan Austin 1:8aa5cdb4ab67 541 if (currentFiber->flags & (MICROBIT_FIBER_FLAG_FOB | MICROBIT_FIBER_FLAG_PARENT | MICROBIT_FIBER_FLAG_CHILD))
Jonathan Austin 1:8aa5cdb4ab67 542 {
Jonathan Austin 1:8aa5cdb4ab67 543 // If we attempt a fork on block whilst already in a fork on block context,
Jonathan Austin 1:8aa5cdb4ab67 544 // simply launch a fiber to deal with the request and we're done.
Jonathan Austin 1:8aa5cdb4ab67 545 create_fiber(entry_fn, param);
Jonathan Austin 1:8aa5cdb4ab67 546 return MICROBIT_OK;
Jonathan Austin 1:8aa5cdb4ab67 547 }
Jonathan Austin 1:8aa5cdb4ab67 548
Jonathan Austin 1:8aa5cdb4ab67 549 // Snapshot current context, but also update the Link Register to
Jonathan Austin 1:8aa5cdb4ab67 550 // refer to our calling function.
Jonathan Austin 1:8aa5cdb4ab67 551 save_register_context(&currentFiber->tcb);
Jonathan Austin 1:8aa5cdb4ab67 552
Jonathan Austin 1:8aa5cdb4ab67 553 // If we're here, there are two possibilities:
Jonathan Austin 1:8aa5cdb4ab67 554 // 1) We're about to attempt to execute the user code
Jonathan Austin 1:8aa5cdb4ab67 555 // 2) We've already tried to execute the code, it blocked, and we've backtracked.
Jonathan Austin 1:8aa5cdb4ab67 556
Jonathan Austin 1:8aa5cdb4ab67 557 // If we're returning from the user function and we forked another fiber then cleanup and exit.
Jonathan Austin 1:8aa5cdb4ab67 558 if (currentFiber->flags & MICROBIT_FIBER_FLAG_PARENT)
Jonathan Austin 1:8aa5cdb4ab67 559 {
Jonathan Austin 1:8aa5cdb4ab67 560 currentFiber->flags &= ~MICROBIT_FIBER_FLAG_FOB;
Jonathan Austin 1:8aa5cdb4ab67 561 currentFiber->flags &= ~MICROBIT_FIBER_FLAG_PARENT;
Jonathan Austin 1:8aa5cdb4ab67 562 return MICROBIT_OK;
Jonathan Austin 1:8aa5cdb4ab67 563 }
Jonathan Austin 1:8aa5cdb4ab67 564
Jonathan Austin 1:8aa5cdb4ab67 565 // Otherwise, we're here for the first time. Enter FORK ON BLOCK mode, and
Jonathan Austin 1:8aa5cdb4ab67 566 // execute the function directly. If the code tries to block, we detect this and
Jonathan Austin 1:8aa5cdb4ab67 567 // spawn a thread to deal with it.
Jonathan Austin 1:8aa5cdb4ab67 568 currentFiber->flags |= MICROBIT_FIBER_FLAG_FOB;
Jonathan Austin 1:8aa5cdb4ab67 569 entry_fn(param);
Jonathan Austin 1:8aa5cdb4ab67 570 currentFiber->flags &= ~MICROBIT_FIBER_FLAG_FOB;
Jonathan Austin 1:8aa5cdb4ab67 571
Jonathan Austin 1:8aa5cdb4ab67 572 // If this is is an exiting fiber that for spawned to handle a blocking call, recycle it.
Jonathan Austin 1:8aa5cdb4ab67 573 // The fiber will then re-enter the scheduler, so no need for further cleanup.
Jonathan Austin 1:8aa5cdb4ab67 574 if (currentFiber->flags & MICROBIT_FIBER_FLAG_CHILD)
Jonathan Austin 1:8aa5cdb4ab67 575 release_fiber(param);
Jonathan Austin 1:8aa5cdb4ab67 576
Jonathan Austin 1:8aa5cdb4ab67 577 return MICROBIT_OK;
Jonathan Austin 1:8aa5cdb4ab67 578 }
Jonathan Austin 1:8aa5cdb4ab67 579
Jonathan Austin 1:8aa5cdb4ab67 580 /**
Jonathan Austin 1:8aa5cdb4ab67 581 * Launches a fiber.
Jonathan Austin 1:8aa5cdb4ab67 582 *
Jonathan Austin 1:8aa5cdb4ab67 583 * @param ep the entry point for the fiber.
Jonathan Austin 1:8aa5cdb4ab67 584 *
Jonathan Austin 1:8aa5cdb4ab67 585 * @param cp the completion routine after ep has finished execution
Jonathan Austin 1:8aa5cdb4ab67 586 */
Jonathan Austin 1:8aa5cdb4ab67 587 void launch_new_fiber(void (*ep)(void), void (*cp)(void))
Jonathan Austin 1:8aa5cdb4ab67 588 {
Jonathan Austin 1:8aa5cdb4ab67 589 // Execute the thread's entrypoint
Jonathan Austin 1:8aa5cdb4ab67 590 ep();
Jonathan Austin 1:8aa5cdb4ab67 591
Jonathan Austin 1:8aa5cdb4ab67 592 // Execute the thread's completion routine;
Jonathan Austin 1:8aa5cdb4ab67 593 cp();
Jonathan Austin 1:8aa5cdb4ab67 594
Jonathan Austin 1:8aa5cdb4ab67 595 // If we get here, then the completion routine didn't recycle the fiber... so do it anyway. :-)
Jonathan Austin 1:8aa5cdb4ab67 596 release_fiber();
Jonathan Austin 1:8aa5cdb4ab67 597 }
Jonathan Austin 1:8aa5cdb4ab67 598
Jonathan Austin 1:8aa5cdb4ab67 599 /**
Jonathan Austin 1:8aa5cdb4ab67 600 * Launches a fiber with a parameter
Jonathan Austin 1:8aa5cdb4ab67 601 *
Jonathan Austin 1:8aa5cdb4ab67 602 * @param ep the entry point for the fiber.
Jonathan Austin 1:8aa5cdb4ab67 603 *
Jonathan Austin 1:8aa5cdb4ab67 604 * @param cp the completion routine after ep has finished execution
Jonathan Austin 1:8aa5cdb4ab67 605 *
Jonathan Austin 1:8aa5cdb4ab67 606 * @param pm the parameter to provide to ep and cp.
Jonathan Austin 1:8aa5cdb4ab67 607 */
Jonathan Austin 1:8aa5cdb4ab67 608 void launch_new_fiber_param(void (*ep)(void *), void (*cp)(void *), void *pm)
Jonathan Austin 1:8aa5cdb4ab67 609 {
Jonathan Austin 1:8aa5cdb4ab67 610 // Execute the thread's entrypoint.
Jonathan Austin 1:8aa5cdb4ab67 611 ep(pm);
Jonathan Austin 1:8aa5cdb4ab67 612
Jonathan Austin 1:8aa5cdb4ab67 613 // Execute the thread's completion routine.
Jonathan Austin 1:8aa5cdb4ab67 614 cp(pm);
Jonathan Austin 1:8aa5cdb4ab67 615
Jonathan Austin 1:8aa5cdb4ab67 616 // If we get here, then the completion routine didn't recycle the fiber... so do it anyway. :-)
Jonathan Austin 1:8aa5cdb4ab67 617 release_fiber(pm);
Jonathan Austin 1:8aa5cdb4ab67 618 }
Jonathan Austin 1:8aa5cdb4ab67 619
Jonathan Austin 1:8aa5cdb4ab67 620 Fiber *__create_fiber(uint32_t ep, uint32_t cp, uint32_t pm, int parameterised)
Jonathan Austin 1:8aa5cdb4ab67 621 {
Jonathan Austin 1:8aa5cdb4ab67 622 // Validate our parameters.
Jonathan Austin 1:8aa5cdb4ab67 623 if (ep == 0 || cp == 0)
Jonathan Austin 1:8aa5cdb4ab67 624 return NULL;
Jonathan Austin 1:8aa5cdb4ab67 625
Jonathan Austin 1:8aa5cdb4ab67 626 // Allocate a TCB from the new fiber. This will come from the fiber pool if availiable,
Jonathan Austin 1:8aa5cdb4ab67 627 // else a new one will be allocated on the heap.
Jonathan Austin 1:8aa5cdb4ab67 628 Fiber *newFiber = getFiberContext();
Jonathan Austin 1:8aa5cdb4ab67 629
Jonathan Austin 1:8aa5cdb4ab67 630 // If we're out of memory, there's nothing we can do.
Jonathan Austin 1:8aa5cdb4ab67 631 if (newFiber == NULL)
Jonathan Austin 1:8aa5cdb4ab67 632 return NULL;
Jonathan Austin 1:8aa5cdb4ab67 633
Jonathan Austin 1:8aa5cdb4ab67 634 newFiber->tcb.R0 = (uint32_t) ep;
Jonathan Austin 1:8aa5cdb4ab67 635 newFiber->tcb.R1 = (uint32_t) cp;
Jonathan Austin 1:8aa5cdb4ab67 636 newFiber->tcb.R2 = (uint32_t) pm;
Jonathan Austin 1:8aa5cdb4ab67 637
Jonathan Austin 1:8aa5cdb4ab67 638 // Set the stack and assign the link register to refer to the appropriate entry point wrapper.
Jonathan Austin 1:8aa5cdb4ab67 639 newFiber->tcb.SP = CORTEX_M0_STACK_BASE - 0x04;
Jonathan Austin 1:8aa5cdb4ab67 640 newFiber->tcb.LR = parameterised ? (uint32_t) &launch_new_fiber_param : (uint32_t) &launch_new_fiber;
Jonathan Austin 1:8aa5cdb4ab67 641
Jonathan Austin 1:8aa5cdb4ab67 642 // Add new fiber to the run queue.
Jonathan Austin 1:8aa5cdb4ab67 643 queue_fiber(newFiber, &runQueue);
Jonathan Austin 1:8aa5cdb4ab67 644
Jonathan Austin 1:8aa5cdb4ab67 645 return newFiber;
Jonathan Austin 1:8aa5cdb4ab67 646 }
Jonathan Austin 1:8aa5cdb4ab67 647
Jonathan Austin 1:8aa5cdb4ab67 648 /**
Jonathan Austin 1:8aa5cdb4ab67 649 * Creates a new Fiber, and launches it.
Jonathan Austin 1:8aa5cdb4ab67 650 *
Jonathan Austin 1:8aa5cdb4ab67 651 * @param entry_fn The function the new Fiber will begin execution in.
Jonathan Austin 1:8aa5cdb4ab67 652 *
Jonathan Austin 1:8aa5cdb4ab67 653 * @param completion_fn The function called when the thread completes execution of entry_fn.
Jonathan Austin 1:8aa5cdb4ab67 654 * Defaults to release_fiber.
Jonathan Austin 1:8aa5cdb4ab67 655 *
Jonathan Austin 1:8aa5cdb4ab67 656 * @return The new Fiber, or NULL if the operation could not be completed.
Jonathan Austin 1:8aa5cdb4ab67 657 */
Jonathan Austin 1:8aa5cdb4ab67 658 Fiber *create_fiber(void (*entry_fn)(void), void (*completion_fn)(void))
Jonathan Austin 1:8aa5cdb4ab67 659 {
Jonathan Austin 1:8aa5cdb4ab67 660 if (!fiber_scheduler_running())
Jonathan Austin 1:8aa5cdb4ab67 661 return NULL;
Jonathan Austin 1:8aa5cdb4ab67 662
Jonathan Austin 1:8aa5cdb4ab67 663 return __create_fiber((uint32_t) entry_fn, (uint32_t)completion_fn, 0, 0);
Jonathan Austin 1:8aa5cdb4ab67 664 }
Jonathan Austin 1:8aa5cdb4ab67 665
Jonathan Austin 1:8aa5cdb4ab67 666
Jonathan Austin 1:8aa5cdb4ab67 667 /**
Jonathan Austin 1:8aa5cdb4ab67 668 * Creates a new parameterised Fiber, and launches it.
Jonathan Austin 1:8aa5cdb4ab67 669 *
Jonathan Austin 1:8aa5cdb4ab67 670 * @param entry_fn The function the new Fiber will begin execution in.
Jonathan Austin 1:8aa5cdb4ab67 671 *
Jonathan Austin 1:8aa5cdb4ab67 672 * @param param an untyped parameter passed into the entry_fn and completion_fn.
Jonathan Austin 1:8aa5cdb4ab67 673 *
Jonathan Austin 1:8aa5cdb4ab67 674 * @param completion_fn The function called when the thread completes execution of entry_fn.
Jonathan Austin 1:8aa5cdb4ab67 675 * Defaults to release_fiber.
Jonathan Austin 1:8aa5cdb4ab67 676 *
Jonathan Austin 1:8aa5cdb4ab67 677 * @return The new Fiber, or NULL if the operation could not be completed.
Jonathan Austin 1:8aa5cdb4ab67 678 */
Jonathan Austin 1:8aa5cdb4ab67 679 Fiber *create_fiber(void (*entry_fn)(void *), void *param, void (*completion_fn)(void *))
Jonathan Austin 1:8aa5cdb4ab67 680 {
Jonathan Austin 1:8aa5cdb4ab67 681 if (!fiber_scheduler_running())
Jonathan Austin 1:8aa5cdb4ab67 682 return NULL;
Jonathan Austin 1:8aa5cdb4ab67 683
Jonathan Austin 1:8aa5cdb4ab67 684 return __create_fiber((uint32_t) entry_fn, (uint32_t)completion_fn, (uint32_t) param, 1);
Jonathan Austin 1:8aa5cdb4ab67 685 }
Jonathan Austin 1:8aa5cdb4ab67 686
Jonathan Austin 1:8aa5cdb4ab67 687 /**
Jonathan Austin 1:8aa5cdb4ab67 688 * Exit point for all fibers.
Jonathan Austin 1:8aa5cdb4ab67 689 *
Jonathan Austin 1:8aa5cdb4ab67 690 * Any fiber reaching the end of its entry function will return here for recycling.
Jonathan Austin 1:8aa5cdb4ab67 691 */
Jonathan Austin 1:8aa5cdb4ab67 692 void release_fiber(void *)
Jonathan Austin 1:8aa5cdb4ab67 693 {
Jonathan Austin 1:8aa5cdb4ab67 694 if (!fiber_scheduler_running())
Jonathan Austin 1:8aa5cdb4ab67 695 return;
Jonathan Austin 1:8aa5cdb4ab67 696
Jonathan Austin 1:8aa5cdb4ab67 697 release_fiber();
Jonathan Austin 1:8aa5cdb4ab67 698 }
Jonathan Austin 1:8aa5cdb4ab67 699
Jonathan Austin 1:8aa5cdb4ab67 700 /**
Jonathan Austin 1:8aa5cdb4ab67 701 * Exit point for all fibers.
Jonathan Austin 1:8aa5cdb4ab67 702 *
Jonathan Austin 1:8aa5cdb4ab67 703 * Any fiber reaching the end of its entry function will return here for recycling.
Jonathan Austin 1:8aa5cdb4ab67 704 */
Jonathan Austin 1:8aa5cdb4ab67 705 void release_fiber(void)
Jonathan Austin 1:8aa5cdb4ab67 706 {
Jonathan Austin 1:8aa5cdb4ab67 707 if (!fiber_scheduler_running())
Jonathan Austin 1:8aa5cdb4ab67 708 return;
Jonathan Austin 1:8aa5cdb4ab67 709
Jonathan Austin 1:8aa5cdb4ab67 710 // Remove ourselves form the runqueue.
Jonathan Austin 1:8aa5cdb4ab67 711 dequeue_fiber(currentFiber);
Jonathan Austin 1:8aa5cdb4ab67 712
Jonathan Austin 1:8aa5cdb4ab67 713 // Add ourselves to the list of free fibers
Jonathan Austin 1:8aa5cdb4ab67 714 queue_fiber(currentFiber, &fiberPool);
Jonathan Austin 1:8aa5cdb4ab67 715
Jonathan Austin 1:8aa5cdb4ab67 716 // Find something else to do!
Jonathan Austin 1:8aa5cdb4ab67 717 schedule();
Jonathan Austin 1:8aa5cdb4ab67 718 }
Jonathan Austin 1:8aa5cdb4ab67 719
Jonathan Austin 1:8aa5cdb4ab67 720 /**
Jonathan Austin 1:8aa5cdb4ab67 721 * Resizes the stack allocation of the current fiber if necessary to hold the system stack.
Jonathan Austin 1:8aa5cdb4ab67 722 *
Jonathan Austin 1:8aa5cdb4ab67 723 * If the stack allocation is large enough to hold the current system stack, then this function does nothing.
Jonathan Austin 1:8aa5cdb4ab67 724 * Otherwise, the the current allocation of the fiber is freed, and a larger block is allocated.
Jonathan Austin 1:8aa5cdb4ab67 725 *
Jonathan Austin 1:8aa5cdb4ab67 726 * @param f The fiber context to verify.
Jonathan Austin 1:8aa5cdb4ab67 727 *
Jonathan Austin 1:8aa5cdb4ab67 728 * @return The stack depth of the given fiber.
Jonathan Austin 1:8aa5cdb4ab67 729 */
Jonathan Austin 1:8aa5cdb4ab67 730 void verify_stack_size(Fiber *f)
Jonathan Austin 1:8aa5cdb4ab67 731 {
Jonathan Austin 1:8aa5cdb4ab67 732 // Ensure the stack buffer is large enough to hold the stack Reallocate if necessary.
Jonathan Austin 1:8aa5cdb4ab67 733 uint32_t stackDepth;
Jonathan Austin 1:8aa5cdb4ab67 734 uint32_t bufferSize;
Jonathan Austin 1:8aa5cdb4ab67 735
Jonathan Austin 1:8aa5cdb4ab67 736 // Calculate the stack depth.
Jonathan Austin 1:8aa5cdb4ab67 737 stackDepth = f->tcb.stack_base - ((uint32_t) __get_MSP());
Jonathan Austin 1:8aa5cdb4ab67 738
Jonathan Austin 1:8aa5cdb4ab67 739 // Calculate the size of our allocated stack buffer
Jonathan Austin 1:8aa5cdb4ab67 740 bufferSize = f->stack_top - f->stack_bottom;
Jonathan Austin 1:8aa5cdb4ab67 741
Jonathan Austin 1:8aa5cdb4ab67 742 // If we're too small, increase our buffer size.
Jonathan Austin 1:8aa5cdb4ab67 743 if (bufferSize < stackDepth)
Jonathan Austin 1:8aa5cdb4ab67 744 {
Jonathan Austin 1:8aa5cdb4ab67 745 // To ease heap churn, we choose the next largest multple of 32 bytes.
Jonathan Austin 1:8aa5cdb4ab67 746 bufferSize = (stackDepth + 32) & 0xffffffe0;
Jonathan Austin 1:8aa5cdb4ab67 747
Jonathan Austin 1:8aa5cdb4ab67 748 // Release the old memory
Jonathan Austin 1:8aa5cdb4ab67 749 if (f->stack_bottom != 0)
Jonathan Austin 1:8aa5cdb4ab67 750 free((void *)f->stack_bottom);
Jonathan Austin 1:8aa5cdb4ab67 751
Jonathan Austin 1:8aa5cdb4ab67 752 // Allocate a new one of the appropriate size.
Jonathan Austin 1:8aa5cdb4ab67 753 f->stack_bottom = (uint32_t) malloc(bufferSize);
Jonathan Austin 1:8aa5cdb4ab67 754
Jonathan Austin 1:8aa5cdb4ab67 755 // Recalculate where the top of the stack is and we're done.
Jonathan Austin 1:8aa5cdb4ab67 756 f->stack_top = f->stack_bottom + bufferSize;
Jonathan Austin 1:8aa5cdb4ab67 757 }
Jonathan Austin 1:8aa5cdb4ab67 758 }
Jonathan Austin 1:8aa5cdb4ab67 759
Jonathan Austin 1:8aa5cdb4ab67 760 /**
Jonathan Austin 1:8aa5cdb4ab67 761 * Determines if any fibers are waiting to be scheduled.
Jonathan Austin 1:8aa5cdb4ab67 762 *
Jonathan Austin 1:8aa5cdb4ab67 763 * @return The number of fibers currently on the run queue
Jonathan Austin 1:8aa5cdb4ab67 764 */
Jonathan Austin 1:8aa5cdb4ab67 765 int scheduler_runqueue_empty()
Jonathan Austin 1:8aa5cdb4ab67 766 {
Jonathan Austin 1:8aa5cdb4ab67 767 return (runQueue == NULL);
Jonathan Austin 1:8aa5cdb4ab67 768 }
Jonathan Austin 1:8aa5cdb4ab67 769
Jonathan Austin 1:8aa5cdb4ab67 770 /**
Jonathan Austin 1:8aa5cdb4ab67 771 * Calls the Fiber scheduler.
Jonathan Austin 1:8aa5cdb4ab67 772 * The calling Fiber will likely be blocked, and control given to another waiting fiber.
Jonathan Austin 1:8aa5cdb4ab67 773 * Call this function to yield control of the processor when you have nothing more to do.
Jonathan Austin 1:8aa5cdb4ab67 774 */
Jonathan Austin 1:8aa5cdb4ab67 775 void schedule()
Jonathan Austin 1:8aa5cdb4ab67 776 {
Jonathan Austin 1:8aa5cdb4ab67 777 if (!fiber_scheduler_running())
Jonathan Austin 1:8aa5cdb4ab67 778 return;
Jonathan Austin 1:8aa5cdb4ab67 779
Jonathan Austin 1:8aa5cdb4ab67 780 // First, take a reference to the currently running fiber;
Jonathan Austin 1:8aa5cdb4ab67 781 Fiber *oldFiber = currentFiber;
Jonathan Austin 1:8aa5cdb4ab67 782
Jonathan Austin 1:8aa5cdb4ab67 783 // First, see if we're in Fork on Block context. If so, we simply want to store the full context
Jonathan Austin 1:8aa5cdb4ab67 784 // of the currently running thread in a newly created fiber, and restore the context of the
Jonathan Austin 1:8aa5cdb4ab67 785 // currently running fiber, back to the point where it entered FOB.
Jonathan Austin 1:8aa5cdb4ab67 786
Jonathan Austin 1:8aa5cdb4ab67 787 if (currentFiber->flags & MICROBIT_FIBER_FLAG_FOB)
Jonathan Austin 1:8aa5cdb4ab67 788 {
Jonathan Austin 1:8aa5cdb4ab67 789 // Record that the fibers have a parent/child relationship
Jonathan Austin 1:8aa5cdb4ab67 790 currentFiber->flags |= MICROBIT_FIBER_FLAG_PARENT;
Jonathan Austin 1:8aa5cdb4ab67 791 forkedFiber->flags |= MICROBIT_FIBER_FLAG_CHILD;
Jonathan Austin 1:8aa5cdb4ab67 792
Jonathan Austin 1:8aa5cdb4ab67 793 // Define the stack base of the forked fiber to be align with the entry point of the parent fiber
Jonathan Austin 1:8aa5cdb4ab67 794 forkedFiber->tcb.stack_base = currentFiber->tcb.SP;
Jonathan Austin 1:8aa5cdb4ab67 795
Jonathan Austin 1:8aa5cdb4ab67 796 // Ensure the stack allocation of the new fiber is large enough
Jonathan Austin 1:8aa5cdb4ab67 797 verify_stack_size(forkedFiber);
Jonathan Austin 1:8aa5cdb4ab67 798
Jonathan Austin 1:8aa5cdb4ab67 799 // Store the full context of this fiber.
Jonathan Austin 1:8aa5cdb4ab67 800 save_context(&forkedFiber->tcb, forkedFiber->stack_top);
Jonathan Austin 1:8aa5cdb4ab67 801
Jonathan Austin 1:8aa5cdb4ab67 802 // We may now be either the newly created thread, or the one that created it.
Jonathan Austin 1:8aa5cdb4ab67 803 // if the MICROBIT_FIBER_FLAG_PARENT flag is still set, we're the old thread, so
Jonathan Austin 1:8aa5cdb4ab67 804 // restore the current fiber to its stored context and we're done.
Jonathan Austin 1:8aa5cdb4ab67 805 if (currentFiber->flags & MICROBIT_FIBER_FLAG_PARENT)
Jonathan Austin 1:8aa5cdb4ab67 806 restore_register_context(&currentFiber->tcb);
Jonathan Austin 1:8aa5cdb4ab67 807
Jonathan Austin 1:8aa5cdb4ab67 808 // If we're the new thread, we must have been unblocked by the scheduler, so simply return
Jonathan Austin 1:8aa5cdb4ab67 809 // and continue processing.
Jonathan Austin 1:8aa5cdb4ab67 810 return;
Jonathan Austin 1:8aa5cdb4ab67 811 }
Jonathan Austin 1:8aa5cdb4ab67 812
Jonathan Austin 1:8aa5cdb4ab67 813 // We're in a normal scheduling context, so perform a round robin algorithm across runnable fibers.
Jonathan Austin 1:8aa5cdb4ab67 814 // OK - if we've nothing to do, then run the IDLE task (power saving sleep)
Jonathan Austin 1:8aa5cdb4ab67 815 if (runQueue == NULL)
Jonathan Austin 1:8aa5cdb4ab67 816 currentFiber = idleFiber;
Jonathan Austin 1:8aa5cdb4ab67 817
Jonathan Austin 1:8aa5cdb4ab67 818 else if (currentFiber->queue == &runQueue)
Jonathan Austin 1:8aa5cdb4ab67 819 // If the current fiber is on the run queue, round robin.
Jonathan Austin 1:8aa5cdb4ab67 820 currentFiber = currentFiber->next == NULL ? runQueue : currentFiber->next;
Jonathan Austin 1:8aa5cdb4ab67 821
Jonathan Austin 1:8aa5cdb4ab67 822 else
Jonathan Austin 1:8aa5cdb4ab67 823 // Otherwise, just pick the head of the run queue.
Jonathan Austin 1:8aa5cdb4ab67 824 currentFiber = runQueue;
Jonathan Austin 1:8aa5cdb4ab67 825
Jonathan Austin 1:8aa5cdb4ab67 826 if (currentFiber == idleFiber && oldFiber->flags & MICROBIT_FIBER_FLAG_DO_NOT_PAGE)
Jonathan Austin 1:8aa5cdb4ab67 827 {
Jonathan Austin 1:8aa5cdb4ab67 828 // Run the idle task right here using the old fiber's stack.
Jonathan Austin 1:8aa5cdb4ab67 829 // Keep idling while the runqueue is empty, or there is data to process.
Jonathan Austin 1:8aa5cdb4ab67 830
Jonathan Austin 1:8aa5cdb4ab67 831 // Run in the context of the original fiber, to preserve state of flags...
Jonathan Austin 1:8aa5cdb4ab67 832 // as we are running on top of this fiber's stack.
Jonathan Austin 1:8aa5cdb4ab67 833 currentFiber = oldFiber;
Jonathan Austin 1:8aa5cdb4ab67 834
Jonathan Austin 1:8aa5cdb4ab67 835 do
Jonathan Austin 1:8aa5cdb4ab67 836 {
Jonathan Austin 1:8aa5cdb4ab67 837 idle();
Jonathan Austin 1:8aa5cdb4ab67 838 }
Jonathan Austin 1:8aa5cdb4ab67 839 while (runQueue == NULL);
Jonathan Austin 1:8aa5cdb4ab67 840
Jonathan Austin 1:8aa5cdb4ab67 841 // Switch to a non-idle fiber.
Jonathan Austin 1:8aa5cdb4ab67 842 // If this fiber is the same as the old one then there'll be no switching at all.
Jonathan Austin 1:8aa5cdb4ab67 843 currentFiber = runQueue;
Jonathan Austin 1:8aa5cdb4ab67 844 }
Jonathan Austin 1:8aa5cdb4ab67 845
Jonathan Austin 1:8aa5cdb4ab67 846 // Swap to the context of the chosen fiber, and we're done.
Jonathan Austin 1:8aa5cdb4ab67 847 // Don't bother with the overhead of switching if there's only one fiber on the runqueue!
Jonathan Austin 1:8aa5cdb4ab67 848 if (currentFiber != oldFiber)
Jonathan Austin 1:8aa5cdb4ab67 849 {
Jonathan Austin 1:8aa5cdb4ab67 850 // Special case for the idle task, as we don't maintain a stack context (just to save memory).
Jonathan Austin 1:8aa5cdb4ab67 851 if (currentFiber == idleFiber)
Jonathan Austin 1:8aa5cdb4ab67 852 {
Jonathan Austin 1:8aa5cdb4ab67 853 idleFiber->tcb.SP = CORTEX_M0_STACK_BASE - 0x04;
Jonathan Austin 1:8aa5cdb4ab67 854 idleFiber->tcb.LR = (uint32_t) &idle_task;
Jonathan Austin 1:8aa5cdb4ab67 855 }
Jonathan Austin 1:8aa5cdb4ab67 856
Jonathan Austin 1:8aa5cdb4ab67 857 if (oldFiber == idleFiber)
Jonathan Austin 1:8aa5cdb4ab67 858 {
Jonathan Austin 1:8aa5cdb4ab67 859 // Just swap in the new fiber, and discard changes to stack and register context.
Jonathan Austin 1:8aa5cdb4ab67 860 swap_context(NULL, &currentFiber->tcb, 0, currentFiber->stack_top);
Jonathan Austin 1:8aa5cdb4ab67 861 }
Jonathan Austin 1:8aa5cdb4ab67 862 else
Jonathan Austin 1:8aa5cdb4ab67 863 {
Jonathan Austin 1:8aa5cdb4ab67 864 // Ensure the stack allocation of the fiber being scheduled out is large enough
Jonathan Austin 1:8aa5cdb4ab67 865 verify_stack_size(oldFiber);
Jonathan Austin 1:8aa5cdb4ab67 866
Jonathan Austin 1:8aa5cdb4ab67 867 // Schedule in the new fiber.
Jonathan Austin 1:8aa5cdb4ab67 868 swap_context(&oldFiber->tcb, &currentFiber->tcb, oldFiber->stack_top, currentFiber->stack_top);
Jonathan Austin 1:8aa5cdb4ab67 869 }
Jonathan Austin 1:8aa5cdb4ab67 870 }
Jonathan Austin 1:8aa5cdb4ab67 871 }
Jonathan Austin 1:8aa5cdb4ab67 872
Jonathan Austin 1:8aa5cdb4ab67 873 /**
Jonathan Austin 1:8aa5cdb4ab67 874 * Adds a component to the array of idle thread components, which are processed
Jonathan Austin 1:8aa5cdb4ab67 875 * when the run queue is empty.
Jonathan Austin 1:8aa5cdb4ab67 876 *
Jonathan Austin 1:8aa5cdb4ab67 877 * @param component The component to add to the array.
Jonathan Austin 1:8aa5cdb4ab67 878 * @return MICROBIT_OK on success or MICROBIT_NO_RESOURCES if the fiber components array is full.
Jonathan Austin 1:8aa5cdb4ab67 879 */
Jonathan Austin 1:8aa5cdb4ab67 880 int fiber_add_idle_component(MicroBitComponent *component)
Jonathan Austin 1:8aa5cdb4ab67 881 {
Jonathan Austin 1:8aa5cdb4ab67 882 int i = 0;
Jonathan Austin 1:8aa5cdb4ab67 883
Jonathan Austin 1:8aa5cdb4ab67 884 while(idleThreadComponents[i] != NULL && i < MICROBIT_IDLE_COMPONENTS)
Jonathan Austin 1:8aa5cdb4ab67 885 i++;
Jonathan Austin 1:8aa5cdb4ab67 886
Jonathan Austin 1:8aa5cdb4ab67 887 if(i == MICROBIT_IDLE_COMPONENTS)
Jonathan Austin 1:8aa5cdb4ab67 888 return MICROBIT_NO_RESOURCES;
Jonathan Austin 1:8aa5cdb4ab67 889
Jonathan Austin 1:8aa5cdb4ab67 890 idleThreadComponents[i] = component;
Jonathan Austin 1:8aa5cdb4ab67 891
Jonathan Austin 1:8aa5cdb4ab67 892 return MICROBIT_OK;
Jonathan Austin 1:8aa5cdb4ab67 893 }
Jonathan Austin 1:8aa5cdb4ab67 894
Jonathan Austin 1:8aa5cdb4ab67 895 /**
LancasterUniversity 41:da05ec75cd5d 896 * remove a component from the array of idle thread components
Jonathan Austin 1:8aa5cdb4ab67 897 *
LancasterUniversity 41:da05ec75cd5d 898 * @param component the component to remove from the idle component array.
LancasterUniversity 41:da05ec75cd5d 899 * @return MICROBIT_OK on success. MICROBIT_INVALID_PARAMETER is returned if the given component has not been previously added.
Jonathan Austin 1:8aa5cdb4ab67 900 */
Jonathan Austin 1:8aa5cdb4ab67 901 int fiber_remove_idle_component(MicroBitComponent *component)
Jonathan Austin 1:8aa5cdb4ab67 902 {
Jonathan Austin 1:8aa5cdb4ab67 903 int i = 0;
Jonathan Austin 1:8aa5cdb4ab67 904
Jonathan Austin 1:8aa5cdb4ab67 905 while(idleThreadComponents[i] != component && i < MICROBIT_IDLE_COMPONENTS)
Jonathan Austin 1:8aa5cdb4ab67 906 i++;
Jonathan Austin 1:8aa5cdb4ab67 907
Jonathan Austin 1:8aa5cdb4ab67 908 if(i == MICROBIT_IDLE_COMPONENTS)
Jonathan Austin 1:8aa5cdb4ab67 909 return MICROBIT_INVALID_PARAMETER;
Jonathan Austin 1:8aa5cdb4ab67 910
Jonathan Austin 1:8aa5cdb4ab67 911 idleThreadComponents[i] = NULL;
Jonathan Austin 1:8aa5cdb4ab67 912
Jonathan Austin 1:8aa5cdb4ab67 913 return MICROBIT_OK;
Jonathan Austin 1:8aa5cdb4ab67 914 }
Jonathan Austin 1:8aa5cdb4ab67 915
Jonathan Austin 1:8aa5cdb4ab67 916 /**
Jonathan Austin 1:8aa5cdb4ab67 917 * Set of tasks to perform when idle.
Jonathan Austin 1:8aa5cdb4ab67 918 * Service any background tasks that are required, and attempt a power efficient sleep.
Jonathan Austin 1:8aa5cdb4ab67 919 */
Jonathan Austin 1:8aa5cdb4ab67 920 void idle()
Jonathan Austin 1:8aa5cdb4ab67 921 {
Jonathan Austin 1:8aa5cdb4ab67 922 // Service background tasks
Jonathan Austin 1:8aa5cdb4ab67 923 for(int i = 0; i < MICROBIT_IDLE_COMPONENTS; i++)
Jonathan Austin 1:8aa5cdb4ab67 924 if(idleThreadComponents[i] != NULL)
Jonathan Austin 1:8aa5cdb4ab67 925 idleThreadComponents[i]->idleTick();
Jonathan Austin 1:8aa5cdb4ab67 926
Jonathan Austin 1:8aa5cdb4ab67 927 // If the above did create any useful work, enter power efficient sleep.
Jonathan Austin 1:8aa5cdb4ab67 928 if(scheduler_runqueue_empty())
Jonathan Austin 1:8aa5cdb4ab67 929 __WFE();
Jonathan Austin 1:8aa5cdb4ab67 930 }
Jonathan Austin 1:8aa5cdb4ab67 931
Jonathan Austin 1:8aa5cdb4ab67 932 /**
Jonathan Austin 1:8aa5cdb4ab67 933 * The idle task, which is called when the runtime has no fibers that require execution.
Jonathan Austin 1:8aa5cdb4ab67 934 *
Jonathan Austin 1:8aa5cdb4ab67 935 * This function typically calls idle().
Jonathan Austin 1:8aa5cdb4ab67 936 */
Jonathan Austin 1:8aa5cdb4ab67 937 void idle_task()
Jonathan Austin 1:8aa5cdb4ab67 938 {
Jonathan Austin 1:8aa5cdb4ab67 939 while(1)
Jonathan Austin 1:8aa5cdb4ab67 940 {
Jonathan Austin 1:8aa5cdb4ab67 941 idle();
Jonathan Austin 1:8aa5cdb4ab67 942 schedule();
Jonathan Austin 1:8aa5cdb4ab67 943 }
LancasterUniversity 22:23d7b9a4b082 944 }