MicroBit as BLE gamepad
Dependencies: BLE_API mbed-dev-bin nRF51822
Fork of microbit-dal by
source/core/MicroBitFiber.cpp@22:23d7b9a4b082, 2016-07-13 (annotated)
- 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?
User | Revision | Line number | New contents of line |
---|---|---|---|
Jonathan Austin |
1:8aa5cdb4ab67 | 1 | /* |
Jonathan Austin |
1:8aa5cdb4ab67 | 2 | The MIT License (MIT) |
Jonathan Austin |
1:8aa5cdb4ab67 | 3 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 4 | Copyright (c) 2016 British Broadcasting Corporation. |
Jonathan Austin |
1:8aa5cdb4ab67 | 5 | This software is provided by Lancaster University by arrangement with the BBC. |
Jonathan Austin |
1:8aa5cdb4ab67 | 6 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 7 | Permission is hereby granted, free of charge, to any person obtaining a |
Jonathan Austin |
1:8aa5cdb4ab67 | 8 | copy of this software and associated documentation files (the "Software"), |
Jonathan Austin |
1:8aa5cdb4ab67 | 9 | to deal in the Software without restriction, including without limitation |
Jonathan Austin |
1:8aa5cdb4ab67 | 10 | the rights to use, copy, modify, merge, publish, distribute, sublicense, |
Jonathan Austin |
1:8aa5cdb4ab67 | 11 | and/or sell copies of the Software, and to permit persons to whom the |
Jonathan Austin |
1:8aa5cdb4ab67 | 12 | Software is furnished to do so, subject to the following conditions: |
Jonathan Austin |
1:8aa5cdb4ab67 | 13 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 14 | The above copyright notice and this permission notice shall be included in |
Jonathan Austin |
1:8aa5cdb4ab67 | 15 | all copies or substantial portions of the Software. |
Jonathan Austin |
1:8aa5cdb4ab67 | 16 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
Jonathan Austin |
1:8aa5cdb4ab67 | 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
Jonathan Austin |
1:8aa5cdb4ab67 | 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
Jonathan Austin |
1:8aa5cdb4ab67 | 20 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
Jonathan Austin |
1:8aa5cdb4ab67 | 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
Jonathan Austin |
1:8aa5cdb4ab67 | 22 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
Jonathan Austin |
1:8aa5cdb4ab67 | 23 | DEALINGS IN THE SOFTWARE. |
Jonathan Austin |
1:8aa5cdb4ab67 | 24 | */ |
Jonathan Austin |
1:8aa5cdb4ab67 | 25 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 26 | /** |
Jonathan Austin |
1:8aa5cdb4ab67 | 27 | * 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(¤tFiber->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(¤tFiber->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(¤tFiber->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, ¤tFiber->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, ¤tFiber->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 | } |