fork

Dependencies:   BLE_API mbed-dev-bin nRF51822

Dependents:   microbit

Fork of microbit-dal by Lancaster University

Committer:
LancasterUniversity
Date:
Wed Jul 13 12:17:54 2016 +0100
Revision:
22:23d7b9a4b082
Parent:
1:8aa5cdb4ab67
Child:
23:6055f6c19fa6
Synchronized with git rev 7cf98c22
Author: James Devine
microbit-dal: patch for fiber_wake_on_event

fiber_wake_on_event used to crash after forking a FOB fiber.

It would attempt to obtain a new fiber context, and would place it on the wait queue.
Then when that fiber was paged in, the context of that fiber would not have been
initialised, as the function presumed schedule would be called immediately after
fiber initialisation.

This patch catches that edge case.

Who changed what in which revision?

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
LancasterUniversity 22:23d7b9a4b082 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 22:23d7b9a4b082 431 {
LancasterUniversity 22:23d7b9a4b082 432 f = forkedFiber;
LancasterUniversity 22:23d7b9a4b082 433 dequeue_fiber(f);
LancasterUniversity 22:23d7b9a4b082 434 queue_fiber(f, &runQueue);
LancasterUniversity 22:23d7b9a4b082 435 schedule();
LancasterUniversity 22:23d7b9a4b082 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 22:23d7b9a4b082 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 22:23d7b9a4b082 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 * The system timer will poll isIdleCallbackNeeded on each component to determine
Jonathan Austin 1:8aa5cdb4ab67 878 * if the scheduler should schedule the idle_task imminently.
Jonathan Austin 1:8aa5cdb4ab67 879 *
Jonathan Austin 1:8aa5cdb4ab67 880 * @param component The component to add to the array.
Jonathan Austin 1:8aa5cdb4ab67 881 *
Jonathan Austin 1:8aa5cdb4ab67 882 * @return MICROBIT_OK on success or MICROBIT_NO_RESOURCES if the fiber components array is full.
Jonathan Austin 1:8aa5cdb4ab67 883 *
Jonathan Austin 1:8aa5cdb4ab67 884 * @code
Jonathan Austin 1:8aa5cdb4ab67 885 * MicroBitI2C i2c(I2C_SDA0, I2C_SCL0);
Jonathan Austin 1:8aa5cdb4ab67 886 *
Jonathan Austin 1:8aa5cdb4ab67 887 * // heap allocated - otherwise it will be paged out!
Jonathan Austin 1:8aa5cdb4ab67 888 * MicroBitAccelerometer* accelerometer = new MicroBitAccelerometer(i2c);
Jonathan Austin 1:8aa5cdb4ab67 889 *
Jonathan Austin 1:8aa5cdb4ab67 890 * fiber_add_idle_component(accelerometer);
Jonathan Austin 1:8aa5cdb4ab67 891 * @endcode
Jonathan Austin 1:8aa5cdb4ab67 892 */
Jonathan Austin 1:8aa5cdb4ab67 893 int fiber_add_idle_component(MicroBitComponent *component)
Jonathan Austin 1:8aa5cdb4ab67 894 {
Jonathan Austin 1:8aa5cdb4ab67 895 int i = 0;
Jonathan Austin 1:8aa5cdb4ab67 896
Jonathan Austin 1:8aa5cdb4ab67 897 while(idleThreadComponents[i] != NULL && i < MICROBIT_IDLE_COMPONENTS)
Jonathan Austin 1:8aa5cdb4ab67 898 i++;
Jonathan Austin 1:8aa5cdb4ab67 899
Jonathan Austin 1:8aa5cdb4ab67 900 if(i == MICROBIT_IDLE_COMPONENTS)
Jonathan Austin 1:8aa5cdb4ab67 901 return MICROBIT_NO_RESOURCES;
Jonathan Austin 1:8aa5cdb4ab67 902
Jonathan Austin 1:8aa5cdb4ab67 903 idleThreadComponents[i] = component;
Jonathan Austin 1:8aa5cdb4ab67 904
Jonathan Austin 1:8aa5cdb4ab67 905 return MICROBIT_OK;
Jonathan Austin 1:8aa5cdb4ab67 906 }
Jonathan Austin 1:8aa5cdb4ab67 907
Jonathan Austin 1:8aa5cdb4ab67 908 /**
Jonathan Austin 1:8aa5cdb4ab67 909 * Remove a component from the array of idle thread components
Jonathan Austin 1:8aa5cdb4ab67 910 *
Jonathan Austin 1:8aa5cdb4ab67 911 * @param component The component to remove from the idle component array.
Jonathan Austin 1:8aa5cdb4ab67 912 *
Jonathan Austin 1:8aa5cdb4ab67 913 * @return MICROBIT_OK on success. MICROBIT_INVALID_PARAMETER is returned if the given component has not been previously added.
Jonathan Austin 1:8aa5cdb4ab67 914 *
Jonathan Austin 1:8aa5cdb4ab67 915 * @code
Jonathan Austin 1:8aa5cdb4ab67 916 * MicroBitI2C i2c(I2C_SDA0, I2C_SCL0);
Jonathan Austin 1:8aa5cdb4ab67 917 *
Jonathan Austin 1:8aa5cdb4ab67 918 * // heap allocated - otherwise it will be paged out!
Jonathan Austin 1:8aa5cdb4ab67 919 * MicroBitAccelerometer* accelerometer = new MicroBitAccelerometer(i2c);
Jonathan Austin 1:8aa5cdb4ab67 920 *
Jonathan Austin 1:8aa5cdb4ab67 921 * fiber_add_idle_component(accelerometer);
Jonathan Austin 1:8aa5cdb4ab67 922 *
Jonathan Austin 1:8aa5cdb4ab67 923 * fiber_remove_idle_component(accelerometer);
Jonathan Austin 1:8aa5cdb4ab67 924 * @endcode
Jonathan Austin 1:8aa5cdb4ab67 925 */
Jonathan Austin 1:8aa5cdb4ab67 926 int fiber_remove_idle_component(MicroBitComponent *component)
Jonathan Austin 1:8aa5cdb4ab67 927 {
Jonathan Austin 1:8aa5cdb4ab67 928 int i = 0;
Jonathan Austin 1:8aa5cdb4ab67 929
Jonathan Austin 1:8aa5cdb4ab67 930 while(idleThreadComponents[i] != component && i < MICROBIT_IDLE_COMPONENTS)
Jonathan Austin 1:8aa5cdb4ab67 931 i++;
Jonathan Austin 1:8aa5cdb4ab67 932
Jonathan Austin 1:8aa5cdb4ab67 933 if(i == MICROBIT_IDLE_COMPONENTS)
Jonathan Austin 1:8aa5cdb4ab67 934 return MICROBIT_INVALID_PARAMETER;
Jonathan Austin 1:8aa5cdb4ab67 935
Jonathan Austin 1:8aa5cdb4ab67 936 idleThreadComponents[i] = NULL;
Jonathan Austin 1:8aa5cdb4ab67 937
Jonathan Austin 1:8aa5cdb4ab67 938 return MICROBIT_OK;
Jonathan Austin 1:8aa5cdb4ab67 939 }
Jonathan Austin 1:8aa5cdb4ab67 940
Jonathan Austin 1:8aa5cdb4ab67 941 /**
Jonathan Austin 1:8aa5cdb4ab67 942 * Set of tasks to perform when idle.
Jonathan Austin 1:8aa5cdb4ab67 943 * Service any background tasks that are required, and attempt a power efficient sleep.
Jonathan Austin 1:8aa5cdb4ab67 944 */
Jonathan Austin 1:8aa5cdb4ab67 945 void idle()
Jonathan Austin 1:8aa5cdb4ab67 946 {
Jonathan Austin 1:8aa5cdb4ab67 947 // Service background tasks
Jonathan Austin 1:8aa5cdb4ab67 948 for(int i = 0; i < MICROBIT_IDLE_COMPONENTS; i++)
Jonathan Austin 1:8aa5cdb4ab67 949 if(idleThreadComponents[i] != NULL)
Jonathan Austin 1:8aa5cdb4ab67 950 idleThreadComponents[i]->idleTick();
Jonathan Austin 1:8aa5cdb4ab67 951
Jonathan Austin 1:8aa5cdb4ab67 952 // If the above did create any useful work, enter power efficient sleep.
Jonathan Austin 1:8aa5cdb4ab67 953 if(scheduler_runqueue_empty())
Jonathan Austin 1:8aa5cdb4ab67 954 __WFE();
Jonathan Austin 1:8aa5cdb4ab67 955 }
Jonathan Austin 1:8aa5cdb4ab67 956
Jonathan Austin 1:8aa5cdb4ab67 957 /**
Jonathan Austin 1:8aa5cdb4ab67 958 * The idle task, which is called when the runtime has no fibers that require execution.
Jonathan Austin 1:8aa5cdb4ab67 959 *
Jonathan Austin 1:8aa5cdb4ab67 960 * This function typically calls idle().
Jonathan Austin 1:8aa5cdb4ab67 961 */
Jonathan Austin 1:8aa5cdb4ab67 962 void idle_task()
Jonathan Austin 1:8aa5cdb4ab67 963 {
Jonathan Austin 1:8aa5cdb4ab67 964 while(1)
Jonathan Austin 1:8aa5cdb4ab67 965 {
Jonathan Austin 1:8aa5cdb4ab67 966 idle();
Jonathan Austin 1:8aa5cdb4ab67 967 schedule();
Jonathan Austin 1:8aa5cdb4ab67 968 }
LancasterUniversity 22:23d7b9a4b082 969 }