msg.h 11.7 KB
/*
 * Copyright (C) 2014 Freie Universität Berlin
 *
 * This file is subject to the terms and conditions of the GNU Lesser
 * General Public License v2.1. See the file LICENSE in the top level
 * directory for more details.
 */

/**
 * @defgroup    core_msg  Messaging / IPC
 * @ingroup     core
 * @brief       Messaging API for inter process communication
 *
 * Messages
 * ========
 * IPC messages consist of a sender PID, a type, and some content. The sender
 * PID will be set by the IPC internally and is not required to be set by the
 * user. The type helps the receiver to multiplex different message types and
 * should be set to a system-wide unique value. The content can either be
 * provided as a 32-bit integer or a pointer.
 *
 * Blocking vs non-blocking
 * ========================
 * Messages can be sent and received blocking and non-blocking. Both can be
 * used combined: A message send while blocking the sender thread can be
 * received with the non-blocking variant and vice-versa.
 *
 * Blocking IPC
 * ------------
 * For the blocking variant use @ref msg_send() or @ref msg_receive()
 * respectively.
 *
 * Additionally, one can use @ref msg_send_receive() to simultaneously block
 * the sending thread and expect a response from the receiving thread. In this
 * case, the receiving thread must use @ref msg_reply() to reply to the message
 * of the sender thread.
 *
 * ~~~~~~~~~~~~~~~~~~~~~~~~ {.c}
 * #include <inttypes.h>
 * #include <stdio.h>
 *
 * #include "msg.h"
 * #include "thread.h"
 *
 * static kernel_pid_t rcv_pid;
 * static char rcv_stack[THREAD_STACKSIZE_DEFAULT];
 *
 * static void *rcv(void *arg)
 * {
 *     msg_t msg_req, msg_resp;
 *
 *     (void)arg;
 *     while (1) {
 *         msg_receive(&msg_req);
 *         msg_resp.content.value = msg_req.content.value + 1;
 *         msg_reply(&msg_req, &msg_resp);
 *     }
 *     return NULL;
 * }
 *
 * int main(void)
 * {
 *     msg_t msg_req, msg_resp;
 *
 *     msg_resp.content.value = 0;
 *     rcv_pid = thread_create(rcv_stack, sizeof(rcv_stack),
 *                             THREAD_PRIORITY_MAIN - 1, 0, rcv, NULL, "rcv");
 *     while (1) {
 *         msg_req.content.value = msg_resp.content.value;
 *         msg_send_receive(&msg_req, &msg_resp, rcv_pid);
 *         printf("Result: %" PRIu32 "\n", msg_resp.content.value);
 *     }
 *     return 0;
 * }
 * ~~~~~~~~~~~~~~~~~~~~~~~~
 *
 * Non-blocking IPC
 * ----------------
 * For the non-blocking variant use @ref msg_try_send() or
 * @ref msg_try_receive() respectively. If a message is sent in synchronous
 * mode or the message queue (see below) of the receiving thread is full
 * messages sent this way will be dropped.
 *
 * You can use the example on asynchronous IPC below — but without the queue —
 * to get an impression of how to use non-blocking IPC.
 *
 * Synchronous vs Asynchronous
 * ===========================
 * RIOT's IPC supports both synchronous and asynchronous IPC.
 *
 * Synchronous IPC
 * ---------------
 * Synchronous IPC is the default mode i.e. is active when the receiving thread
 * has no message queue initialized. Messages that can't be delivered when
 * sending non-blocking (because the receiver already received a message) or
 * which are sent when the receiver is not receive-blocked will be dropped.
 *
 * Asynchronous IPC
 * ----------------
 * To use asynchronous IPC one needs to initialize a message queue using
 * @ref msg_init_queue() (note that it **must** be of a size equal to a power of
 * two). Messages sent to a thread with a message queue that isn't full are
 * never dropped and the sending never blocks, even when using @ref msg_send().
 * If the queue is full and the sending thread has a higher priority than the
 * receiving thread the send-behavior is equivalent to synchronous mode.
 *
 * ~~~~~~~~~~~~~~~~~~~~~~~~ {.c}
 * #include <inttypes.h>
 * #include <stdio.h>
 *
 * #include "msg.h"
 * #include "thread.h"
 *
 * #define RCV_QUEUE_SIZE  (8)
 *
 * static kernel_pid_t rcv_pid;
 * static char rcv_stack[THREAD_STACKSIZE_DEFAULT + THREAD_EXTRA_STACKSIZE_PRINTF];
 * static msg_t rcv_queue[RCV_QUEUE_SIZE];
 *
 * static void *rcv(void *arg)
 * {
 *     msg_t msg;
 *
 *     (void)arg;
 *     msg_init_queue(rcv_queue, RCV_QUEUE_SIZE);
 *     while (1) {
 *         msg_receive(&msg);
 *         printf("Received %" PRIu32 "\n", msg.content.value);
 *     }
 *     return NULL;
 * }
 *
 * int main(void)
 * {
 *     msg_t msg;
 *
 *     msg.content.value = 0;
 *     rcv_pid = thread_create(rcv_stack, sizeof(rcv_stack),
 *                             THREAD_PRIORITY_MAIN - 1, 0, rcv, NULL, "rcv");
 *     while (1) {
 *         if (msg_try_send(&msg, rcv_pid) == 0) {
 *             printf("Receiver queue full.\n");
 *         }
 *         msg.content.value++;
 *     }
 *     return 0;
 * }
 * ~~~~~~~~~~~~~~~~~~~~~~~~
 *
 * Timing & messages
 * =================
 * Timing out the reception of a message or sending messages at a certain time
 * is out of scope for the basic IPC provided by the kernel. See the
 * @ref sys_xtimer "xtimer" module on information for these functionalities.
 *
 * @{
 *
 * @file
 * @brief       Messaging API for inter process communication
 *
 * @author      Kaspar Schleiser <kaspar@schleiser.de>
 * @author      Kévin Roussel <Kevin.Roussel@inria.fr>
 */

