doc: Enhance information about microkernel pipes

Revises and supplements information on concepts.

Change-Id: I7dd9dd2e99cd5d975cc0f617b076dcf13c414fd0
Signed-off-by: Allan Stephens <allan.stephens@windriver.com>
This commit is contained in:
Allan Stephens 2015-09-02 09:19:58 -04:00 committed by Anas Nashif
commit dfa734d57e

View file

@ -3,39 +3,161 @@
Pipes
#####
Definition
**********
Microkernel pipes are defined in :file:`kernel/microkernel/k_pipe.c`.
Pipes allow any task to put any amount of data in or out. Pipes are
conceptually similar to FIFO objects in that they communicate
anonymously in a time-ordered, first-in, first-out manner, to exchange
data between tasks. Like FIFO objects, pipes can have a buffer, but
un-buffered operation is also possible. The main difference between
FIFO objects and pipes is that pipes handle variable-sized data.
Function
Concepts
********
Pipes accept and send variable-sized data, and can be configured to work
with or without a buffer. Buffered pipes are time-ordered. The incoming
data is stored on a first-come, first-serve basis in the buffer; it is
not sorted by priority.
The microkernel's pipe object type is an implementation of a traditional
anonymous pipe.
Pipes have no size limit. The size of the data transfer and the size of
the buffer have no limit except for the available memory. Pipes allow
senders or receivers to perform partial read and partial write
operations.
A pipe allows a task to send a byte stream to another task. The pipe can be
configured with a ring buffer which holds data that has been sent
but not yet received; alternatively, the pipe may have no ring buffer.
Pipes can be used to transfer chunks of data in whole or in part, and either
synchronously or asynchronously.
Pipes support both synchronous and asynchronous operations. If a pipe is
unbuffered, the sender can asynchronously put data into the pipe, or
wait for the data to be received, and the receiver can attempt to
remove data from the pipe, or wait on the data to be available.
Buffered pipes are synchronous by design.
Any number of pipes can be defined in a microkernel system. Each pipe has a
name that uniquely identifies it. In addition, a pipe defines the size of
its ring buffer; a size of zero defines a pipe with no ring buffer.
Sending Data
============
A task sends data to a pipe by specifying a pointer to the data bytes
to be sent. It also specifies both the number of data bytes available
and a *pipe option* that indicates the minimum number of data bytes
the pipe must accept. The following pipe option values are supported:
:option:`_ALL_N`
Specifies that all available data bytes must be accepted by the pipe;
otherwise the send request fails or performs a partial send.
:option:`_1_TO_N`
Specifies that at least one data byte must be accepted by the pipe;
otherwise the send request fails.
:option:`_0_TO_N`
Specifies that any number of data bytes (including zero) may be accepted
by the pipe; the send request never fails.
The pipe accepts data bytes from the sending task if they can be delivered
by copying them directly to the receiving task. If the sending task is unable
to wait, or has waited as long as it can, the pipe can also accept data bytes
by copying them to its ring buffer for later delivery. The ring buffer is used
only when necessary to minimize copying of data bytes.
Upon completion of a send operation a return code is provided to indicate
if the send request was satisfied. The sending task can also read the *bytes
written* argument to determine how many data bytes were accepted by the pipe,
allowing it to deal with any unsent data bytes.
Data sent to a pipe that does not have a ring buffer is sent synchronously;
that is, when the send operation is complete the sending task knows that the
receiving task has received the data that was sent. Data sent to a pipe
that has a ring buffer is sent asynchronously; that is, when the send operation
is complete some or all of the data that was sent may still be in the pipe
waiting for the receiving task to receive it.
Incomplete Send Requests
------------------------
Although a pipe endeavors to accept all available data bytes when the
:option:`_ALL_N` pipe option is specified, it does not guarantee that the
data bytes will be accepted in an "all or nothing" manner. If the pipe
is able to accept at least one data byte it returns :option:`RC_INCOMPLETE`
to notify the sending task that its request was not fully satisfied; if
the pipe is unable to accept any data bytes it returns :option:`RC_FAIL`.
An example of a situation that can result in an incomplete send is a time
limited send request through an unbuffered pipe. If the receiving task
chooses to receive only a subset of the sender's data bytes, and then the
send operation times out before the receiving task attempts to receive the
remainder, an incomplete send occurs.
Sending using a Memory Pool Block
---------------------------------
A task that sends large chunks of data through a pipe may be able to improve
its efficiency by placing the data into a memory pool block and sending
the block. The pipe treats the memory block as a temporary addition to
its ring buffer, allowing it to immediately accept the data bytes without
copying them. Once all of the data bytes in the block have been delivered
to the receiving task the pipe automatically frees the block back to the
memory pool.
Data sent using a memory pool block is always sent asynchronously, even for
a pipe with no ring buffer of its own. Likewise, the pipe always accepts all
of the available data in the block---a partial transfer never occurs.
Receiving Data
==============
A task receives from a pipe by specifying a pointer to an area to receive
the data bytes that were sent. It also specifies both the desired number
of data bytes and a *pipe option* that indicates the minimum number of data
bytes the pipe must deliver. The following pipe option values are supported:
:option:`_ALL_N`
Specifies that all desired number of data bytes must be received;
otherwise the receive request fails or performs a partial receive.
:option:`_1_TO_N`
Specifies that at least one data byte must be received; otherwise
the receive request fails.
:option:`_0_TO_N`
Specifies that any number of data bytes (including zero) may be
received; the receive request never fails.
The pipe delivers data bytes by copying them directly from the sending task
or from the pipe's ring buffer. Data bytes taken from the ring buffer are
delivered in a first in, first out manner.
When a pipe is unable to deliver the specified minimum number of data bytes
the receiving task may choose to wait until they can be delivered.
Upon completion of a receive operation a return code is provided to indicate
if the receive request was satisfied. The receiving task can also read the
*bytes read* argument to determine how many data bytes were delivered
by the pipe.
Incomplete Receive Requests
---------------------------
Although a pipe endeavors to deliver all desired data bytes when the
:option:`_ALL_N` pipe option is specified, it does not guarantee that the
data bytes will be delivered in an "all or nothing" manner. If the pipe
is able to deliver at least one data byte it returns :option:`RC_INCOMPLETE`
to notify the receiving task that its request was not fully satisfied; if
the pipe is unable to deliver any data bytes it returns :option:`RC_FAIL`.
An example of a situation that can result in an incomplete receive is a time
limited recieve request through an unbuffered pipe. If the sending task
sends fewer than the desired number of data bytes, and then the receive
operation times out before the sending task attempts to send the remainder,
an incomplete receive occurs.
Receiving using a Memory Pool Block
-----------------------------------
A task can achieve the effect of receiving data from a pipe into a memory pool
block by pre-allocating a block and then receiving the data into it.
Sharing a Pipe
==============
A pipe is typically used by a single sending task and a single receiving
task; however, it is possible for a pipe to be shared by multiple sending
tasks or multiple receiving tasks.
Care must be taken when a pipe is shared by multiple sending tasks to
ensure the data bytes they send do not become interleaved unexpectedly;
using the :option:`_ALL_N` pipe option helps to ensure that each data chunk is
transferred in a single operation. The same is true when multiple receiving
tasks are reading from the same pipe.
Purpose
*******
Use a pipe to transfer data when the receiving task needs the ability
to split or merge the data items generated by the sending task.
Pipes are anonymous. The pipe transfer does not identify the sender or
receiver. Alternatively, mailboxes can be used to specify the sender
and receiver identities.
Usage
*****
@ -49,8 +171,8 @@ The following parameters must be defined:
This specifies a unique name for the pipe.
*buffer_size*
This specifies the size (in bytes) of the pipe's internal buffer.
If no internal buffer is to be used specify zero.
This specifies the size (in bytes) of the pipe's ring buffer.
If no ring buffer is to be used specify zero.
Public Pipe
@ -62,7 +184,7 @@ Define the pipe in the application's .MDEF file using the following syntax:
PIPE name buffer_size
For example, the file :file:`projName.mdef` defines a pipe with a 1 KB internal
For example, the file :file:`projName.mdef` defines a pipe with a 1 KB ring
buffer as follows:
.. code-block:: console