4 * This file is part of BeRTOS.
6 * Bertos is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 * As a special exception, you may use this file as part of a free software
21 * library without restriction. Specifically, if other files instantiate
22 * templates or use macros or inline functions from this file, or you compile
23 * this file and link it with other files to produce an executable, this
24 * file does not by itself cause the resulting executable to be covered by
25 * the GNU General Public License. This exception does not however
26 * invalidate any other reasons why the executable file might be covered by
27 * the GNU General Public License.
29 * Copyright 2004 Develer S.r.l. (http://www.develer.com/)
30 * Copyright 1999,2001 Bernardo Innocenti <bernie@develer.com>
34 * \brief Simple inter-process messaging system
36 * This module implements a common system for executing
37 * a user defined action calling a hook function.
41 * \author Bernardo Innocenti <bernie@develer.com>
46 *#* Revision 1.9 2006/07/19 12:56:27 bernie
47 *#* Convert to new Doxygen style.
49 *#* Revision 1.8 2005/11/04 16:20:02 bernie
50 *#* Fix reference to README.devlib in header.
52 *#* Revision 1.7 2005/02/09 21:48:30 bernie
55 *#* Revision 1.6 2005/01/22 04:20:26 bernie
56 *#* Write extensive documentation; Add simple locking.
58 *#* Revision 1.5 2004/11/28 23:20:25 bernie
59 *#* Remove obsolete INITLIST macro.
61 *#* Revision 1.4 2004/10/19 08:22:09 bernie
62 *#* msg_peek(): New function.
64 *#* Revision 1.3 2004/08/25 14:12:09 rasky
65 *#* Aggiornato il comment block dei log RCS
67 *#* Revision 1.2 2004/08/14 19:37:57 rasky
68 *#* Merge da SC: macros.h, pool.h, BIT_CHANGE, nome dei processi, etc.
70 *#* Revision 1.1 2004/06/06 15:11:08 bernie
71 *#* Import into DevLib.
78 #include <mware/list.h>
82 * Handle queues of messages associated an action.
84 * A message port is an abstraction used to exchange information
85 * asynchronously between processes or other entities such as
86 * interrupts and call-back functions.
88 * This form of IPC is higher-level than bare signals and
89 * semaphores, because it sets a policy for exchanging
90 * structured data with well-defined synchronization and
91 * ownership semantics.
93 * Before using it, a message port must be initialized by
94 * calling msg_initPort(), which associates the port with
95 * an Event object, which can be setup to signal a process
96 * or invoke a call-back hook.
98 * A process or interrupt routine can deliver messages to any
99 * message port by calling msg_put(). By sending a message,
100 * the sender temporarly or permanently transfers ownership
101 * of its associated data to the receiver.
103 * Queuing a message to a port automatically triggers the
104 * associated Event to notify the receiver. When the
105 * receiver wakes up, it usually invokes msg_get() to pick
106 * the next message from the port.
108 * Message ports can hold any number of pending messages,
109 * and receivers usually process them in FIFO order.
110 * Other scheduling policies are possible, but not implemented
113 * After the receiver has done processing a message, it replies
114 * it back to the sender with msg_reply(), which transfer
115 * ownership back to the original sender. Replies are delivered
116 * to a reply port, which is nothing more than another MsgPort
117 * structure designated by the sender.
119 * Returning messages to senders is not mandatory, but it provides
120 * a convenient way to provide some kind of result and simplify
121 * the resource allocation scheme at the same time.
123 * When using signals to receive messages in a process, you
124 * call sig_wait() in an event-loop to wake up when messages
125 * are delivered to any of your ports. When your process
126 * wakes up with the port signal active, multiple messages
127 * may already have queued up at the message port, and the
128 * process must process them all before returning to sleep.
129 * Signals don't keep a nesting count.
131 * A simple message loop works like this:
134 * // Our message port.
135 * static MsgPort test_port;
137 * // A test message with two parameters and a result.
147 * // A process that sends two messages and waits for replies.
148 * static void sender_proc(void)
150 * MsgPort test_reply_port;
155 * msg_initPort(&reply_port,
156 * event_createSignal(proc_current(), SIGF_SINGLE);
158 * // Fill-in first message and send it out.
161 * msg1.msg.replyPort = &test_reply_port;
162 * msg_put(&test_port, &msg1);
164 * // Fill-in second message and send it out too.
167 * msg2.msg.replyPort = &test_reply_port;
168 * msg_put(&test_port, &msg1);
170 * // Wait for a reply...
171 * sig_wait(SIG_SINGLE);
173 * reply = (TestMsg *)msg_get(&test_reply_port);
174 * ASSERT(reply != NULL);
175 * ASSERT(reply->result == 5);
177 * // Get reply to second message.
178 * while (!(reply = (TestMsg *)msg_get(&test_reply_port))
180 * // Not yet, be patient and wait some more.
181 * sig_wait(SIG_SINGLE);
184 * ASSERT(reply->result == 9);
188 * // Receive messages and do something boring with them.
189 * static void receiver_proc(void)
191 * msg_initPort(&test_port,
192 * event_createSignal(proc_current(), SIGF_EXAMPLE);
194 * proc_new(sender_proc, (iptr_t)&test_port,
195 * sender_stack, sizeof(sender_stack);
199 * sigmask_t sigs = sig_wait(SIGF_EXAMPLE | more_signals);
201 * if (sigs & SIGF_EXAMPLE)
204 * while (emsg = (TestMsg *)msg_get(&test_port)
206 * // Do something with the message
207 * emsg->result = emsg->x + emsg->y;
208 * msg_reply((Msg *)msg);
215 typedef struct MsgPort
217 List queue; /**< Messages queued at this port. */
218 Event event; /**< Event to trigger when a message arrives. */
224 Node link; /**< Link into message port queue. */
225 MsgPort *replyPort; /**< Port to which the msg is to be replied. */
226 /* User data may follow */
231 * Lock a message port.
233 * This is required before reading or manipulating
234 * any field of the MsgPort structure.
236 * \note Ports may be locked multiple times and each
237 * call to msg_lockPort() must be paired with
238 * a corresponding call to msg_unlockPort().
240 * \todo Add a configurable policy for locking against
241 * interrupts and locking with semaphorse.
243 * \see msg_unlockPort()
245 INLINE void msg_lockPort(MsgPort *port)
251 * Unlock a message port.
253 * \see msg_lockPort()
255 INLINE void msg_unlockPort(MsgPort *port)
261 /** Initialize a message port */
262 INLINE void msg_initPort(MsgPort *port, Event event)
264 LIST_INIT(&port->queue);
268 /** Queue \a msg into \a port, triggering the associated event */
269 INLINE void msg_put(MsgPort *port, Msg *msg)
272 ADDTAIL(&port->queue, &msg->link);
273 msg_portUnlock(port);
275 event_do(&port->event);
279 * Get the first message from the queue of \a port.
281 * \return Pointer to the message or NULL if the port was empty.
283 INLINE Msg *msg_get(MsgPort *port)
288 msg = (Msg *)REMHEAD(&port->queue);
289 msg_portUnlock(port);
294 /** Peek the first message in the queue of \a port, or NULL if the port is empty. */
295 INLINE Msg *msg_peek(MsgPort *port)
300 msg = (Msg *)port->queue.head;
301 if (ISLISTEMPTY(&port->queue))
303 msg_portUnlock(port);
308 /** Send back (reply) \a msg to its sender. */
309 INLINE void msg_reply(Msg *msg)
311 msg_put(msg->replyPort, msg);
314 #endif /* KERN_MSG_H */