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