#ifndef MSG_H_
#define MSG_H_

#include <stdint.h>
#include <stdbool.h>
#include "kernel_types.h"

#ifdef __cplusplus
extern "C" {
#endif

/**
 * @brief Describes a message object which can be sent between threads.
 *
 * User can set type and one of content.ptr and content.value. (content is a union)
 * The meaning of type and the content fields is totally up to the user,
 * the corresponding fields are never read by the kernel.
 *
 */
typedef struct {
    kernel_pid_t sender_pid;    /**< PID of sending thread. Will be filled in
                                     by msg_send. */
    uint16_t type;              /**< Type field. */
    union {
        void *ptr;              /**< Pointer content field. */
        uint32_t value;         /**< Value content field. */
    } content;                  /**< Content of the message. */
} msg_t;


/**
 * @brief Send a message (blocking).
 *
 * This function sends a message to another thread. The ``msg_t`` structure has
 * to be allocated (e.g. on the stack) before calling the function and can be
 * freed afterwards. If called from an interrupt, this function will never
 * block.
 *
 * @param[in] m             Pointer to preallocated ``msg_t`` structure, must
 *                          not be NULL.
 * @param[in] target_pid    PID of target thread
 *
 * @return 1, if sending was successful (message delivered directly or to a
 *            queue)
 * @return 0, if called from ISR and receiver cannot receive the message now
 *            (it is not waiting or it's message queue is full)
 * @return -1, on error (invalid PID)
 */
int msg_send(msg_t *m, kernel_pid_t target_pid);


/**
 * @brief Send a message (non-blocking).
 *
 * This function sends a message to another thread. The ``msg_t`` structure has
 * to be allocated (e.g. on the stack) before calling the function and can be
 * freed afterwards. This function will never block.
 *
 * @param[in] m             Pointer to preallocated ``msg_t`` structure, must
 *                          not be NULL.
 * @param[in] target_pid    PID of target thread
 *
 * @return 1, if sending was successful (message delivered directly or to a
 *         queue)
 * @return 0, if receiver is not waiting or has a full message queue
 * @return -1, on error (invalid PID)
 */
int msg_try_send(msg_t *m, kernel_pid_t target_pid);


/**
 * @brief Send a message to the current thread.
 * @details Will work only if the thread has a message queue.
 *
 * Will be automatically chosen instead of @c msg_send
 * if @c target_pid == @c thread_pid.
 * This function never blocks.
 *
 * @param  m pointer to message structure
 *
 * @return 1 if sending was successful
 * @return 0 if the thread's message queue is full (or inexistent)
 */
int msg_send_to_self(msg_t *m);

/**
 * Value of msg_t::sender_pid if the sender was an interrupt service routine.
 */
#define KERNEL_PID_ISR (KERNEL_PID_LAST + 1)

/**
 * @brief Send message from interrupt.
 *
 * Will be automatically chosen instead of msg_send() if called from an
 * interrupt/ISR.
 *
 * The value of ``m->sender_pid`` is set to @ref KERNEL_PID_ISR.
 *
 * @see msg_sent_by_int()
 *
 * @param[in] m             Pointer to preallocated @ref msg_t structure, must
 *                          not be NULL.
 * @param[in] target_pid    PID of target thread.
 *
 * @return 1, if sending was successful
 * @return 0, if receiver is not waiting and ``block == 0``
 * @return -1, on error (invalid PID)
 */
int msg_send_int(msg_t *m, kernel_pid_t target_pid);

/**
 * @brief Test if the message was sent inside an ISR.
 * @see msg_send_int()
 * @param[in] m The message in question.
 * @returns `== 0` if *not* sent by an ISR
 * @returns `!= 0` if sent by an ISR
 */
static inline int msg_sent_by_int(const msg_t *m)
{
    return (m->sender_pid == KERNEL_PID_ISR);
}

/**
 * @brief Receive a message.
 *
 * This function blocks until a message was received.
 *
 * @param[out] m    Pointer to preallocated ``msg_t`` structure, must not be
 *                  NULL.
 *
 * @return  1, Function always succeeds or blocks forever.
 */
int msg_receive(msg_t *m);

/**
 * @brief Try to receive a message.
 *
 * This function does not block if no message can be received.
 *
 * @param[out] m    Pointer to preallocated ``msg_t`` structure, must not be
 *                  NULL.
 *
 * @return  1, if a message was received
 * @return  -1, otherwise.
 */
int msg_try_receive(msg_t *m);

/**
 * @brief Send a message, block until reply received.
 *
 * This function sends a message to *target_pid* and then blocks until target
 * has sent a reply which is then stored in *reply*.
 *
 * @pre     @p target_pid is not the PID of the current thread.
 *
 * @param[in] m             Pointer to preallocated ``msg_t`` structure with
 *                          the message to send, must not be NULL.
 * @param[out] reply        Pointer to preallocated msg. Reply will be written
 *                          here, must not be NULL. Can be identical to @p m.
 * @param[in] target_pid    The PID of the target process
 *
 * @return  1, if successful.
 */
int msg_send_receive(msg_t *m, msg_t *reply, kernel_pid_t target_pid);

/**
 * @brief Replies to a message.
 *
 * Sender must have sent the message with msg_send_receive().
 *
 * @param[in] m         message to reply to, must not be NULL.
 * @param[out] reply    message that target will get as reply, must not be NULL.
 *
 * @return 1, if successful
 * @return -1, on error
 */
int msg_reply(msg_t *m, msg_t *reply);

/**
 * @brief Replies to a message from interrupt.
 *
 * An ISR can obviously not receive messages, however a thread might delegate
 * replying to a message to an ISR.
 *
 * @param[in] m         message to reply to, must not be NULL.
 * @param[out] reply    message that target will get as reply, must not be NULL.
 *
 * @return 1, if successful
 * @return -1, on error
 */
int msg_reply_int(msg_t *m, msg_t *reply);

/**
 * @brief Check how many messages are available in the message queue
 *
 * @return Number of messages available in our queue on success
 * @return -1, if no caller's message queue is initialized
 */
int msg_avail(void);

/**
 * @brief Initialize the current thread's message queue.
 *
 * @pre @p num **MUST BE A POWER OF TWO!**
 *
 * @param[in] array Pointer to preallocated array of ``msg_t`` structures, must
 *                  not be NULL.
 * @param[in] num   Number of ``msg_t`` structures in array.
 *                  **MUST BE POWER OF TWO!**
 */
void msg_init_queue(msg_t *array, int num);

/**
 * @brief   Prints the message queue of the current thread.
 */
void msg_queue_print(void);

#ifdef __cplusplus
}
#endif

#endif /* MSG_H_ */
/** @} */