/* * 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 * #include * * #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 * #include * * #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 * @author Kévin Roussel */ #ifndef MSG_H_ #define MSG_H_ #include #include #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_ */ /** @} */