f46b16795f79bad384e5eba2a445a1b5693b005e
[bertos.git] / kern / msg.h
1 /**
2  * \file
3  * <!--
4  * Copyright 2004 Develer S.r.l. (http://www.develer.com/)
5  * Copyright 1999,2001 Bernardo Innocenti <bernie@develer.com>
6  * This file is part of DevLib - See README.devlib for information.
7  * -->
8  *
9  * \brief Simple inter-process messaging system
10  *
11  * This module implements a common system for executing
12  * a user defined action calling a hook function.
13  *
14  * \version $Id$
15  *
16  * \author Bernardo Innocenti <bernie@develer.com>
17  */
18
19 /*#*
20  *#* $Log$
21  *#* Revision 1.9  2006/07/19 12:56:27  bernie
22  *#* Convert to new Doxygen style.
23  *#*
24  *#* Revision 1.8  2005/11/04 16:20:02  bernie
25  *#* Fix reference to README.devlib in header.
26  *#*
27  *#* Revision 1.7  2005/02/09 21:48:30  bernie
28  *#* Doxygen fixes.
29  *#*
30  *#* Revision 1.6  2005/01/22 04:20:26  bernie
31  *#* Write extensive documentation; Add simple locking.
32  *#*
33  *#* Revision 1.5  2004/11/28 23:20:25  bernie
34  *#* Remove obsolete INITLIST macro.
35  *#*
36  *#* Revision 1.4  2004/10/19 08:22:09  bernie
37  *#* msg_peek(): New function.
38  *#*
39  *#* Revision 1.3  2004/08/25 14:12:09  rasky
40  *#* Aggiornato il comment block dei log RCS
41  *#*
42  *#* Revision 1.2  2004/08/14 19:37:57  rasky
43  *#* Merge da SC: macros.h, pool.h, BIT_CHANGE, nome dei processi, etc.
44  *#*
45  *#* Revision 1.1  2004/06/06 15:11:08  bernie
46  *#* Import into DevLib.
47  *#*
48  *#*/
49 #ifndef KERN_MSG_H
50 #define KERN_MSG_H
51
52 #include "event.h"
53 #include <mware/list.h>
54
55
56 /**
57  * Handle queues of messages associated an action.
58  *
59  * A message port is an abstraction used to exchange information
60  * asynchronously between processes or other entities such as
61  * interrupts and call-back functions.
62  *
63  * This form of IPC is higher-level than bare signals and
64  * semaphores, because it sets a policy for exchanging
65  * structured data with well-defined synchronization and
66  * ownership semantics.
67  *
68  * Before using it, a message port must be initialized by
69  * calling msg_initPort(), which associates the port with
70  * an Event object, which can be setup to signal a process
71  * or invoke a call-back hook.
72  *
73  * A process or interrupt routine can deliver messages to any
74  * message port by calling msg_put().  By sending a message,
75  * the sender temporarly or permanently transfers ownership
76  * of its associated data to the receiver.
77  *
78  * Queuing a message to a port automatically triggers the
79  * associated Event to notify the receiver.  When the
80  * receiver wakes up, it usually invokes msg_get() to pick
81  * the next message from the port.
82  *
83  * Message ports can hold any number of pending messages,
84  * and receivers usually process them in FIFO order.
85  * Other scheduling policies are possible, but not implemented
86  * in this API.
87  *
88  * After the receiver has done processing a message, it replies
89  * it back to the sender with msg_reply(), which transfer
90  * ownership back to the original sender.  Replies are delivered
91  * to a reply port, which is nothing more than another MsgPort
92  * structure designated by the sender.
93  *
94  * Returning messages to senders is not mandatory, but it provides
95  * a convenient way to provide some kind of result and simplify
96  * the resource allocation scheme at the same time.
97  *
98  * When using signals to receive messages in a process, you
99  * call sig_wait() in an event-loop to wake up when messages
100  * are delivered to any of your ports.  When your process
101  * wakes up with the port signal active, multiple messages
102  * may already have queued up at the message port, and the
103  * process must process them all before returning to sleep.
104  * Signals don't keep a nesting count.
105  *
106  * A simple message loop works like this:
107  *
108  * \code
109  *      // Our message port.
110  *      static MsgPort test_port;
111  *
112  *      // A test message with two parameters and a result.
113  *      typedef struct
114  *      {
115  *              Msg msg;
116  *
117  *              int x, y;
118  *              int result;
119  *      } TestMsg;
120  *
121  *
122  *      // A process that sends two messages and waits for replies.
123  *      static void sender_proc(void)
124  *      {
125  *              MsgPort test_reply_port;
126  *              TestMsg msg1;
127  *              TestMsg msg2;
128  *              Msg *reply;
129  *
130  *              msg_initPort(&reply_port,
131  *                      event_createSignal(proc_current(), SIGF_SINGLE);
132  *
133  *              // Fill-in first message and send it out.
134  *              msg1.x = 3;
135  *              msg1.y = 2;
136  *              msg1.msg.replyPort = &test_reply_port;
137  *              msg_put(&test_port, &msg1);
138  *
139  *              // Fill-in second message and send it out too.
140  *              msg2.x = 5;
141  *              msg2.y = 4;
142  *              msg2.msg.replyPort = &test_reply_port;
143  *              msg_put(&test_port, &msg1);
144  *
145  *              // Wait for a reply...
146  *              sig_wait(SIG_SINGLE);
147  *
148  *              reply = (TestMsg *)msg_get(&test_reply_port);
149  *              ASSERT(reply != NULL);
150  *              ASSERT(reply->result == 5);
151  *
152  *              // Get reply to second message.
153  *              while (!(reply = (TestMsg *)msg_get(&test_reply_port))
154  *              {
155  *                      // Not yet, be patient and wait some more.
156  *                      sig_wait(SIG_SINGLE);
157  *              }
158  *
159  *              ASSERT(reply->result == 9);
160  *      }
161  *
162  *
163  *      // Receive messages and do something boring with them.
164  *      static void receiver_proc(void)
165  *      {
166  *              msg_initPort(&test_port,
167  *                      event_createSignal(proc_current(), SIGF_EXAMPLE);
168  *
169  *              proc_new(sender_proc, (iptr_t)&test_port,
170  *                      sender_stack, sizeof(sender_stack);
171  *
172  *              for (;;)
173  *              {
174  *                      sigmask_t sigs = sig_wait(SIGF_EXAMPLE | more_signals);
175  *
176  *                      if (sigs & SIGF_EXAMPLE)
177  *                      {
178  *                              TestMsg *emsg;
179  *                              while (emsg = (TestMsg *)msg_get(&test_port)
180  *                              {
181  *                                      // Do something with the message
182  *                                      emsg->result = emsg->x + emsg->y;
183  *                                      msg_reply((Msg *)msg);
184  *                              }
185  *                      }
186  *              }
187  *      }
188  * \endcode
189  */
190 typedef struct MsgPort
191 {
192         List  queue;   /**< Messages queued at this port. */
193         Event event;   /**< Event to trigger when a message arrives. */
194 } MsgPort;
195
196
197 typedef struct Msg
198 {
199         Node     link;      /**< Link into message port queue. */
200         MsgPort *replyPort; /**< Port to which the msg is to be replied. */
201         /* User data may follow */
202 } Msg;
203
204
205 /**
206  * Lock a message port.
207  *
208  * This is required before reading or manipulating
209  * any field of the MsgPort structure.
210  *
211  * \note Ports may be locked multiple times and each
212  *       call to msg_lockPort() must be paired with
213  *       a corresponding call to msg_unlockPort().
214  *
215  * \todo Add a configurable policy for locking against
216  *       interrupts and locking with semaphorse.
217  *
218  * \see msg_unlockPort()
219  */
220 INLINE void msg_lockPort(MsgPort *port)
221 {
222         proc_forbid();
223 }
224
225 /**
226  * Unlock a message port.
227  *
228  * \see msg_lockPort()
229  */
230 INLINE void msg_unlockPort(MsgPort *port)
231 {
232         proc_permit();
233 }
234
235
236 /** Initialize a message port */
237 INLINE void msg_initPort(MsgPort *port, Event event)
238 {
239         LIST_INIT(&port->queue);
240         port->event = event;
241 }
242
243 /** Queue \a msg into \a port, triggering the associated event */
244 INLINE void msg_put(MsgPort *port, Msg *msg)
245 {
246         msg_portLock(port);
247         ADDTAIL(&port->queue, &msg->link);
248         msg_portUnlock(port);
249
250         event_do(&port->event);
251 }
252
253 /**
254  * Get the first message from the queue of \a port.
255  *
256  * \return Pointer to the message or NULL if the port was empty.
257  */
258 INLINE Msg *msg_get(MsgPort *port)
259 {
260         Msg *msg;
261
262         msg_portLock(port);
263         msg = (Msg *)REMHEAD(&port->queue);
264         msg_portUnlock(port);
265
266         return msg;
267 }
268
269 /** Peek the first message in the queue of \a port, or NULL if the port is empty. */
270 INLINE Msg *msg_peek(MsgPort *port)
271 {
272         Msg *msg;
273
274         msg_portLock(port);
275         msg = (Msg *)port->queue.head;
276         if (ISLISTEMPTY(&port->queue))
277                 msg = NULL;
278         msg_portUnlock(port);
279
280         return msg;
281 }
282
283 /** Send back (reply) \a msg to its sender. */
284 INLINE void msg_reply(Msg *msg)
285 {
286         msg_put(msg->replyPort, msg);
287 }
288
289 #endif /* KERN_MSG_H */