Skip to content

Commit

Permalink
Integrate optional fiber meta-data contributed in #424
Browse files Browse the repository at this point in the history
  - Add new CONFIG option to enable per-fiber meta data (default: disabled)
  - Add (void *) pointer to Fiber struct when CONFIG option enabled
  - Add Yotta CONFIG glue
  - Refactor fork-on-block handling into a unified function
  - Align validation semantics of both flavours of invoke() function
  • Loading branch information
finneyj committed May 7, 2019
1 parent 1c1426f commit 461fb33
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 44 deletions.
5 changes: 5 additions & 0 deletions inc/core/MicroBitConfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,11 @@ extern uint32_t __etext;
#define INITIAL_FIBER_TABLE_SIZE 4
#endif

// Enable used_data field in Fiber structure (for thread-local data)
#ifndef MICROBIT_FIBER_USER_DATA
#define MICROBIT_FIBER_USER_DATA 0
#endif

//
// Message Bus:
// Default behaviour for event handlers, if not specified in the listen() call
Expand Down
10 changes: 10 additions & 0 deletions inc/core/MicroBitFiber.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,12 @@ DEALINGS IN THE SOFTWARE.
#define MICROBIT_FIBER_FLAG_CHILD 0x04
#define MICROBIT_FIBER_FLAG_DO_NOT_PAGE 0x08

#if CONFIG_ENABLED(MICROBIT_FIBER_USER_DATA)
#define HAS_THREAD_USER_DATA (currentFiber->user_data != NULL)
#else
#define HAS_THREAD_USER_DATA false
#endif

/**
* Thread Context for an ARM Cortex M0 core.
*
Expand Down Expand Up @@ -89,6 +95,10 @@ struct Fiber
uint32_t flags; // Information about this fiber.
Fiber **queue; // The queue this fiber is stored on.
Fiber *next; // Position of this Fiber on the run queue.

#if CONFIG_ENABLED(MICROBIT_FIBER_USER_DATA)
void *user_data; // Optional pointer to user defined data block.
#endif
};

struct FiberTable
Expand Down
9 changes: 9 additions & 0 deletions inc/platform/yotta_cfg_mappings.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,15 @@
*/

//DAL mappings

#ifdef YOTTA_CFG_MICROBIT_DAL_INITIAL_FIBER_TABLE_SIZE
#define INITIAL_FIBER_TABLE_SIZE YOTTA_CFG_MICROBIT_DAL_INITIAL_FIBER_TABLE_SIZE
#endif

#ifdef YOTTA_CFG_MICROBIT_DAL_FIBER_USER_DATA
#define MICROBIT_FIBER_USER_DATA YOTTA_CFG_MICROBIT_DAL_FIBER_USER_DATA
#endif

#ifdef YOTTA_CFG_MICROBIT_DAL_HEAP_ALLOCATOR
#define MICROBIT_HEAP_ALLOCATOR YOTTA_CFG_MICROBIT_DAL_HEAP_ALLOCATOR
#endif
Expand Down
96 changes: 52 additions & 44 deletions source/core/MicroBitFiber.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,10 @@ Fiber *getFiberContext()
f->flags = 0;
f->tcb.stack_base = CORTEX_M0_STACK_BASE;

#if CONFIG_ENABLED(MICROBIT_FIBER_USER_DATA)
f->user_data = 0;
#endif

// Add the new Fiber to the FiberTable
__disable_irq();
if (fiberTable->length == fiberTable->capacity)
Expand Down Expand Up @@ -367,6 +371,37 @@ void scheduler_event(MicroBitEvent evt)
messageBus->ignore(evt.source, evt.value, scheduler_event);
}

/**
* Internal utility function to perform a fork operation on the current fiber, and return
* the current fibers context to the point at which it was checkpointed.
*
* This function is called whenever a fiber requests a "Fork on Block" behaviour and a
* blocking call to the scheduler is requested.
*/
static Fiber* handle_fob()
{
Fiber *f = currentFiber;

// This is a blocking call, so if we're in a fork on block context,
// it's time to spawn a new fiber...
if (f->flags & MICROBIT_FIBER_FLAG_FOB)
{
// Allocate a TCB from the new fiber. This will come from the tread pool if availiable,
// else a new one will be allocated on the heap.
forkedFiber = getFiberContext();

// If we're out of memory, there's nothing we can do.
// keep running in the context of the current thread as a best effort.
if (forkedFiber != NULL) {
#if CONFIG_ENABLED(MICROBIT_FIBER_USER_DATA)
forkedFiber->user_data = f->user_data;
f->user_data = NULL;
#endif
f = forkedFiber;
}
}
return f;
}

