microbit: Ammendments to ensure fiber is never pages out due to eventing and minor cleanups

- renamed fiber flag to be more general.
- renamed idle to idleFiber to be consistent with the rest of the code.
- factored content of idle() into a funciton to avoid dupication.
- added test case to MessageBus::process() to avoid forking fibers if not requested.
- removed fiber_allow_run_idle_within mutator in favour of exposing currentFiber, as a more general solution.
This commit is contained in:
Joe Finney 2015-09-19 21:44:58 +01:00
parent 9ccdd25e9f
commit 3cfc7ec6e7
3 changed files with 46 additions and 49 deletions

View file

@ -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();

View file

@ -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, &currentFiber->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();
}
}

View file

@ -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);