.. _pipes_v2:

Pipes
#####

A :dfn:`pipe` is a kernel object that allows a thread to send a byte stream
to another thread. Pipes enable efficient inter-thread communication and can
be used to synchronously transfer chunks of data in whole or in part.

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

Concepts
********

Any number of pipes can be defined, limited only by available RAM. Each pipe
is referenced by its memory address.

A pipe has the following key property:

* A **size** that indicates the capacity of the pipe's ring buffer.

A pipe must be initialized before it can be used. When initialized, the pipe
is empty.

Threads interact with the pipe as follows:

- **Writing**: Data is synchronously written, either in whole or in part, to
  a pipe by a thread. If the pipe's ring buffer is full, the operation blocks
  until sufficient space becomes available or the specified timeout expires.

- **Reading**: Data is synchronously read, either in whole or in part, from a
  pipe by a thread. If the pipe's ring buffer is empty, the operation blocks
  until data becomes available or the specified timeout expires.

- **Resetting**: A thread can reset a pipe, which resets its internal state and
  ends all pending read and write operations with an error code.

Pipes are well-suited for scenarios like producer-consumer patterns or
streaming data between threads.

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

A pipe is defined using a variable of type :c:struct:`k_pipe` and a
byte buffer. The pipe must then be initialized by calling :c:func:`k_pipe_init`.

The following code defines and initializes an empty pipe with a ring buffer
capable of holding 100 bytes, aligned to a 4-byte boundary:

.. code-block:: c

    uint8_t __aligned(4) my_ring_buffer[100];
    struct k_pipe my_pipe;

    k_pipe_init(&my_pipe, my_ring_buffer, sizeof(my_ring_buffer));

Alternatively, a pipe can be defined and initialized at compile time using
the :c:macro:`K_PIPE_DEFINE` macro, which defines both the pipe and its
ring buffer:

.. code-block:: c

    K_PIPE_DEFINE(my_pipe, 100, 4);

This has the same effect as the code above.

Writing to a Pipe
=================

Data is added to a pipe by calling :c:func:`k_pipe_write`.

The following example demonstrates using a pipe to send data from a producer
thread to one or more consumer threads. If the pipe's ring buffer fills up,
the producer thread waits for a specified amount of time.

.. code-block:: c

   struct message_header {
       size_t num_data_bytes; /* Example field */
       ...
   };

   void producer_thread(void)
   {
       int rc;
       uint8_t *data;
       size_t total_size;
       size_t bytes_written;

       while (1) {
           /* Craft message to send in the pipe */
           make_message(data, &total_size);
           bytes_written = 0;

           /* Write data to the pipe, handling partial writes */
           while (bytes_written < total_size) {
               rc = k_pipe_write(&my_pipe, &data[bytes_written], total_size - bytes_written, K_NO_WAIT);

               if (rc < 0) {
                   /* Error occurred */
                   ...
                   break;
               } else {
                   /* Partial or full write succeeded; adjust for next iteration */
                   bytes_written += rc;
               }
           }

           /* Reset bytes_written for the next message */
           bytes_written = 0;
           ...
       }
   }

Reading from a Pipe
===================

Data is retrieved from the pipe by calling :c:func:`k_pipe_read`.

The following example builds on the producer thread example above. It shows
a consumer thread that processes data generated by the producer.

.. code-block:: c

   struct message_header {
       size_t num_data_bytes; /* Example field */
       ...
   };

   void consumer_thread(void)
   {
       int rc;
       uint8_t buffer[128];
       size_t bytes_read = 0;
       struct message_header *header = (struct message_header *)buffer;

       while (1) {
           /* Step 1: Read the message header */
           bytes_read = 0;
      read_header:
           while (bytes_read < sizeof(*header)) {
               rc = k_pipe_read(&my_pipe, &buffer[bytes_read], sizeof(*header) - bytes_read, &bytes_read, K_NO_WAIT);

               if (rc < 0) {
                   /* Error occurred */
                   ...
                   goto read_header;
               }

               /* Adjust for partial reads */
               bytes_read += rc;
           }

           /* Step 2: Read the message body */
           bytes_read = 0;
           while (bytes_read < header->num_data_bytes) {
               rc = k_pipe_read(&my_pipe, &buffer[sizeof(*header) + bytes_read], header->num_data_bytes - bytes_read, K_NO_WAIT);

               if (rc < 0) {
                   /* Error occurred */
                   ...
                   goto read_header;
               }

               /* Adjust for partial reads */
               bytes_read += rc;
           }
           /* Successfully received the complete message */
       }
   }

Resetting a Pipe
================

The pipe can be reset by calling :c:func:`k_pipe_reset`. Resetting a pipe
resets its internal state and ends all pending operations with an error code.

The following example demonstrates resetting a pipe in response to a critical
error:

.. code-block:: c

    void monitor_thread(void)
    {
        while (1) {
            ...
            /* Critical error detected: reset the entire pipe to reset it. */
            k_pipe_reset(&my_pipe);
            ...
        }
    }

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

Pipes are useful for sending streams of data between threads. Typical
applications include:

- Implementing producer-consumer patterns.
- Streaming logs or packets between threads.
- Handling variable-length message passing in real-time systems.

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

.. doxygengroup:: pipe_apis
