Gave asynchronous operations approximate POSIX signal semantics. This
[people/xl0/gpxe.git] / src / core / async.c
1 /*
2  * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License as
6  * published by the Free Software Foundation; either version 2 of the
7  * License, or any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  */
18
19 #include <string.h>
20 #include <errno.h>
21 #include <assert.h>
22 #include <gpxe/process.h>
23 #include <gpxe/async.h>
24
25 /** @file
26  *
27  * Asynchronous operations
28  *
29  */
30
31 /**
32  * Name signal
33  *
34  * @v signal            Signal number
35  * @ret name            Name of signal
36  */
37 static inline __attribute__ (( always_inline )) const char *
38 signal_name ( enum signal signal ) {
39         switch ( signal ) {
40         case SIGCHLD:           return "SIGCHLD";
41         case SIGKILL:           return "SIGKILL";
42         case SIGUPDATE:         return "SIGUPDATE";
43         default:                return "SIG<UNKNOWN>";
44         }
45 }
46
47 /**
48  * Initialise an asynchronous operation
49  *
50  * @v async             Asynchronous operation
51  * @v aop               Asynchronous operation operations to use
52  * @v parent            Parent asynchronous operation, or NULL
53  * @ret aid             Asynchronous operation ID
54  *
55  * It is valid to create an asynchronous operation with no parent
56  * operation; see async_init_orphan().
57  */
58 aid_t async_init ( struct async *async, struct async_operations *aop,
59                    struct async *parent ) {
60         static aid_t aid = 1;
61
62         /* Assign identifier.  Negative IDs are used to indicate
63          * errors, so avoid assigning them.
64          */
65         ++aid;
66         aid &= ( ( ~( ( aid_t ) 0 ) ) >> 1 );
67
68         DBGC ( async, "ASYNC %p (type %p) initialising as", async, aop );
69         if ( parent ) {
70                 DBGC ( async, " child of ASYNC %p", parent );
71         } else {
72                 DBGC ( async, " orphan" );
73         }
74         DBGC ( async, " with ID %ld\n", aid );
75
76         assert ( async != NULL );
77         assert ( aop != NULL );
78
79         /* Add to hierarchy */
80         if ( parent ) {
81                 async->parent = parent;
82                 list_add ( &async->siblings, &parent->children );
83         }
84         INIT_LIST_HEAD ( &async->children );
85
86         /* Initialise fields */
87         async->rc = -EINPROGRESS;
88         async->completed = 0;
89         async->total = 0;
90         async->aop = aop;
91         async->aid = aid;
92
93         return async->aid;
94 }
95
96 /**
97  * SIGCHLD 'ignore' handler
98  *
99  * @v async             Asynchronous operation
100  * @v signal            Signal received
101  */
102 static void async_ignore_sigchld ( struct async *async, enum signal signal ) {
103         aid_t waited_aid;
104
105         assert ( async != NULL );
106         assert ( signal == SIGCHLD );
107
108         /* Reap the child */
109         waited_aid = async_wait ( async, NULL, 0 );
110         assert ( waited_aid >= 0 );
111 }
112
113 /**
114  * 'Ignore' signal handler
115  *
116  * @v async             Asynchronous operation
117  * @v signal            Signal received
118  */
119 void async_ignore_signal ( struct async *async, enum signal signal ) {
120
121         DBGC ( async, "ASYNC %p using ignore handler for %s\n",
122                async, signal_name ( signal ) );
123
124         assert ( async != NULL );
125
126         switch ( signal ) {
127         case SIGCHLD:
128                 async_ignore_sigchld ( async, signal );
129                 break;
130         case SIGKILL:
131         case SIGUPDATE:
132         default:
133                 /* Nothing to do */
134                 break;
135         }
136 }
137
138 /**
139  * Default signal handler
140  *
141  * @v async             Asynchronous operation
142  * @v signal            Signal received
143  */
144 static void async_default_signal ( struct async *async, enum signal signal ) {
145
146         DBGC ( async, "ASYNC %p using default handler for %s\n",
147                async, signal_name ( signal ) );
148
149         assert ( async != NULL );
150
151         switch ( signal ) {
152         case SIGCHLD:
153         case SIGKILL:
154         case SIGUPDATE:
155         default:
156                 /* Nothing to do */
157                 break;
158         }
159 }
160
161 /**
162  * Send signal to asynchronous operation
163  *
164  * @v async             Asynchronous operation
165  * @v signal            Signal to send
166  */
167 void async_signal ( struct async *async, enum signal signal ) {
168         signal_handler_t handler;
169
170         DBGC ( async, "ASYNC %p receiving %s\n",
171                async, signal_name ( signal ) );
172
173         assert ( async != NULL );
174         assert ( async->aop != NULL );
175         assert ( signal < SIGMAX );
176
177         handler = async->aop->signal[signal];
178         if ( handler ) {
179                 /* Use the asynchronous operation's signal handler */
180                 handler ( async, signal );
181         } else {
182                 /* Use the default handler */
183                 async_default_signal ( async, signal );
184         }
185 }
186
187 /**
188  * Send signal to all child asynchronous operations
189  *
190  * @v async             Asynchronous operation
191  * @v signal            Signal to send
192  */
193 void async_signal_children ( struct async *async, enum signal signal ) {
194         struct async *child;
195         struct async *tmp;
196
197         assert ( async != NULL );
198
199         list_for_each_entry_safe ( child, tmp, &async->children, siblings ) {
200                 async_signal ( child, signal );
201         }
202 }
203
204 /**
205  * Mark asynchronous operation as complete
206  *
207  * @v async             Asynchronous operation
208  * @v rc                Return status code
209  *
210  * An asynchronous operation should call this once it has completed.
211  * After calling async_done(), it must be prepared to be reaped by
212  * having its reap() method called.
213  */
214 void async_done ( struct async *async, int rc ) {
215
216         DBGC ( async, "ASYNC %p completing with status %d (%s)\n",
217                async, rc, strerror ( rc ) );
218
219         assert ( async != NULL );
220         assert ( async->parent != NULL );
221         assert ( rc != -EINPROGRESS );
222
223         /* Store return status code */
224         async->rc = rc;
225
226         /* Send SIGCHLD to parent.  Guard against NULL pointer dereferences */
227         if ( async->parent )
228                 async_signal ( async->parent, SIGCHLD );
229 }
230
231 /**
232  * Reap default handler
233  *
234  * @v async             Asynchronous operation
235  */
236 static void async_reap_default ( struct async *async ) {
237
238         DBGC ( async, "ASYNC %p ignoring REAP\n", async );
239
240         assert ( async != NULL );
241
242         /* Nothing to do */
243 }
244
245 /**
246  * Wait for any child asynchronous operation to complete
247  * 
248  * @v child             Child asynchronous operation
249  * @v rc                Child exit status to fill in, or NULL
250  * @v block             Block waiting for child operation to complete
251  * @ret aid             Asynchronous operation ID, or -1 on error
252  */
253 aid_t async_wait ( struct async *async, int *rc, int block ) {
254         struct async *child;
255         aid_t child_aid;
256         int dummy_rc;
257
258         DBGC ( async, "ASYNC %p performing %sblocking wait%s\n", async,
259                ( block ? "" : "non-" ), ( rc ? "" : " (ignoring status)" ) );
260
261         assert ( async != NULL );
262
263         /* Avoid multiple tests for "if ( rc )" */
264         if ( ! rc )
265                 rc = &dummy_rc;
266
267         while ( 1 ) {
268
269                 /* Return immediately if we have no children */
270                 if ( list_empty ( &async->children ) ) {
271                         DBGC ( async, "ASYNC %p has no more children\n",
272                                async );
273                         *rc = -ECHILD;
274                         return -1;
275                 }
276
277                 /* Look for a completed child */
278                 list_for_each_entry ( child, &async->children, siblings ) {
279                         if ( child->rc == -EINPROGRESS )
280                                 continue;
281
282                         /* Found a completed child */
283                         *rc = child->rc;
284                         child_aid = child->aid;
285
286                         DBGC ( async, "ASYNC %p reaping child ASYNC %p (ID "
287                                "%ld), exit status %d (%s)\n", async, child,
288                                child_aid, child->rc, strerror ( child->rc ) );
289
290                         /* Reap the child */
291                         assert ( child->aop != NULL );
292                         assert ( list_empty ( &child->children ) );
293
294                         /* Unlink from operations hierarchy */
295                         list_del ( &child->siblings );
296                         child->parent = NULL;
297
298                         /* Release all resources */
299                         if ( child->aop->reap ) {
300                                 child->aop->reap ( child );
301                         } else {
302                                 async_reap_default ( child );
303                         }
304
305                         return child_aid;
306                 }
307
308                 /* Return immediately if non-blocking */
309                 if ( ! block ) {
310                         *rc = -EINPROGRESS;
311                         return -1;
312                 }
313
314                 /* Allow processes to run */
315                 step();
316         }
317 }
318
319 /**
320  * Default asynchronous operations
321  *
322  * The default is to ignore SIGCHLD (i.e. to automatically reap
323  * children) and to use the default handler (i.e. do nothing) for all
324  * other signals.
325  */
326 struct async_operations default_async_operations = {
327         .signal = {
328                 [SIGCHLD] = SIG_IGN,
329         },
330 };
331
332 /**
333  * Default asynchronous operations for orphan asynchronous operations
334  *
335  * The default for orphan asynchronous operations is to do nothing for
336  * SIGCHLD (i.e. to not automatically reap children), on the
337  * assumption that you're probably creating the orphan solely in order
338  * to async_wait() on it.
339  */
340 struct async_operations orphan_async_operations = {
341         .signal = {
342                 [SIGCHLD] = SIG_DFL,
343         },
344 };