/** * \file * * * * \defgroup kern_msg Message box IPC * \ingroup kern * \{ * * \brief Simple inter-process messaging system * * This module implements a common system for executing * a user defined action calling a hook function. * * A message port is an abstraction used to exchange information * asynchronously between processes or other entities such as * interrupts and call-back functions. * * This form of IPC is higher-level than bare signals and * semaphores, because it sets a policy for exchanging * structured data with well-defined synchronization and * ownership semantics. * * Before using it, a message port must be initialized by * calling msg_initPort(), which associates the port with * an Event object, which can be setup to signal a process * or invoke a call-back hook. * * A process or interrupt routine can deliver messages to any * message port by calling msg_put(). By sending a message, * the sender temporarly or permanently transfers ownership * of its associated data to the receiver. * * Queuing a message to a port automatically triggers the * associated Event to notify the receiver. When the * receiver wakes up, it usually invokes msg_get() to pick * the next message from the port. * * \note * When you put a message into a port, such message becomes * unavailable until you retrieve it using msg_get(), eg. * you must not delete it or put it into another port. * * Message ports can hold any number of pending messages, * and receivers usually process them in FIFO order. * Other scheduling policies are possible, but not implemented * in this API. * * After the receiver has done processing a message, it replies * it back to the sender with msg_reply(), which transfer * ownership back to the original sender. Replies are delivered * to a reply port, which is nothing more than another MsgPort * structure designated by the sender. * * Returning messages to senders is not mandatory, but it provides * a convenient way to provide some kind of result and simplify * the resource allocation scheme at the same time. * * When using signals to receive messages in a process, you * call sig_wait() in an event-loop to wake up when messages * are delivered to any of your ports. When your process * wakes up with the port signal active, multiple messages * may already have queued up at the message port, and the * process must process them all before returning to sleep. * Signals don't keep a nesting count. * * A simple message loop works like this: * * \code * // Our message port. * static MsgPort test_port; * * // A test message with two parameters and a result. * typedef struct * { * Msg msg; * * int x, y; * int result; * } TestMsg; * * * PROC_DEFINE_STACK(sender_stack, KERN_MINSTACKSIZE); * * // A process that sends two messages and waits for replies. * static void sender_proc(void) * { * MsgPort test_reply_port; * TestMsg msg1; * TestMsg msg2; * Msg *reply; * * msg_initPort(&test_reply_port, * event_createSignal(proc_current(), SIG_SINGLE); * * // Fill-in first message and send it out. * msg1.x = 3; * msg1.y = 2; * msg1.msg.replyPort = &test_reply_port; * msg_put(&test_port, &msg1.msg); * * // Fill-in second message and send it out too. * msg2.x = 5; * msg2.y = 4; * msg2.msg.replyPort = &test_reply_port; * msg_put(&test_port, &msg2.msg); * * // Wait for a reply... * sig_wait(SIG_SINGLE); * reply = containerof(msg_get(&test_reply_port), TestMsg, msg); * ASSERT(reply != NULL); * ASSERT(reply->result == 5); * * // Get reply to second message. * while (!(reply = containerof(msg_get(&test_reply_port), TestMsg, msg))) * { * // Not yet, be patient and wait some more. * sig_wait(SIG_SINGLE); * } * * ASSERT(reply->result == 9); * } * * * // Receive messages and do something boring with them. * static void receiver_proc(void) * { * msg_initPort(&test_port, * event_createSignal(proc_current(), SIG_EXAMPLE); * * proc_new(sender_proc, NULL,sizeof(sender_stack), sender_stack); * * for (;;) * { * sigmask_t sigs = sig_wait(SIG_EXAMPLE | more_signals); * * if (sigs & SIG_EXAMPLE) * { * TestMsg *emsg; * while((emsg = containerof(msg_get(&test_port), TestMsg, msg))) * { * // Do something with the message * emsg->result = emsg->x + emsg->y; * msg_reply(emsg->msg); * } * } * } * } * \endcode * * \author Bernie Innocenti * * $WIZ$ module_name = "msg" * $WIZ$ module_depends = "event", "signal", "kernel" */ #ifndef KERN_MSG_H #define KERN_MSG_H #include #include #include typedef struct MsgPort { List queue; /**< Messages queued at this port. */ Event event; /**< Event to trigger when a message arrives. */ } MsgPort; typedef struct Msg { Node link; /**< Link into message port queue. */ MsgPort *replyPort; /**< Port to which the msg is to be replied. */ /* User data may follow */ } Msg; /** * Lock a message port. * * This is required before reading or manipulating * any field of the MsgPort structure. * * \note Ports may be locked multiple times and each * call to msg_lockPort() must be paired with * a corresponding call to msg_unlockPort(). * * \todo Add a configurable policy for locking against * interrupts and locking with semaphorse. * * \see msg_unlockPort() */ INLINE void msg_lockPort(UNUSED_ARG(MsgPort *, port)) { proc_forbid(); } /** * Unlock a message port. * * \see msg_lockPort() */ INLINE void msg_unlockPort(UNUSED_ARG(MsgPort *, port)) { proc_permit(); } /** Initialize a message port */ INLINE void msg_initPort(MsgPort *port, Event event) { LIST_INIT(&port->queue); port->event = event; } /** Queue \a msg into \a port, triggering the associated event */ INLINE void msg_put(MsgPort *port, Msg *msg) { msg_lockPort(port); ADDTAIL(&port->queue, &msg->link); msg_unlockPort(port); event_do(&port->event); } /** * Get the first message from the queue of \a port. * * \return Pointer to the message or NULL if the port was empty. */ INLINE Msg *msg_get(MsgPort *port) { Msg *msg; msg_lockPort(port); msg = (Msg *)list_remHead(&port->queue); msg_unlockPort(port); return msg; } /** Peek the first message in the queue of \a port, or NULL if the port is empty. */ INLINE Msg *msg_peek(MsgPort *port) { Msg *msg; msg_lockPort(port); msg = (Msg *)port->queue.head.succ; if (LIST_EMPTY(&port->queue)) msg = NULL; msg_unlockPort(port); return msg; } /** Send back (reply) \a msg to its sender. */ INLINE void msg_reply(Msg *msg) { msg_put(msg->replyPort, msg); } /** \} */ //defgroup kern_msg int msg_testRun(void); int msg_testSetup(void); int msg_testTearDown(void); #endif /* KERN_MSG_H */