Nanokernel LIFOs

Concepts

The nanokernel’s LIFO object type is an implementation of a traditional last in, first out queue. It is mainly intended for use by fibers.

A nanokernel LIFO allows data items of any size tasks to be sent and received asynchronously. The LIFO uses a linked list to hold data items that have been sent but not yet received.

LIFO data items must be aligned on a 4-byte boundary, as the kernel reserves the first 32 bits of each item for use as a pointer to the next data item in the LIFO’s linked list. Consequently, a data item that holds N bytes of application data requires N+4 bytes of memory.

Any number of nanokernel LIFOs can be defined. Each LIFO is a distinct variable of type struct nano_lifo, and is referenced using a pointer to that variable. A LIFO must be initialized before it can be used to send or receive data items.

Items can be added to a nanokernel LIFO in a non-blocking manner by any context type (i.e. ISR, fiber, or task).

Items can be removed from a nanokernel LIFO in a non-blocking manner by any context type; if the LIFO is empty the NULL return code indicates that no item was removed. Items can also be removed from a nanokernel LIFO in a blocking manner by a fiber or task; if the LIFO is empty the thread waits for an item to be added.

Any number of threads may wait on an empty nanokernel LIFO simultaneously. When a data item becomes available it is given to the fiber that has waited longest, or to a waiting task if no fiber is waiting.

Note

A task that waits on an empty nanokernel LIFO does a busy wait. This is not an issue for a nanokernel application’s background task; however, in a microkernel application a task that waits on a nanokernel LIFO remains the current task. In contrast, a microkernel task that waits on a microkernel data passing object ceases to be the current task, allowing other tasks of equal or lower priority to do useful work.

If multiple tasks in a microkernel application wait on the same nanokernel LIFO, higher priority tasks are given data items in preference to lower priority tasks. However, the order in which equal priority tasks are given data items is unpredictable.

Purpose

Use a nanokernel LIFO to asynchronously transfer data items of arbitrary size in a “last in, first out” manner.

Usage

Example: Initializing a Nanokernel LIFO

This code establishes an empty nanokernel LIFO.

struct nano_lifo  signal_lifo;

nano_lifo_init(&signal_lifo);

Example: Writing to a Nanokernel LIFO from a Fiber

This code uses a nanokernel LIFO to send data to a consumer fiber.

struct data_item_t {
    void *lifo_reserved;   /* 1st word reserved for use by LIFO */
    ...
};

struct data_item_t  tx data;

void producer_fiber(int unused1, int unused2)
{
    ARG_UNUSED(unused1);
    ARG_UNUSED(unused2);

    while (1) {
        /* create data item to send */
        tx_data = ...

        /* send data to consumer */
        nano_fiber_lifo_put(&signal_lifo, &tx_data);

        ...
    }
}

Example: Reading from a Nanokernel LIFO

This code uses a nanokernel LIFO to obtain data items from a producer fiber, which are then processed in some manner.

void consumer_fiber(int unused1, int unused2)
{
    struct data_item_t  *rx_data;

    ARG_UNUSED(unused1);
    ARG_UNUSED(unused2);

    while (1) {
        rx_data = nano_fiber_lifo_get(&signal_lifo, TICKS_UNLIMITED);
        /* process LIFO data */
        ...
    }
}

APIs

The following APIs for a nanokernel LIFO are provided by nanokernel.h:

nano_lifo_init()

Initializes a LIFO.

nano_task_lifo_put(), nano_fiber_lifo_put(), nano_isr_lifo_put(), nano_lifo_put()

Add an item to a LIFO.

nano_task_lifo_get(), nano_fiber_lifo_get(), nano_isr_lifo_get(), nano_lifo_get()

Remove an item from a LIFO, or waits for an item for a specified time period if it is empty.