/**
* Blocks the calling thread for the given period of time.
Expand All @@ -380,28 +415,8 @@ void scheduler_event(MicroBitEvent evt)
*/
void fiber_sleep(unsigned long t)
{
Fiber *f = currentFiber;

// If the scheduler is not running, then simply perform a spin wait and exit.
if (!fiber_scheduler_running())
{
wait_ms(t);
return;
}

// Sleep is a blocking call, so if we're in a fork on block context,
// it's time to spawn a new fiber...
if (currentFiber->flags & MICROBIT_FIBER_FLAG_FOB)
{
// Allocate a new fiber. This will come from the fiber pool if availiable,
// else a new one will be allocated on the heap.
forkedFiber = getFiberContext();

// If we're out of memory, there's nothing we can do.
// keep running in the context of the current thread as a best effort.
if (forkedFiber != NULL)
f = forkedFiber;
}
// Fork a new fiber if necessary
Fiber *f = handle_fob();

// Calculate and store the time we want to wake up.
f->context = system_timer_current_time() + t;
Expand Down Expand Up @@ -465,29 +480,11 @@ int fiber_wait_for_event(uint16_t id, uint16_t value)
*/
int fiber_wake_on_event(uint16_t id, uint16_t value)
{
Fiber *f = currentFiber;

if (messageBus == NULL || !fiber_scheduler_running())
return MICROBIT_NOT_SUPPORTED;

// Sleep is a blocking call, so if we're in a fork on block context,
// it's time to spawn a new fiber...
if (currentFiber->flags & MICROBIT_FIBER_FLAG_FOB)
{
// Allocate a TCB from the new fiber. This will come from the tread pool if availiable,
// else a new one will be allocated on the heap.
forkedFiber = getFiberContext();

// If we're out of memory, there's nothing we can do.
// keep running in the context of the current thread as a best effort.
if (forkedFiber != NULL)
{
f = forkedFiber;
dequeue_fiber(f);
queue_fiber(f, &runQueue);
schedule();
}
}
// Fork a new fiber if necessary
Fiber *f = handle_fob();

// Encode the event data in the context field. It's handy having a 32 bit core. :-)
f->context = value << 16 | id;
Expand All @@ -503,6 +500,9 @@ int fiber_wake_on_event(uint16_t id, uint16_t value)
if (id != MICROBIT_ID_NOTIFY && id != MICROBIT_ID_NOTIFY_ONE)
messageBus->listen(id, value, scheduler_event, MESSAGE_BUS_LISTENER_IMMEDIATE);

// NOTE: We intentionally don't re-enter the scheduler here, such that this function
// can be used to create atomic wait events. if using this function, the calling thread MUST
// call schedule() as its next call to the scheduler.
return MICROBIT_OK;
}

Expand All @@ -528,7 +528,7 @@ int invoke(void (*entry_fn)(void))
if (!fiber_scheduler_running())
return MICROBIT_NOT_SUPPORTED;

if (currentFiber->flags & MICROBIT_FIBER_FLAG_FOB)
if (currentFiber->flags & (MICROBIT_FIBER_FLAG_FOB | MICROBIT_FIBER_FLAG_PARENT | MICROBIT_FIBER_FLAG_CHILD) || HAS_THREAD_USER_DATA)
{
// If we attempt a fork on block whilst already in fork n block context,
// simply launch a fiber to deal with the request and we're done.
Expand Down Expand Up @@ -557,6 +557,10 @@ int invoke(void (*entry_fn)(void))
// spawn a thread to deal with it.
currentFiber->flags |= MICROBIT_FIBER_FLAG_FOB;
entry_fn();

#if CONFIG_ENABLED(MICROBIT_FIBER_USER_DATA)
currentFiber->user_data = 0;
#endif
currentFiber->flags &= ~MICROBIT_FIBER_FLAG_FOB;

// If this is is an exiting fiber that for spawned to handle a blocking call, recycle it.
Expand Down Expand Up @@ -591,7 +595,7 @@ int invoke(void (*entry_fn)(void *), void *param)
if (!fiber_scheduler_running())
return MICROBIT_NOT_SUPPORTED;

if (currentFiber->flags & (MICROBIT_FIBER_FLAG_FOB | MICROBIT_FIBER_FLAG_PARENT | MICROBIT_FIBER_FLAG_CHILD))
if (currentFiber->flags & (MICROBIT_FIBER_FLAG_FOB | MICROBIT_FIBER_FLAG_PARENT | MICROBIT_FIBER_FLAG_CHILD) || HAS_THREAD_USER_DATA)
{
// If we attempt a fork on block whilst already in a fork on block context,
// simply launch a fiber to deal with the request and we're done.
Expand Down Expand Up @@ -620,6 +624,10 @@ int invoke(void (*entry_fn)(void *), void *param)
// spawn a thread to deal with it.
currentFiber->flags |= MICROBIT_FIBER_FLAG_FOB;
entry_fn(param);

#if CONFIG_ENABLED(MICROBIT_FIBER_USER_DATA)
currentFiber->user_data = 0;
#endif
currentFiber->flags &= ~MICROBIT_FIBER_FLAG_FOB;

// If this is is an exiting fiber that for spawned to handle a blocking call, recycle it.
Expand Down

0 comments on commit 461fb33

Please sign in to comment.