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