Updated copiright notice.
[bertos.git] / kern / msg.h
1 /**
2  * \file
3  * <!--
4  * This file is part of BeRTOS.
5  *
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.
10  *
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.
15  *
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
19  *
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.
28  *
29  * Copyright 2004 Develer S.r.l. (http://www.develer.com/)
30  * Copyright 1999,2001 Bernardo Innocenti <bernie@develer.com>
31  * This file is part of DevLib - See README.devlib for information.
32  * -->
33  *
34  * \brief Simple inter-process messaging system
35  *
36  * This module implements a common system for executing
37  * a user defined action calling a hook function.
38  *
39  * \version $Id$
40  *
41  * \author Bernardo Innocenti <bernie@develer.com>
42  */
43
44 /*#*
45  *#* $Log$
46  *#* Revision 1.9  2006/07/19 12:56:27  bernie
47  *#* Convert to new Doxygen style.
48  *#*
49  *#* Revision 1.8  2005/11/04 16:20:02  bernie
50  *#* Fix reference to README.devlib in header.
51  *#*
52  *#* Revision 1.7  2005/02/09 21:48:30  bernie
53  *#* Doxygen fixes.
54  *#*
55  *#* Revision 1.6  2005/01/22 04:20:26  bernie
56  *#* Write extensive documentation; Add simple locking.
57  *#*
58  *#* Revision 1.5  2004/11/28 23:20:25  bernie
59  *#* Remove obsolete INITLIST macro.
60  *#*
61  *#* Revision 1.4  2004/10/19 08:22:09  bernie
62  *#* msg_peek(): New function.
63  *#*
64  *#* Revision 1.3  2004/08/25 14:12:09  rasky
65  *#* Aggiornato il comment block dei log RCS
66  *#*
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.
69  *#*
70  *#* Revision 1.1  2004/06/06 15:11:08  bernie
71  *#* Import into DevLib.
72  *#*
73  *#*/
74 #ifndef KERN_MSG_H
75 #define KERN_MSG_H
76
77 #include "event.h"
78 #include <mware/list.h>
79
80
81 /**
82  * Handle queues of messages associated an action.
83  *
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.
87  *
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.
92  *
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.
97  *
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.
102  *
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.
107  *
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
111  * in this API.
112  *
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.
118  *
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.
122  *
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.
130  *
131  * A simple message loop works like this:
132  *
133  * \code
134  *      // Our message port.
135  *      static MsgPort test_port;
136  *
137  *      // A test message with two parameters and a result.
138  *      typedef struct
139  *      {
140  *              Msg msg;
141  *
142  *              int x, y;
143  *              int result;
144  *      } TestMsg;
145  *
146  *
147  *      // A process that sends two messages and waits for replies.
148  *      static void sender_proc(void)
149  *      {
150  *              MsgPort test_reply_port;
151  *              TestMsg msg1;
152  *              TestMsg msg2;
153  *              Msg *reply;
154  *
155  *              msg_initPort(&reply_port,
156  *                      event_createSignal(proc_current(), SIGF_SINGLE);
157  *
158  *              // Fill-in first message and send it out.
159  *              msg1.x = 3;
160  *              msg1.y = 2;
161  *              msg1.msg.replyPort = &test_reply_port;
162  *              msg_put(&test_port, &msg1);
163  *
164  *              // Fill-in second message and send it out too.
165  *              msg2.x = 5;
166  *              msg2.y = 4;
167  *              msg2.msg.replyPort = &test_reply_port;
168  *              msg_put(&test_port, &msg1);
169  *
170  *              // Wait for a reply...
171  *              sig_wait(SIG_SINGLE);
172  *
173  *              reply = (TestMsg *)msg_get(&test_reply_port);
174  *              ASSERT(reply != NULL);
175  *              ASSERT(reply->result == 5);
176  *
177  *              // Get reply to second message.
178  *              while (!(reply = (TestMsg *)msg_get(&test_reply_port))
179  *              {
180  *                      // Not yet, be patient and wait some more.
181  *                      sig_wait(SIG_SINGLE);
182  *              }
183  *
184  *              ASSERT(reply->result == 9);
185  *      }
186  *
187  *
188  *      // Receive messages and do something boring with them.
189  *      static void receiver_proc(void)
190  *      {
191  *              msg_initPort(&test_port,
192  *                      event_createSignal(proc_current(), SIGF_EXAMPLE);
193  *
194  *              proc_new(sender_proc, (iptr_t)&test_port,
195  *                      sender_stack, sizeof(sender_stack);
196  *
197  *              for (;;)
198  *              {
199  *                      sigmask_t sigs = sig_wait(SIGF_EXAMPLE | more_signals);
200  *
201  *                      if (sigs & SIGF_EXAMPLE)
202  *                      {
203  *                              TestMsg *emsg;
204  *                              while (emsg = (TestMsg *)msg_get(&test_port)
205  *                              {
206  *                                      // Do something with the message
207  *                                      emsg->result = emsg->x + emsg->y;
208  *                                      msg_reply((Msg *)msg);
209  *                              }
210  *                      }
211  *              }
212  *      }
213  * \endcode
214  */
215 typedef struct MsgPort
216 {
217         List  queue;   /**< Messages queued at this port. */
218         Event event;   /**< Event to trigger when a message arrives. */
219 } MsgPort;
220
221
222 typedef struct Msg
223 {
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 */
227 } Msg;
228
229
230 /**
231  * Lock a message port.
232  *
233  * This is required before reading or manipulating
234  * any field of the MsgPort structure.
235  *
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().
239  *
240  * \todo Add a configurable policy for locking against
241  *       interrupts and locking with semaphorse.
242  *
243  * \see msg_unlockPort()
244  */
245 INLINE void msg_lockPort(MsgPort *port)
246 {
247         proc_forbid();
248 }
249
250 /**
251  * Unlock a message port.
252  *
253  * \see msg_lockPort()
254  */
255 INLINE void msg_unlockPort(MsgPort *port)
256 {
257         proc_permit();
258 }
259
260
261 /** Initialize a message port */
262 INLINE void msg_initPort(MsgPort *port, Event event)
263 {
264         LIST_INIT(&port->queue);
265         port->event = event;
266 }
267
268 /** Queue \a msg into \a port, triggering the associated event */
269 INLINE void msg_put(MsgPort *port, Msg *msg)
270 {
271         msg_portLock(port);
272         ADDTAIL(&port->queue, &msg->link);
273         msg_portUnlock(port);
274
275         event_do(&port->event);
276 }
277
278 /**
279  * Get the first message from the queue of \a port.
280  *
281  * \return Pointer to the message or NULL if the port was empty.
282  */
283 INLINE Msg *msg_get(MsgPort *port)
284 {
285         Msg *msg;
286
287         msg_portLock(port);
288         msg = (Msg *)REMHEAD(&port->queue);
289         msg_portUnlock(port);
290
291         return msg;
292 }
293
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)
296 {
297         Msg *msg;
298
299         msg_portLock(port);
300         msg = (Msg *)port->queue.head;
301         if (ISLISTEMPTY(&port->queue))
302                 msg = NULL;
303         msg_portUnlock(port);
304
305         return msg;
306 }
307
308 /** Send back (reply) \a msg to its sender. */
309 INLINE void msg_reply(Msg *msg)
310 {
311         msg_put(msg->replyPort, msg);
312 }
313
314 #endif /* KERN_MSG_H */