.. _lifos_v2:

LIFOs
#####

A :dfn:`LIFO` is a kernel object that implements a traditional
last in, first out (LIFO) queue, allowing threads and ISRs
to add and remove data items of any size.

.. contents::
    :local:
    :depth: 2

Concepts
********

Any number of LIFOs can be defined (limited only by available RAM). Each LIFO is
referenced by its memory address.

A LIFO has the following key properties:

* A **queue** of data items that have been added but not yet removed.
  The queue is implemented as a simple linked list.

A LIFO must be initialized before it can be used. This sets its queue to empty.

LIFO data items must be aligned on a word boundary, as the kernel reserves
the first word of an item for use as a pointer to the next data item in the
queue. Consequently, a data item that holds N bytes of application data
requires N+4 (or N+8) bytes of memory. There are no alignment or reserved
space requirements for data items if they are added with
:c:func:`k_lifo_alloc_put`, instead additional memory is temporarily
allocated from the calling thread's resource pool.

.. note::
    LIFO data items are restricted to single active instance across all LIFO
    data queues. Any attempt to re-add a LIFO data item to a queue before
    it has been removed from the queue to which it was previously added will
    result in undefined behavior.

A data item may be **added** to a LIFO by a thread or an ISR.
The item is given directly to a waiting thread, if one exists;
otherwise the item is added to the LIFO's queue.
There is no limit to the number of items that may be queued.

A data item may be **removed** from a LIFO by a thread. If the LIFO's queue
is empty a thread may choose to wait for a data item to be given.
Any number of threads may wait on an empty LIFO simultaneously.
When a data item is added, it is given to the highest priority thread
that has waited longest.

.. note::
    The kernel does allow an ISR to remove an item from a LIFO, however
    the ISR must not attempt to wait if the LIFO is empty.

Implementation
**************

Defining a LIFO
===============

A LIFO is defined using a variable of type :c:struct:`k_lifo`.
It must then be initialized by calling :c:func:`k_lifo_init`.

The following defines and initializes an empty LIFO.

.. code-block:: c

    struct k_lifo my_lifo;

    k_lifo_init(&my_lifo);

Alternatively, an empty LIFO can be defined and initialized at compile time
by calling :c:macro:`K_LIFO_DEFINE`.

The following code has the same effect as the code segment above.

.. code-block:: c

    K_LIFO_DEFINE(my_lifo);

Writing to a LIFO
=================

A data item is added to a LIFO by calling :c:func:`k_lifo_put`.

The following code builds on the example above, and uses the LIFO
to send data to one or more consumer threads.

.. code-block:: c

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

    struct data_item_t tx data;

    void producer_thread(int unused1, int unused2, int unused3)
    {
        while (1) {
            /* create data item to send */
            tx_data = ...

            /* send data to consumers */
            k_lifo_put(&my_lifo, &tx_data);

            ...
        }
    }

A data item can be added to a LIFO with :c:func:`k_lifo_alloc_put`.
With this API, there is no need to reserve space for the kernel's use in
the data item, instead additional memory will be allocated from the calling
thread's resource pool until the item is read.

Reading from a LIFO
===================

A data item is removed from a LIFO by calling :c:func:`k_lifo_get`.

The following code builds on the example above, and uses the LIFO
to obtain data items from a producer thread,
which are then processed in some manner.

.. code-block:: c

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

        while (1) {
            rx_data = k_lifo_get(&my_lifo, K_FOREVER);

            /* process LIFO data item */
            ...
        }
    }

Suggested Uses
**************

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

Configuration Options
*********************

Related configuration options:

* None.

API Reference
*************

.. doxygengroup:: lifo_apis
