diff --git a/inc/MicroBitFiber.h b/inc/MicroBitFiber.h index b560663..1de9183 100644 --- a/inc/MicroBitFiber.h +++ b/inc/MicroBitFiber.h @@ -24,7 +24,7 @@ #define MICROBIT_FIBER_FLAG_FOB 0x01 #define MICROBIT_FIBER_FLAG_PARENT 0x02 #define MICROBIT_FIBER_FLAG_CHILD 0x04 -#define MICROBIT_FIBER_FLAG_RUN_IDLE_WITHIN 0x08 +#define MICROBIT_FIBER_FLAG_DO_NOT_PAGE 0x08 /** * Thread Context for an ARM Cortex M0 core. @@ -68,6 +68,7 @@ struct Fiber Fiber *next, *prev; // Position of this Fiber on the run queues. }; +extern Fiber *currentFiber; /** * Initialises the Fiber scheduler. @@ -119,13 +120,6 @@ Fiber *create_fiber(void (*entry_fn)(void), void (*completion_fn)(void) = releas */ Fiber *create_fiber(void (*entry_fn)(void *), void *param, void (*completion_fn)(void *) = release_fiber); -/** - * Allow the idle thread to run within the current thread's stack frame. - * This is useful to prevent paging of the thread's stack to the heap. - */ -void fiber_allow_run_idle_within(); - - /** * Calls the Fiber scheduler. @@ -234,10 +228,15 @@ void queue_fiber(Fiber *f, Fiber **queue); */ void dequeue_fiber(Fiber *f); +/** + * Set of tasks to perform when idle. + * Service any background tasks that are required, and attmept power efficient sleep. + */ +void idle(); + /** * IDLE task. - * Only scheduled for execution when the runqueue is empty. - * Performs a procressor sleep operation, then returns to the scheduler - most likely after a timer interrupt. + * Only scheduled for execution when the runqueue is empty. Typically calls idle(). */ void idle_task(); diff --git a/source/MicroBitFiber.cpp b/source/MicroBitFiber.cpp index 080206c..be6e3a5 100644 --- a/source/MicroBitFiber.cpp +++ b/source/MicroBitFiber.cpp @@ -15,7 +15,7 @@ */ Fiber *currentFiber = NULL; // The context in which the current fiber is executing. Fiber *forkedFiber = NULL; // The context in which a newly created child fiber is executing. -Fiber *idle = NULL; // IDLE task - performs a power efficient sleep, and system maintenance tasks. +Fiber *idleFiber = NULL; // IDLE task - performs a power efficient sleep, and system maintenance tasks. /* * Scheduler state. @@ -159,9 +159,9 @@ void scheduler_init() // Create the IDLE fiber. // Configure the fiber to directly enter the idle task. - idle = getFiberContext(); - idle->tcb.SP = CORTEX_M0_STACK_BASE - 0x04; - idle->tcb.LR = (uint32_t) &idle_task; + idleFiber = getFiberContext(); + idleFiber->tcb.SP = CORTEX_M0_STACK_BASE - 0x04; + idleFiber->tcb.LR = (uint32_t) &idle_task; // Flag that we now have a scheduler running uBit.flags |= MICROBIT_FLAG_SCHEDULER_RUNNING; @@ -226,15 +226,6 @@ void scheduler_event(MicroBitEvent evt) } } -/** - * Allow the idle thread to run within the current thread's stack frame. - * This is useful to prevent paging of the thread's stack to the heap. - */ -void fiber_allow_run_idle_within() -{ - currentFiber->flags |= MICROBIT_FIBER_FLAG_RUN_IDLE_WITHIN; -} - /** * Blocks the calling thread for the given period of time. @@ -621,7 +612,7 @@ void schedule() // We're in a normal scheduling context, so perform a round robin algorithm across runnable fibers. // OK - if we've nothing to do, then run the IDLE task (power saving sleep) if (runQueue == NULL || fiber_flags & MICROBIT_FLAG_DATA_READY) - currentFiber = idle; + currentFiber = idleFiber; else if (currentFiber->queue == &runQueue) // If the current fiber is on the run queue, round robin. @@ -631,21 +622,18 @@ void schedule() // Otherwise, just pick the head of the run queue. currentFiber = runQueue; - if (currentFiber == idle && oldFiber->flags & MICROBIT_FIBER_FLAG_RUN_IDLE_WITHIN) + if (currentFiber == idleFiber && oldFiber->flags & MICROBIT_FIBER_FLAG_DO_NOT_PAGE) { // Run the idle task right here using the old fiber's stack. // Keep idling while the runqueue is empty, or there is data to process. + + // Run in the context of the original fiber, to preserve state of flags... + // as we are running on top of this fiber's stack. + currentFiber = oldFiber; + do { - uBit.systemTasks(); - - if(scheduler_runqueue_empty()) - { - if (uBit.ble) - uBit.ble->waitForEvent(); - else - __WFI(); - } + idle(); } while (runQueue == NULL || fiber_flags & MICROBIT_FLAG_DATA_READY); @@ -659,13 +647,13 @@ void schedule() if (currentFiber != oldFiber) { // Special case for the idle task, as we don't maintain a stack context (just to save memory). - if (currentFiber == idle) + if (currentFiber == idleFiber) { - idle->tcb.SP = CORTEX_M0_STACK_BASE - 0x04; - idle->tcb.LR = (uint32_t) &idle_task; + idleFiber->tcb.SP = CORTEX_M0_STACK_BASE - 0x04; + idleFiber->tcb.LR = (uint32_t) &idle_task; } - if (oldFiber == idle) + if (oldFiber == idleFiber) { // Just swap in the new fiber, and discard changes to stack and register context. swap_context(NULL, ¤tFiber->tcb, NULL, currentFiber->stack_top); @@ -681,6 +669,25 @@ void schedule() } } + +/** + * Set of tasks to perform when idle. + * Service any background tasks that are required, and attmept power efficient sleep. + */ +void idle() +{ + // Service background tasks + uBit.systemTasks(); + + // If the above did create any useful work, enter power efficient sleep. + if(scheduler_runqueue_empty()) + { + if (uBit.ble) + uBit.ble->waitForEvent(); + else + __WFI(); + } +} /** * IDLE task. * Only scheduled for execution when the runqueue is empty. @@ -690,16 +697,7 @@ void idle_task() { while(1) { - uBit.systemTasks(); - - if(scheduler_runqueue_empty()) - { - if (uBit.ble) - uBit.ble->waitForEvent(); - else - __WFI(); - } - + idle(); schedule(); } } diff --git a/source/MicroBitMessageBus.cpp b/source/MicroBitMessageBus.cpp index be5c1ff..6e02466 100644 --- a/source/MicroBitMessageBus.cpp +++ b/source/MicroBitMessageBus.cpp @@ -229,7 +229,7 @@ void MicroBitMessageBus::process(MicroBitEvent evt) // Otherwise, we invoke it in a 'fork on block' context, that will automatically create a fiber // should the event handler attempt a blocking operation, but doesn't have the overhead // of creating a fiber needlessly. (cool huh?) - if (l->flags & MESSAGE_BUS_LISTENER_NONBLOCKING) + if (l->flags & MESSAGE_BUS_LISTENER_NONBLOCKING || currentFiber->flags & MICROBIT_FIBER_FLAG_DO_NOT_PAGE) async_callback(l); else invoke(async_callback, l);