No Changes

Dependencies:   BLE_API mbed-dev-bin nRF51822

Dependents:   microbit

Fork of microbit-dal by Lancaster University

Committer:
Jonathan Austin
Date:
Thu Apr 07 01:33:22 2016 +0100
Revision:
1:8aa5cdb4ab67
Child:
22:23d7b9a4b082
Synchronized with git rev 55cb9199

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
Jonathan Austin 1:8aa5cdb4ab67 65 // Array of components which are iterated during idle thread execution, isIdleCallbackNeeded is polled during a systemTick.
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
Jonathan Austin 1:8aa5cdb4ab67 420 // Sleep is a blocking call, so if we'r ein 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)
Jonathan Austin 1:8aa5cdb4ab67 431 f = forkedFiber;
Jonathan Austin 1:8aa5cdb4ab67 432 }
Jonathan Austin 1:8aa5cdb4ab67 433
Jonathan Austin 1:8aa5cdb4ab67 434 // Encode the event data in the context field. It's handy having a 32 bit core. :-)
Jonathan Austin 1:8aa5cdb4ab67 435 f->context = value << 16 | id;
Jonathan Austin 1:8aa5cdb4ab67 436
Jonathan Austin 1:8aa5cdb4ab67 437 // Remove ourselve from the run queue
Jonathan Austin 1:8aa5cdb4ab67 438 dequeue_fiber(f);
Jonathan Austin 1:8aa5cdb4ab67 439
Jonathan Austin 1:8aa5cdb4ab67 440 // Add ourselves to the sleep queue. We maintain strict ordering here to reduce lookup times.
Jonathan Austin 1:8aa5cdb4ab67 441 queue_fiber(f, &waitQueue);
Jonathan Austin 1:8aa5cdb4ab67 442
Jonathan Austin 1:8aa5cdb4ab67 443 // Register to receive this event, so we can wake up the fiber when it happens.
Jonathan Austin 1:8aa5cdb4ab67 444 // Special case for teh notify channel, as we always stay registered for that.
Jonathan Austin 1:8aa5cdb4ab67 445 if (id != MICROBIT_ID_NOTIFY && id != MICROBIT_ID_NOTIFY_ONE)
Jonathan Austin 1:8aa5cdb4ab67 446 messageBus->listen(id, value, scheduler_event, MESSAGE_BUS_LISTENER_IMMEDIATE);
Jonathan Austin 1:8aa5cdb4ab67 447
Jonathan Austin 1:8aa5cdb4ab67 448 return MICROBIT_OK;
Jonathan Austin 1:8aa5cdb4ab67 449 }
Jonathan Austin 1:8aa5cdb4ab67 450
Jonathan Austin 1:8aa5cdb4ab67 451 /**
Jonathan Austin 1:8aa5cdb4ab67 452 * Executes the given function asynchronously if necessary.
Jonathan Austin 1:8aa5cdb4ab67 453 *
Jonathan Austin 1:8aa5cdb4ab67 454 * Fibers are often used to run event handlers, however many of these event handlers are very simple functions
Jonathan Austin 1:8aa5cdb4ab67 455 * that complete very quickly, bringing unecessary RAM overhead.
Jonathan Austin 1:8aa5cdb4ab67 456 *
Jonathan Austin 1:8aa5cdb4ab67 457 * This function takes a snapshot of the current processor context, then attempts to optimistically call the given function directly.
Jonathan Austin 1:8aa5cdb4ab67 458 * We only create an additional fiber if that function performs a block operation.
Jonathan Austin 1:8aa5cdb4ab67 459 *
Jonathan Austin 1:8aa5cdb4ab67 460 * @param entry_fn The function to execute.
Jonathan Austin 1:8aa5cdb4ab67 461 *
Jonathan Austin 1:8aa5cdb4ab67 462 * @return MICROBIT_OK, or MICROBIT_INVALID_PARAMETER.
Jonathan Austin 1:8aa5cdb4ab67 463 */
Jonathan Austin 1:8aa5cdb4ab67 464 int invoke(void (*entry_fn)(void))
Jonathan Austin 1:8aa5cdb4ab67 465 {
Jonathan Austin 1:8aa5cdb4ab67 466 // Validate our parameters.
Jonathan Austin 1:8aa5cdb4ab67 467 if (entry_fn == NULL)
Jonathan Austin 1:8aa5cdb4ab67 468 return MICROBIT_INVALID_PARAMETER;
Jonathan Austin 1:8aa5cdb4ab67 469
Jonathan Austin 1:8aa5cdb4ab67 470 if (!fiber_scheduler_running())
Jonathan Austin 1:8aa5cdb4ab67 471 return MICROBIT_NOT_SUPPORTED;
Jonathan Austin 1:8aa5cdb4ab67 472
Jonathan Austin 1:8aa5cdb4ab67 473 if (currentFiber->flags & MICROBIT_FIBER_FLAG_FOB)
Jonathan Austin 1:8aa5cdb4ab67 474 {
Jonathan Austin 1:8aa5cdb4ab67 475 // If we attempt a fork on block whilst already in fork n block context,
Jonathan Austin 1:8aa5cdb4ab67 476 // simply launch a fiber to deal with the request and we're done.
Jonathan Austin 1:8aa5cdb4ab67 477 create_fiber(entry_fn);
Jonathan Austin 1:8aa5cdb4ab67 478 return MICROBIT_OK;
Jonathan Austin 1:8aa5cdb4ab67 479 }
Jonathan Austin 1:8aa5cdb4ab67 480
Jonathan Austin 1:8aa5cdb4ab67 481 // Snapshot current context, but also update the Link Register to
Jonathan Austin 1:8aa5cdb4ab67 482 // refer to our calling function.
Jonathan Austin 1:8aa5cdb4ab67 483 save_register_context(&currentFiber->tcb);
Jonathan Austin 1:8aa5cdb4ab67 484
Jonathan Austin 1:8aa5cdb4ab67 485 // If we're here, there are two possibilities:
Jonathan Austin 1:8aa5cdb4ab67 486 // 1) We're about to attempt to execute the user code
Jonathan Austin 1:8aa5cdb4ab67 487 // 2) We've already tried to execute the code, it blocked, and we've backtracked.
Jonathan Austin 1:8aa5cdb4ab67 488
Jonathan Austin 1:8aa5cdb4ab67 489 // If we're returning from the user function and we forked another fiber then cleanup and exit.
Jonathan Austin 1:8aa5cdb4ab67 490 if (currentFiber->flags & MICROBIT_FIBER_FLAG_PARENT)
Jonathan Austin 1:8aa5cdb4ab67 491 {
Jonathan Austin 1:8aa5cdb4ab67 492 currentFiber->flags &= ~MICROBIT_FIBER_FLAG_FOB;
Jonathan Austin 1:8aa5cdb4ab67 493 currentFiber->flags &= ~MICROBIT_FIBER_FLAG_PARENT;
Jonathan Austin 1:8aa5cdb4ab67 494 return MICROBIT_OK;
Jonathan Austin 1:8aa5cdb4ab67 495 }
Jonathan Austin 1:8aa5cdb4ab67 496
Jonathan Austin 1:8aa5cdb4ab67 497 // Otherwise, we're here for the first time. Enter FORK ON BLOCK mode, and
Jonathan Austin 1:8aa5cdb4ab67 498 // execute the function directly. If the code tries to block, we detect this and
Jonathan Austin 1:8aa5cdb4ab67 499 // spawn a thread to deal with it.
Jonathan Austin 1:8aa5cdb4ab67 500 currentFiber->flags |= MICROBIT_FIBER_FLAG_FOB;
Jonathan Austin 1:8aa5cdb4ab67 501 entry_fn();
Jonathan Austin 1:8aa5cdb4ab67 502 currentFiber->flags &= ~MICROBIT_FIBER_FLAG_FOB;
Jonathan Austin 1:8aa5cdb4ab67 503
Jonathan Austin 1:8aa5cdb4ab67 504 // If this is is an exiting fiber that for spawned to handle a blocking call, recycle it.
Jonathan Austin 1:8aa5cdb4ab67 505 // The fiber will then re-enter the scheduler, so no need for further cleanup.
Jonathan Austin 1:8aa5cdb4ab67 506 if (currentFiber->flags & MICROBIT_FIBER_FLAG_CHILD)
Jonathan Austin 1:8aa5cdb4ab67 507 release_fiber();
Jonathan Austin 1:8aa5cdb4ab67 508
Jonathan Austin 1:8aa5cdb4ab67 509 return MICROBIT_OK;
Jonathan Austin 1:8aa5cdb4ab67 510 }
Jonathan Austin 1:8aa5cdb4ab67 511
Jonathan Austin 1:8aa5cdb4ab67 512 /**
Jonathan Austin 1:8aa5cdb4ab67 513 * Executes the given function asynchronously if necessary, and offers the ability to provide a parameter.
Jonathan Austin 1:8aa5cdb4ab67 514 *
Jonathan Austin 1:8aa5cdb4ab67 515 * Fibers are often used to run event handlers, however many of these event handlers are very simple functions
Jonathan Austin 1:8aa5cdb4ab67 516 * that complete very quickly, bringing unecessary RAM. overhead
Jonathan Austin 1:8aa5cdb4ab67 517 *
Jonathan Austin 1:8aa5cdb4ab67 518 * This function takes a snapshot of the current fiber context, then attempt to optimistically call the given function directly.
Jonathan Austin 1:8aa5cdb4ab67 519 * We only create an additional fiber if that function performs a block operation.
Jonathan Austin 1:8aa5cdb4ab67 520 *
Jonathan Austin 1:8aa5cdb4ab67 521 * @param entry_fn The function to execute.
Jonathan Austin 1:8aa5cdb4ab67 522 *
Jonathan Austin 1:8aa5cdb4ab67 523 * @param param an untyped parameter passed into the entry_fn and completion_fn.
Jonathan Austin 1:8aa5cdb4ab67 524 *
Jonathan Austin 1:8aa5cdb4ab67 525 * @return MICROBIT_OK, or MICROBIT_INVALID_PARAMETER.
Jonathan Austin 1:8aa5cdb4ab67 526 */
Jonathan Austin 1:8aa5cdb4ab67 527 int invoke(void (*entry_fn)(void *), void *param)
Jonathan Austin 1:8aa5cdb4ab67 528 {
Jonathan Austin 1:8aa5cdb4ab67 529 // Validate our parameters.
Jonathan Austin 1:8aa5cdb4ab67 530 if (entry_fn == NULL)
Jonathan Austin 1:8aa5cdb4ab67 531 return MICROBIT_INVALID_PARAMETER;
Jonathan Austin 1:8aa5cdb4ab67 532
Jonathan Austin 1:8aa5cdb4ab67 533 if (!fiber_scheduler_running())
Jonathan Austin 1:8aa5cdb4ab67 534 return MICROBIT_NOT_SUPPORTED;
Jonathan Austin 1:8aa5cdb4ab67 535
Jonathan Austin 1:8aa5cdb4ab67 536 if (currentFiber->flags & (MICROBIT_FIBER_FLAG_FOB | MICROBIT_FIBER_FLAG_PARENT | MICROBIT_FIBER_FLAG_CHILD))
Jonathan Austin 1:8aa5cdb4ab67 537 {
Jonathan Austin 1:8aa5cdb4ab67 538 // If we attempt a fork on block whilst already in a fork on block context,
Jonathan Austin 1:8aa5cdb4ab67 539 // simply launch a fiber to deal with the request and we're done.
Jonathan Austin 1:8aa5cdb4ab67 540 create_fiber(entry_fn, param);
Jonathan Austin 1:8aa5cdb4ab67 541 return MICROBIT_OK;
Jonathan Austin 1:8aa5cdb4ab67 542 }
Jonathan Austin 1:8aa5cdb4ab67 543
Jonathan Austin 1:8aa5cdb4ab67 544 // Snapshot current context, but also update the Link Register to
Jonathan Austin 1:8aa5cdb4ab67 545 // refer to our calling function.
Jonathan Austin 1:8aa5cdb4ab67 546 save_register_context(&currentFiber->tcb);
Jonathan Austin 1:8aa5cdb4ab67 547
Jonathan Austin 1:8aa5cdb4ab67 548 // If we're here, there are two possibilities:
Jonathan Austin 1:8aa5cdb4ab67 549 // 1) We're about to attempt to execute the user code
Jonathan Austin 1:8aa5cdb4ab67 550 // 2) We've already tried to execute the code, it blocked, and we've backtracked.
Jonathan Austin 1:8aa5cdb4ab67 551
Jonathan Austin 1:8aa5cdb4ab67 552 // If we're returning from the user function and we forked another fiber then cleanup and exit.
Jonathan Austin 1:8aa5cdb4ab67 553 if (currentFiber->flags & MICROBIT_FIBER_FLAG_PARENT)
Jonathan Austin 1:8aa5cdb4ab67 554 {
Jonathan Austin 1:8aa5cdb4ab67 555 currentFiber->flags &= ~MICROBIT_FIBER_FLAG_FOB;
Jonathan Austin 1:8aa5cdb4ab67 556 currentFiber->flags &= ~MICROBIT_FIBER_FLAG_PARENT;
Jonathan Austin 1:8aa5cdb4ab67 557 return MICROBIT_OK;
Jonathan Austin 1:8aa5cdb4ab67 558 }
Jonathan Austin 1:8aa5cdb4ab67 559
Jonathan Austin 1:8aa5cdb4ab67 560 // Otherwise, we're here for the first time. Enter FORK ON BLOCK mode, and
Jonathan Austin 1:8aa5cdb4ab67 561 // execute the function directly. If the code tries to block, we detect this and
Jonathan Austin 1:8aa5cdb4ab67 562 // spawn a thread to deal with it.
Jonathan Austin 1:8aa5cdb4ab67 563 currentFiber->flags |= MICROBIT_FIBER_FLAG_FOB;
Jonathan Austin 1:8aa5cdb4ab67 564 entry_fn(param);
Jonathan Austin 1:8aa5cdb4ab67 565 currentFiber->flags &= ~MICROBIT_FIBER_FLAG_FOB;
Jonathan Austin 1:8aa5cdb4ab67 566
Jonathan Austin 1:8aa5cdb4ab67 567 // If this is is an exiting fiber that for spawned to handle a blocking call, recycle it.
Jonathan Austin 1:8aa5cdb4ab67 568 // The fiber will then re-enter the scheduler, so no need for further cleanup.
Jonathan Austin 1:8aa5cdb4ab67 569 if (currentFiber->flags & MICROBIT_FIBER_FLAG_CHILD)
Jonathan Austin 1:8aa5cdb4ab67 570 release_fiber(param);
Jonathan Austin 1:8aa5cdb4ab67 571
Jonathan Austin 1:8aa5cdb4ab67 572 return MICROBIT_OK;
Jonathan Austin 1:8aa5cdb4ab67 573 }
Jonathan Austin 1:8aa5cdb4ab67 574
Jonathan Austin 1:8aa5cdb4ab67 575 /**
Jonathan Austin 1:8aa5cdb4ab67 576 * Launches a fiber.
Jonathan Austin 1:8aa5cdb4ab67 577 *
Jonathan Austin 1:8aa5cdb4ab67 578 * @param ep the entry point for the fiber.
Jonathan Austin 1:8aa5cdb4ab67 579 *
Jonathan Austin 1:8aa5cdb4ab67 580 * @param cp the completion routine after ep has finished execution
Jonathan Austin 1:8aa5cdb4ab67 581 */
Jonathan Austin 1:8aa5cdb4ab67 582 void launch_new_fiber(void (*ep)(void), void (*cp)(void))
Jonathan Austin 1:8aa5cdb4ab67 583 {
Jonathan Austin 1:8aa5cdb4ab67 584 // Execute the thread's entrypoint
Jonathan Austin 1:8aa5cdb4ab67 585 ep();
Jonathan Austin 1:8aa5cdb4ab67 586
Jonathan Austin 1:8aa5cdb4ab67 587 // Execute the thread's completion routine;
Jonathan Austin 1:8aa5cdb4ab67 588 cp();
Jonathan Austin 1:8aa5cdb4ab67 589
Jonathan Austin 1:8aa5cdb4ab67 590 // If we get here, then the completion routine didn't recycle the fiber... so do it anyway. :-)
Jonathan Austin 1:8aa5cdb4ab67 591 release_fiber();
Jonathan Austin 1:8aa5cdb4ab67 592 }
Jonathan Austin 1:8aa5cdb4ab67 593
Jonathan Austin 1:8aa5cdb4ab67 594 /**
Jonathan Austin 1:8aa5cdb4ab67 595 * Launches a fiber with a parameter
Jonathan Austin 1:8aa5cdb4ab67 596 *
Jonathan Austin 1:8aa5cdb4ab67 597 * @param ep the entry point for the fiber.
Jonathan Austin 1:8aa5cdb4ab67 598 *
Jonathan Austin 1:8aa5cdb4ab67 599 * @param cp the completion routine after ep has finished execution
Jonathan Austin 1:8aa5cdb4ab67 600 *
Jonathan Austin 1:8aa5cdb4ab67 601 * @param pm the parameter to provide to ep and cp.
Jonathan Austin 1:8aa5cdb4ab67 602 */
Jonathan Austin 1:8aa5cdb4ab67 603 void launch_new_fiber_param(void (*ep)(void *), void (*cp)(void *), void *pm)
Jonathan Austin 1:8aa5cdb4ab67 604 {
Jonathan Austin 1:8aa5cdb4ab67 605 // Execute the thread's entrypoint.
Jonathan Austin 1:8aa5cdb4ab67 606 ep(pm);
Jonathan Austin 1:8aa5cdb4ab67 607
Jonathan Austin 1:8aa5cdb4ab67 608 // Execute the thread's completion routine.
Jonathan Austin 1:8aa5cdb4ab67 609 cp(pm);
Jonathan Austin 1:8aa5cdb4ab67 610
Jonathan Austin 1:8aa5cdb4ab67 611 // If we get here, then the completion routine didn't recycle the fiber... so do it anyway. :-)
Jonathan Austin 1:8aa5cdb4ab67 612 release_fiber(pm);
Jonathan Austin 1:8aa5cdb4ab67 613 }
Jonathan Austin 1:8aa5cdb4ab67 614
Jonathan Austin 1:8aa5cdb4ab67 615 Fiber *__create_fiber(uint32_t ep, uint32_t cp, uint32_t pm, int parameterised)
Jonathan Austin 1:8aa5cdb4ab67 616 {
Jonathan Austin 1:8aa5cdb4ab67 617 // Validate our parameters.
Jonathan Austin 1:8aa5cdb4ab67 618 if (ep == 0 || cp == 0)
Jonathan Austin 1:8aa5cdb4ab67 619 return NULL;
Jonathan Austin 1:8aa5cdb4ab67 620
Jonathan Austin 1:8aa5cdb4ab67 621 // Allocate a TCB from the new fiber. This will come from the fiber pool if availiable,
Jonathan Austin 1:8aa5cdb4ab67 622 // else a new one will be allocated on the heap.
Jonathan Austin 1:8aa5cdb4ab67 623 Fiber *newFiber = getFiberContext();
Jonathan Austin 1:8aa5cdb4ab67 624
Jonathan Austin 1:8aa5cdb4ab67 625 // If we're out of memory, there's nothing we can do.
Jonathan Austin 1:8aa5cdb4ab67 626 if (newFiber == NULL)
Jonathan Austin 1:8aa5cdb4ab67 627 return NULL;
Jonathan Austin 1:8aa5cdb4ab67 628
Jonathan Austin 1:8aa5cdb4ab67 629 newFiber->tcb.R0 = (uint32_t) ep;
Jonathan Austin 1:8aa5cdb4ab67 630 newFiber->tcb.R1 = (uint32_t) cp;
Jonathan Austin 1:8aa5cdb4ab67 631 newFiber->tcb.R2 = (uint32_t) pm;
Jonathan Austin 1:8aa5cdb4ab67 632
Jonathan Austin 1:8aa5cdb4ab67 633 // Set the stack and assign the link register to refer to the appropriate entry point wrapper.
Jonathan Austin 1:8aa5cdb4ab67 634 newFiber->tcb.SP = CORTEX_M0_STACK_BASE - 0x04;
Jonathan Austin 1:8aa5cdb4ab67 635 newFiber->tcb.LR = parameterised ? (uint32_t) &launch_new_fiber_param : (uint32_t) &launch_new_fiber;
Jonathan Austin 1:8aa5cdb4ab67 636
Jonathan Austin 1:8aa5cdb4ab67 637 // Add new fiber to the run queue.
Jonathan Austin 1:8aa5cdb4ab67 638 queue_fiber(newFiber, &runQueue);
Jonathan Austin 1:8aa5cdb4ab67 639
Jonathan Austin 1:8aa5cdb4ab67 640 return newFiber;
Jonathan Austin 1:8aa5cdb4ab67 641 }
Jonathan Austin 1:8aa5cdb4ab67 642
Jonathan Austin 1:8aa5cdb4ab67 643 /**
Jonathan Austin 1:8aa5cdb4ab67 644 * Creates a new Fiber, and launches it.
Jonathan Austin 1:8aa5cdb4ab67 645 *
Jonathan Austin 1:8aa5cdb4ab67 646 * @param entry_fn The function the new Fiber will begin execution in.
Jonathan Austin 1:8aa5cdb4ab67 647 *
Jonathan Austin 1:8aa5cdb4ab67 648 * @param completion_fn The function called when the thread completes execution of entry_fn.
Jonathan Austin 1:8aa5cdb4ab67 649 * Defaults to release_fiber.
Jonathan Austin 1:8aa5cdb4ab67 650 *
Jonathan Austin 1:8aa5cdb4ab67 651 * @return The new Fiber, or NULL if the operation could not be completed.
Jonathan Austin 1:8aa5cdb4ab67 652 */
Jonathan Austin 1:8aa5cdb4ab67 653 Fiber *create_fiber(void (*entry_fn)(void), void (*completion_fn)(void))
Jonathan Austin 1:8aa5cdb4ab67 654 {
Jonathan Austin 1:8aa5cdb4ab67 655 if (!fiber_scheduler_running())
Jonathan Austin 1:8aa5cdb4ab67 656 return NULL;
Jonathan Austin 1:8aa5cdb4ab67 657
Jonathan Austin 1:8aa5cdb4ab67 658 return __create_fiber((uint32_t) entry_fn, (uint32_t)completion_fn, 0, 0);
Jonathan Austin 1:8aa5cdb4ab67 659 }
Jonathan Austin 1:8aa5cdb4ab67 660
Jonathan Austin 1:8aa5cdb4ab67 661
Jonathan Austin 1:8aa5cdb4ab67 662 /**
Jonathan Austin 1:8aa5cdb4ab67 663 * Creates a new parameterised Fiber, and launches it.
Jonathan Austin 1:8aa5cdb4ab67 664 *
Jonathan Austin 1:8aa5cdb4ab67 665 * @param entry_fn The function the new Fiber will begin execution in.
Jonathan Austin 1:8aa5cdb4ab67 666 *
Jonathan Austin 1:8aa5cdb4ab67 667 * @param param an untyped parameter passed into the entry_fn and completion_fn.
Jonathan Austin 1:8aa5cdb4ab67 668 *
Jonathan Austin 1:8aa5cdb4ab67 669 * @param completion_fn The function called when the thread completes execution of entry_fn.
Jonathan Austin 1:8aa5cdb4ab67 670 * Defaults to release_fiber.
Jonathan Austin 1:8aa5cdb4ab67 671 *
Jonathan Austin 1:8aa5cdb4ab67 672 * @return The new Fiber, or NULL if the operation could not be completed.
Jonathan Austin 1:8aa5cdb4ab67 673 */
Jonathan Austin 1:8aa5cdb4ab67 674 Fiber *create_fiber(void (*entry_fn)(void *), void *param, void (*completion_fn)(void *))
Jonathan Austin 1:8aa5cdb4ab67 675 {
Jonathan Austin 1:8aa5cdb4ab67 676 if (!fiber_scheduler_running())
Jonathan Austin 1:8aa5cdb4ab67 677 return NULL;
Jonathan Austin 1:8aa5cdb4ab67 678
Jonathan Austin 1:8aa5cdb4ab67 679 return __create_fiber((uint32_t) entry_fn, (uint32_t)completion_fn, (uint32_t) param, 1);
Jonathan Austin 1:8aa5cdb4ab67 680 }
Jonathan Austin 1:8aa5cdb4ab67 681
Jonathan Austin 1:8aa5cdb4ab67 682 /**
Jonathan Austin 1:8aa5cdb4ab67 683 * Exit point for all fibers.
Jonathan Austin 1:8aa5cdb4ab67 684 *
Jonathan Austin 1:8aa5cdb4ab67 685 * Any fiber reaching the end of its entry function will return here for recycling.
Jonathan Austin 1:8aa5cdb4ab67 686 */
Jonathan Austin 1:8aa5cdb4ab67 687 void release_fiber(void *)
Jonathan Austin 1:8aa5cdb4ab67 688 {
Jonathan Austin 1:8aa5cdb4ab67 689 if (!fiber_scheduler_running())
Jonathan Austin 1:8aa5cdb4ab67 690 return;
Jonathan Austin 1:8aa5cdb4ab67 691
Jonathan Austin 1:8aa5cdb4ab67 692 release_fiber();
Jonathan Austin 1:8aa5cdb4ab67 693 }
Jonathan Austin 1:8aa5cdb4ab67 694
Jonathan Austin 1:8aa5cdb4ab67 695 /**
Jonathan Austin 1:8aa5cdb4ab67 696 * Exit point for all fibers.
Jonathan Austin 1:8aa5cdb4ab67 697 *
Jonathan Austin 1:8aa5cdb4ab67 698 * Any fiber reaching the end of its entry function will return here for recycling.
Jonathan Austin 1:8aa5cdb4ab67 699 */
Jonathan Austin 1:8aa5cdb4ab67 700 void release_fiber(void)
Jonathan Austin 1:8aa5cdb4ab67 701 {
Jonathan Austin 1:8aa5cdb4ab67 702 if (!fiber_scheduler_running())
Jonathan Austin 1:8aa5cdb4ab67 703 return;
Jonathan Austin 1:8aa5cdb4ab67 704
Jonathan Austin 1:8aa5cdb4ab67 705 // Remove ourselves form the runqueue.
Jonathan Austin 1:8aa5cdb4ab67 706 dequeue_fiber(currentFiber);
Jonathan Austin 1:8aa5cdb4ab67 707
Jonathan Austin 1:8aa5cdb4ab67 708 // Add ourselves to the list of free fibers
Jonathan Austin 1:8aa5cdb4ab67 709 queue_fiber(currentFiber, &fiberPool);
Jonathan Austin 1:8aa5cdb4ab67 710
Jonathan Austin 1:8aa5cdb4ab67 711 // Find something else to do!
Jonathan Austin 1:8aa5cdb4ab67 712 schedule();
Jonathan Austin 1:8aa5cdb4ab67 713 }
Jonathan Austin 1:8aa5cdb4ab67 714
Jonathan Austin 1:8aa5cdb4ab67 715 /**
Jonathan Austin 1:8aa5cdb4ab67 716 * Resizes the stack allocation of the current fiber if necessary to hold the system stack.
Jonathan Austin 1:8aa5cdb4ab67 717 *
Jonathan Austin 1:8aa5cdb4ab67 718 * If the stack allocation is large enough to hold the current system stack, then this function does nothing.
Jonathan Austin 1:8aa5cdb4ab67 719 * Otherwise, the the current allocation of the fiber is freed, and a larger block is allocated.
Jonathan Austin 1:8aa5cdb4ab67 720 *
Jonathan Austin 1:8aa5cdb4ab67 721 * @param f The fiber context to verify.
Jonathan Austin 1:8aa5cdb4ab67 722 *
Jonathan Austin 1:8aa5cdb4ab67 723 * @return The stack depth of the given fiber.
Jonathan Austin 1:8aa5cdb4ab67 724 */
Jonathan Austin 1:8aa5cdb4ab67 725 void verify_stack_size(Fiber *f)
Jonathan Austin 1:8aa5cdb4ab67 726 {
Jonathan Austin 1:8aa5cdb4ab67 727 // Ensure the stack buffer is large enough to hold the stack Reallocate if necessary.
Jonathan Austin 1:8aa5cdb4ab67 728 uint32_t stackDepth;
Jonathan Austin 1:8aa5cdb4ab67 729 uint32_t bufferSize;
Jonathan Austin 1:8aa5cdb4ab67 730
Jonathan Austin 1:8aa5cdb4ab67 731 // Calculate the stack depth.
Jonathan Austin 1:8aa5cdb4ab67 732 stackDepth = f->tcb.stack_base - ((uint32_t) __get_MSP());
Jonathan Austin 1:8aa5cdb4ab67 733
Jonathan Austin 1:8aa5cdb4ab67 734 // Calculate the size of our allocated stack buffer
Jonathan Austin 1:8aa5cdb4ab67 735 bufferSize = f->stack_top - f->stack_bottom;
Jonathan Austin 1:8aa5cdb4ab67 736
Jonathan Austin 1:8aa5cdb4ab67 737 // If we're too small, increase our buffer size.
Jonathan Austin 1:8aa5cdb4ab67 738 if (bufferSize < stackDepth)
Jonathan Austin 1:8aa5cdb4ab67 739 {
Jonathan Austin 1:8aa5cdb4ab67 740 // To ease heap churn, we choose the next largest multple of 32 bytes.
Jonathan Austin 1:8aa5cdb4ab67 741 bufferSize = (stackDepth + 32) & 0xffffffe0;
Jonathan Austin 1:8aa5cdb4ab67 742
Jonathan Austin 1:8aa5cdb4ab67 743 // Release the old memory
Jonathan Austin 1:8aa5cdb4ab67 744 if (f->stack_bottom != 0)
Jonathan Austin 1:8aa5cdb4ab67 745 free((void *)f->stack_bottom);
Jonathan Austin 1:8aa5cdb4ab67 746
Jonathan Austin 1:8aa5cdb4ab67 747 // Allocate a new one of the appropriate size.
Jonathan Austin 1:8aa5cdb4ab67 748 f->stack_bottom = (uint32_t) malloc(bufferSize);
Jonathan Austin 1:8aa5cdb4ab67 749
Jonathan Austin 1:8aa5cdb4ab67 750 // Recalculate where the top of the stack is and we're done.
Jonathan Austin 1:8aa5cdb4ab67 751 f->stack_top = f->stack_bottom + bufferSize;
Jonathan Austin 1:8aa5cdb4ab67 752 }
Jonathan Austin 1:8aa5cdb4ab67 753 }
Jonathan Austin 1:8aa5cdb4ab67 754
Jonathan Austin 1:8aa5cdb4ab67 755 /**
Jonathan Austin 1:8aa5cdb4ab67 756 * Determines if any fibers are waiting to be scheduled.
Jonathan Austin 1:8aa5cdb4ab67 757 *
Jonathan Austin 1:8aa5cdb4ab67 758 * @return The number of fibers currently on the run queue
Jonathan Austin 1:8aa5cdb4ab67 759 */
Jonathan Austin 1:8aa5cdb4ab67 760 int scheduler_runqueue_empty()
Jonathan Austin 1:8aa5cdb4ab67 761 {
Jonathan Austin 1:8aa5cdb4ab67 762 return (runQueue == NULL);
Jonathan Austin 1:8aa5cdb4ab67 763 }
Jonathan Austin 1:8aa5cdb4ab67 764
Jonathan Austin 1:8aa5cdb4ab67 765 /**
Jonathan Austin 1:8aa5cdb4ab67 766 * Calls the Fiber scheduler.
Jonathan Austin 1:8aa5cdb4ab67 767 * The calling Fiber will likely be blocked, and control given to another waiting fiber.
Jonathan Austin 1:8aa5cdb4ab67 768 * Call this function to yield control of the processor when you have nothing more to do.
Jonathan Austin 1:8aa5cdb4ab67 769 */
Jonathan Austin 1:8aa5cdb4ab67 770 void schedule()
Jonathan Austin 1:8aa5cdb4ab67 771 {
Jonathan Austin 1:8aa5cdb4ab67 772 if (!fiber_scheduler_running())
Jonathan Austin 1:8aa5cdb4ab67 773 return;
Jonathan Austin 1:8aa5cdb4ab67 774
Jonathan Austin 1:8aa5cdb4ab67 775 // First, take a reference to the currently running fiber;
Jonathan Austin 1:8aa5cdb4ab67 776 Fiber *oldFiber = currentFiber;
Jonathan Austin 1:8aa5cdb4ab67 777
Jonathan Austin 1:8aa5cdb4ab67 778 // First, see if we're in Fork on Block context. If so, we simply want to store the full context
Jonathan Austin 1:8aa5cdb4ab67 779 // of the currently running thread in a newly created fiber, and restore the context of the
Jonathan Austin 1:8aa5cdb4ab67 780 // currently running fiber, back to the point where it entered FOB.
Jonathan Austin 1:8aa5cdb4ab67 781
Jonathan Austin 1:8aa5cdb4ab67 782 if (currentFiber->flags & MICROBIT_FIBER_FLAG_FOB)
Jonathan Austin 1:8aa5cdb4ab67 783 {
Jonathan Austin 1:8aa5cdb4ab67 784 // Record that the fibers have a parent/child relationship
Jonathan Austin 1:8aa5cdb4ab67 785 currentFiber->flags |= MICROBIT_FIBER_FLAG_PARENT;
Jonathan Austin 1:8aa5cdb4ab67 786 forkedFiber->flags |= MICROBIT_FIBER_FLAG_CHILD;
Jonathan Austin 1:8aa5cdb4ab67 787
Jonathan Austin 1:8aa5cdb4ab67 788 // Define the stack base of the forked fiber to be align with the entry point of the parent fiber
Jonathan Austin 1:8aa5cdb4ab67 789 forkedFiber->tcb.stack_base = currentFiber->tcb.SP;
Jonathan Austin 1:8aa5cdb4ab67 790
Jonathan Austin 1:8aa5cdb4ab67 791 // Ensure the stack allocation of the new fiber is large enough
Jonathan Austin 1:8aa5cdb4ab67 792 verify_stack_size(forkedFiber);
Jonathan Austin 1:8aa5cdb4ab67 793
Jonathan Austin 1:8aa5cdb4ab67 794 // Store the full context of this fiber.
Jonathan Austin 1:8aa5cdb4ab67 795 save_context(&forkedFiber->tcb, forkedFiber->stack_top);
Jonathan Austin 1:8aa5cdb4ab67 796
Jonathan Austin 1:8aa5cdb4ab67 797 // We may now be either the newly created thread, or the one that created it.
Jonathan Austin 1:8aa5cdb4ab67 798 // if the MICROBIT_FIBER_FLAG_PARENT flag is still set, we're the old thread, so
Jonathan Austin 1:8aa5cdb4ab67 799 // restore the current fiber to its stored context and we're done.
Jonathan Austin 1:8aa5cdb4ab67 800 if (currentFiber->flags & MICROBIT_FIBER_FLAG_PARENT)
Jonathan Austin 1:8aa5cdb4ab67 801 restore_register_context(&currentFiber->tcb);
Jonathan Austin 1:8aa5cdb4ab67 802
Jonathan Austin 1:8aa5cdb4ab67 803 // If we're the new thread, we must have been unblocked by the scheduler, so simply return
Jonathan Austin 1:8aa5cdb4ab67 804 // and continue processing.
Jonathan Austin 1:8aa5cdb4ab67 805 return;
Jonathan Austin 1:8aa5cdb4ab67 806 }
Jonathan Austin 1:8aa5cdb4ab67 807
Jonathan Austin 1:8aa5cdb4ab67 808 // We're in a normal scheduling context, so perform a round robin algorithm across runnable fibers.
Jonathan Austin 1:8aa5cdb4ab67 809 // OK - if we've nothing to do, then run the IDLE task (power saving sleep)
Jonathan Austin 1:8aa5cdb4ab67 810 if (runQueue == NULL)
Jonathan Austin 1:8aa5cdb4ab67 811 currentFiber = idleFiber;
Jonathan Austin 1:8aa5cdb4ab67 812
Jonathan Austin 1:8aa5cdb4ab67 813 else if (currentFiber->queue == &runQueue)
Jonathan Austin 1:8aa5cdb4ab67 814 // If the current fiber is on the run queue, round robin.
Jonathan Austin 1:8aa5cdb4ab67 815 currentFiber = currentFiber->next == NULL ? runQueue : currentFiber->next;
Jonathan Austin 1:8aa5cdb4ab67 816
Jonathan Austin 1:8aa5cdb4ab67 817 else
Jonathan Austin 1:8aa5cdb4ab67 818 // Otherwise, just pick the head of the run queue.
Jonathan Austin 1:8aa5cdb4ab67 819 currentFiber = runQueue;
Jonathan Austin 1:8aa5cdb4ab67 820
Jonathan Austin 1:8aa5cdb4ab67 821 if (currentFiber == idleFiber && oldFiber->flags & MICROBIT_FIBER_FLAG_DO_NOT_PAGE)
Jonathan Austin 1:8aa5cdb4ab67 822 {
Jonathan Austin 1:8aa5cdb4ab67 823 // Run the idle task right here using the old fiber's stack.
Jonathan Austin 1:8aa5cdb4ab67 824 // Keep idling while the runqueue is empty, or there is data to process.
Jonathan Austin 1:8aa5cdb4ab67 825
Jonathan Austin 1:8aa5cdb4ab67 826 // Run in the context of the original fiber, to preserve state of flags...
Jonathan Austin 1:8aa5cdb4ab67 827 // as we are running on top of this fiber's stack.
Jonathan Austin 1:8aa5cdb4ab67 828 currentFiber = oldFiber;
Jonathan Austin 1:8aa5cdb4ab67 829
Jonathan Austin 1:8aa5cdb4ab67 830 do
Jonathan Austin 1:8aa5cdb4ab67 831 {
Jonathan Austin 1:8aa5cdb4ab67 832 idle();
Jonathan Austin 1:8aa5cdb4ab67 833 }
Jonathan Austin 1:8aa5cdb4ab67 834 while (runQueue == NULL);
Jonathan Austin 1:8aa5cdb4ab67 835
Jonathan Austin 1:8aa5cdb4ab67 836 // Switch to a non-idle fiber.
Jonathan Austin 1:8aa5cdb4ab67 837 // If this fiber is the same as the old one then there'll be no switching at all.
Jonathan Austin 1:8aa5cdb4ab67 838 currentFiber = runQueue;
Jonathan Austin 1:8aa5cdb4ab67 839 }
Jonathan Austin 1:8aa5cdb4ab67 840
Jonathan Austin 1:8aa5cdb4ab67 841 // Swap to the context of the chosen fiber, and we're done.
Jonathan Austin 1:8aa5cdb4ab67 842 // Don't bother with the overhead of switching if there's only one fiber on the runqueue!
Jonathan Austin 1:8aa5cdb4ab67 843 if (currentFiber != oldFiber)
Jonathan Austin 1:8aa5cdb4ab67 844 {
Jonathan Austin 1:8aa5cdb4ab67 845 // Special case for the idle task, as we don't maintain a stack context (just to save memory).
Jonathan Austin 1:8aa5cdb4ab67 846 if (currentFiber == idleFiber)
Jonathan Austin 1:8aa5cdb4ab67 847 {
Jonathan Austin 1:8aa5cdb4ab67 848 idleFiber->tcb.SP = CORTEX_M0_STACK_BASE - 0x04;
Jonathan Austin 1:8aa5cdb4ab67 849 idleFiber->tcb.LR = (uint32_t) &idle_task;
Jonathan Austin 1:8aa5cdb4ab67 850 }
Jonathan Austin 1:8aa5cdb4ab67 851
Jonathan Austin 1:8aa5cdb4ab67 852 if (oldFiber == idleFiber)
Jonathan Austin 1:8aa5cdb4ab67 853 {
Jonathan Austin 1:8aa5cdb4ab67 854 // Just swap in the new fiber, and discard changes to stack and register context.
Jonathan Austin 1:8aa5cdb4ab67 855 swap_context(NULL, &currentFiber->tcb, 0, currentFiber->stack_top);
Jonathan Austin 1:8aa5cdb4ab67 856 }
Jonathan Austin 1:8aa5cdb4ab67 857 else
Jonathan Austin 1:8aa5cdb4ab67 858 {
Jonathan Austin 1:8aa5cdb4ab67 859 // Ensure the stack allocation of the fiber being scheduled out is large enough
Jonathan Austin 1:8aa5cdb4ab67 860 verify_stack_size(oldFiber);
Jonathan Austin 1:8aa5cdb4ab67 861
Jonathan Austin 1:8aa5cdb4ab67 862 // Schedule in the new fiber.
Jonathan Austin 1:8aa5cdb4ab67 863 swap_context(&oldFiber->tcb, &currentFiber->tcb, oldFiber->stack_top, currentFiber->stack_top);
Jonathan Austin 1:8aa5cdb4ab67 864 }
Jonathan Austin 1:8aa5cdb4ab67 865 }
Jonathan Austin 1:8aa5cdb4ab67 866 }
Jonathan Austin 1:8aa5cdb4ab67 867
Jonathan Austin 1:8aa5cdb4ab67 868 /**
Jonathan Austin 1:8aa5cdb4ab67 869 * Adds a component to the array of idle thread components, which are processed
Jonathan Austin 1:8aa5cdb4ab67 870 * when the run queue is empty.
Jonathan Austin 1:8aa5cdb4ab67 871 *
Jonathan Austin 1:8aa5cdb4ab67 872 * The system timer will poll isIdleCallbackNeeded on each component to determine
Jonathan Austin 1:8aa5cdb4ab67 873 * if the scheduler should schedule the idle_task imminently.
Jonathan Austin 1:8aa5cdb4ab67 874 *
Jonathan Austin 1:8aa5cdb4ab67 875 * @param component The component to add to the array.
Jonathan Austin 1:8aa5cdb4ab67 876 *
Jonathan Austin 1:8aa5cdb4ab67 877 * @return MICROBIT_OK on success or MICROBIT_NO_RESOURCES if the fiber components array is full.
Jonathan Austin 1:8aa5cdb4ab67 878 *
Jonathan Austin 1:8aa5cdb4ab67 879 * @code
Jonathan Austin 1:8aa5cdb4ab67 880 * MicroBitI2C i2c(I2C_SDA0, I2C_SCL0);
Jonathan Austin 1:8aa5cdb4ab67 881 *
Jonathan Austin 1:8aa5cdb4ab67 882 * // heap allocated - otherwise it will be paged out!
Jonathan Austin 1:8aa5cdb4ab67 883 * MicroBitAccelerometer* accelerometer = new MicroBitAccelerometer(i2c);
Jonathan Austin 1:8aa5cdb4ab67 884 *
Jonathan Austin 1:8aa5cdb4ab67 885 * fiber_add_idle_component(accelerometer);
Jonathan Austin 1:8aa5cdb4ab67 886 * @endcode
Jonathan Austin 1:8aa5cdb4ab67 887 */
Jonathan Austin 1:8aa5cdb4ab67 888 int fiber_add_idle_component(MicroBitComponent *component)
Jonathan Austin 1:8aa5cdb4ab67 889 {
Jonathan Austin 1:8aa5cdb4ab67 890 int i = 0;
Jonathan Austin 1:8aa5cdb4ab67 891
Jonathan Austin 1:8aa5cdb4ab67 892 while(idleThreadComponents[i] != NULL && i < MICROBIT_IDLE_COMPONENTS)
Jonathan Austin 1:8aa5cdb4ab67 893 i++;
Jonathan Austin 1:8aa5cdb4ab67 894
Jonathan Austin 1:8aa5cdb4ab67 895 if(i == MICROBIT_IDLE_COMPONENTS)
Jonathan Austin 1:8aa5cdb4ab67 896 return MICROBIT_NO_RESOURCES;
Jonathan Austin 1:8aa5cdb4ab67 897
Jonathan Austin 1:8aa5cdb4ab67 898 idleThreadComponents[i] = component;
Jonathan Austin 1:8aa5cdb4ab67 899
Jonathan Austin 1:8aa5cdb4ab67 900 return MICROBIT_OK;
Jonathan Austin 1:8aa5cdb4ab67 901 }
Jonathan Austin 1:8aa5cdb4ab67 902
Jonathan Austin 1:8aa5cdb4ab67 903 /**
Jonathan Austin 1:8aa5cdb4ab67 904 * Remove a component from the array of idle thread components
Jonathan Austin 1:8aa5cdb4ab67 905 *
Jonathan Austin 1:8aa5cdb4ab67 906 * @param component The component to remove from the idle component array.
Jonathan Austin 1:8aa5cdb4ab67 907 *
Jonathan Austin 1:8aa5cdb4ab67 908 * @return MICROBIT_OK on success. MICROBIT_INVALID_PARAMETER is returned if the given component has not been previously added.
Jonathan Austin 1:8aa5cdb4ab67 909 *
Jonathan Austin 1:8aa5cdb4ab67 910 * @code
Jonathan Austin 1:8aa5cdb4ab67 911 * MicroBitI2C i2c(I2C_SDA0, I2C_SCL0);
Jonathan Austin 1:8aa5cdb4ab67 912 *
Jonathan Austin 1:8aa5cdb4ab67 913 * // heap allocated - otherwise it will be paged out!
Jonathan Austin 1:8aa5cdb4ab67 914 * MicroBitAccelerometer* accelerometer = new MicroBitAccelerometer(i2c);
Jonathan Austin 1:8aa5cdb4ab67 915 *
Jonathan Austin 1:8aa5cdb4ab67 916 * fiber_add_idle_component(accelerometer);
Jonathan Austin 1:8aa5cdb4ab67 917 *
Jonathan Austin 1:8aa5cdb4ab67 918 * fiber_remove_idle_component(accelerometer);
Jonathan Austin 1:8aa5cdb4ab67 919 * @endcode
Jonathan Austin 1:8aa5cdb4ab67 920 */
Jonathan Austin 1:8aa5cdb4ab67 921 int fiber_remove_idle_component(MicroBitComponent *component)
Jonathan Austin 1:8aa5cdb4ab67 922 {
Jonathan Austin 1:8aa5cdb4ab67 923 int i = 0;
Jonathan Austin 1:8aa5cdb4ab67 924
Jonathan Austin 1:8aa5cdb4ab67 925 while(idleThreadComponents[i] != component && i < MICROBIT_IDLE_COMPONENTS)
Jonathan Austin 1:8aa5cdb4ab67 926 i++;
Jonathan Austin 1:8aa5cdb4ab67 927
Jonathan Austin 1:8aa5cdb4ab67 928 if(i == MICROBIT_IDLE_COMPONENTS)
Jonathan Austin 1:8aa5cdb4ab67 929 return MICROBIT_INVALID_PARAMETER;
Jonathan Austin 1:8aa5cdb4ab67 930
Jonathan Austin 1:8aa5cdb4ab67 931 idleThreadComponents[i] = NULL;
Jonathan Austin 1:8aa5cdb4ab67 932
Jonathan Austin 1:8aa5cdb4ab67 933 return MICROBIT_OK;
Jonathan Austin 1:8aa5cdb4ab67 934 }
Jonathan Austin 1:8aa5cdb4ab67 935
Jonathan Austin 1:8aa5cdb4ab67 936 /**
Jonathan Austin 1:8aa5cdb4ab67 937 * Set of tasks to perform when idle.
Jonathan Austin 1:8aa5cdb4ab67 938 * Service any background tasks that are required, and attempt a power efficient sleep.
Jonathan Austin 1:8aa5cdb4ab67 939 */
Jonathan Austin 1:8aa5cdb4ab67 940 void idle()
Jonathan Austin 1:8aa5cdb4ab67 941 {
Jonathan Austin 1:8aa5cdb4ab67 942 // Service background tasks
Jonathan Austin 1:8aa5cdb4ab67 943 for(int i = 0; i < MICROBIT_IDLE_COMPONENTS; i++)
Jonathan Austin 1:8aa5cdb4ab67 944 if(idleThreadComponents[i] != NULL)
Jonathan Austin 1:8aa5cdb4ab67 945 idleThreadComponents[i]->idleTick();
Jonathan Austin 1:8aa5cdb4ab67 946
Jonathan Austin 1:8aa5cdb4ab67 947 // If the above did create any useful work, enter power efficient sleep.
Jonathan Austin 1:8aa5cdb4ab67 948 if(scheduler_runqueue_empty())
Jonathan Austin 1:8aa5cdb4ab67 949 __WFE();
Jonathan Austin 1:8aa5cdb4ab67 950 }
Jonathan Austin 1:8aa5cdb4ab67 951
Jonathan Austin 1:8aa5cdb4ab67 952 /**
Jonathan Austin 1:8aa5cdb4ab67 953 * The idle task, which is called when the runtime has no fibers that require execution.
Jonathan Austin 1:8aa5cdb4ab67 954 *
Jonathan Austin 1:8aa5cdb4ab67 955 * This function typically calls idle().
Jonathan Austin 1:8aa5cdb4ab67 956 */
Jonathan Austin 1:8aa5cdb4ab67 957 void idle_task()
Jonathan Austin 1:8aa5cdb4ab67 958 {
Jonathan Austin 1:8aa5cdb4ab67 959 while(1)
Jonathan Austin 1:8aa5cdb4ab67 960 {
Jonathan Austin 1:8aa5cdb4ab67 961 idle();
Jonathan Austin 1:8aa5cdb4ab67 962 schedule();
Jonathan Austin 1:8aa5cdb4ab67 963 }
Jonathan Austin 1:8aa5cdb4ab67 964 }