Added async_block_progress() and default SIGUPDATE handler.
[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  * Uninitialise an asynchronous operation
98  *
99  * @v async             Asynchronous operation
100  *
101  * Abandon an asynchronous operation without signalling the parent.
102  * You may do this only during the period between calling async_init()
103  * and returning to the parent for the first time.  It is designed to
104  * simplify the error paths of asynchronous operations that themselves
105  * spawn further asynchronous operations.
106  *
107  * An example may help:
108  *
109  *     int start_something ( ..., struct async *parent ) {
110  *         struct my_data_structure *myself;
111  *
112  *         ... allocate memory for myself ...
113  *
114  *         async_init ( &myself->async, &my_async_operations, parent );
115  *         if ( ( rc = start_child_operation ( ..., &myself->async ) ) != 0 ) {
116  *             async_uninit ( &myself->async );
117  *             return rc;
118  *         }
119  *
120  *         return 0;
121  *     }
122  *
123  * It is valid to call async_uninit() on an asynchronous operation
124  * that has not yet been initialised (i.e. a zeroed-out @c struct @c
125  * async).
126  */
127 void async_uninit ( struct async *async ) {
128
129         assert ( async != NULL );
130
131         if ( async->parent ) {
132                 assert ( list_empty ( &async->children ) );
133
134                 DBGC ( async, "ASYNC %p uninitialising\n", async );
135                 list_del ( &async->siblings );
136         }
137 }
138
139 /**
140  * SIGCHLD 'ignore' handler
141  *
142  * @v async             Asynchronous operation
143  * @v signal            Signal received
144  */
145 static void async_ignore_sigchld ( struct async *async, enum signal signal ) {
146         aid_t waited_aid;
147
148         assert ( async != NULL );
149         assert ( signal == SIGCHLD );
150
151         /* Reap the child */
152         waited_aid = async_wait ( async, NULL, 0 );
153         assert ( waited_aid >= 0 );
154 }
155
156 /**
157  * SIGUPDATE 'ignore' handler
158  *
159  * @v async             Asynchronous operation
160  * @v signal            Signal received
161  */
162 static void async_ignore_sigupdate ( struct async *async,
163                                      enum signal signal ) {
164         struct async *child;
165
166         assert ( async != NULL );
167         assert ( signal == SIGUPDATE );
168
169         async_signal_children ( async, signal );
170         async->completed = 0;
171         async->total = 0;
172         list_for_each_entry ( child, &async->children, siblings ) {
173                 async->completed += child->completed;
174                 async->total += child->total;
175         }
176 }
177
178 /**
179  * 'Ignore' signal handler
180  *
181  * @v async             Asynchronous operation
182  * @v signal            Signal received
183  */
184 void async_ignore_signal ( struct async *async, enum signal signal ) {
185
186         DBGC ( async, "ASYNC %p using ignore handler for %s\n",
187                async, signal_name ( signal ) );
188
189         assert ( async != NULL );
190
191         switch ( signal ) {
192         case SIGCHLD:
193                 async_ignore_sigchld ( async, signal );
194                 break;
195         case SIGUPDATE:
196                 async_ignore_sigupdate ( async, signal );
197                 break;
198         case SIGKILL:
199         default:
200                 /* Nothing to do */
201                 break;
202         }
203 }
204
205 /**
206  * Default signal handler
207  *
208  * @v async             Asynchronous operation
209  * @v signal            Signal received
210  */
211 static void async_default_signal ( struct async *async, enum signal signal ) {
212
213         DBGC ( async, "ASYNC %p using default handler for %s\n",
214                async, signal_name ( signal ) );
215
216         assert ( async != NULL );
217
218         switch ( signal ) {
219         case SIGCHLD:
220         case SIGKILL:
221         case SIGUPDATE:
222         default:
223                 /* Nothing to do */
224                 break;
225         }
226 }
227
228 /**
229  * Send signal to asynchronous operation
230  *
231  * @v async             Asynchronous operation
232  * @v signal            Signal to send
233  */
234 void async_signal ( struct async *async, enum signal signal ) {
235         signal_handler_t handler;
236
237         DBGC ( async, "ASYNC %p receiving %s\n",
238                async, signal_name ( signal ) );
239
240         assert ( async != NULL );
241         assert ( async->aop != NULL );
242         assert ( signal < SIGMAX );
243
244         handler = async->aop->signal[signal];
245         if ( handler ) {
246                 /* Use the asynchronous operation's signal handler */
247                 handler ( async, signal );
248         } else {
249                 /* Use the default handler */
250                 async_default_signal ( async, signal );
251         }
252 }
253
254 /**
255  * Send signal to all child asynchronous operations
256  *
257  * @v async             Asynchronous operation
258  * @v signal            Signal to send
259  */
260 void async_signal_children ( struct async *async, enum signal signal ) {
261         struct async *child;
262         struct async *tmp;
263
264         assert ( async != NULL );
265
266         list_for_each_entry_safe ( child, tmp, &async->children, siblings ) {
267                 async_signal ( child, signal );
268         }
269 }
270
271 /**
272  * Reap default handler
273  *
274  * @v async             Asynchronous operation
275  */
276 static void async_reap_default ( struct async *async ) {
277
278         DBGC ( async, "ASYNC %p ignoring REAP\n", async );
279
280         assert ( async != NULL );
281
282         /* Nothing to do */
283 }
284
285 /**
286  * Reap asynchronous operation
287  *
288  * @v async             Asynchronous operation
289  *
290  * Note that the asynchronous operation should have been freed by
291  * calling this function; you may not dereference @c async after this
292  * call.
293  */
294 static void async_reap ( struct async *async ) {
295
296         DBGC ( async, "ASYNC %p being reaped, exit status %d (%s)\n",
297                async, async->rc, strerror ( async->rc ) );
298
299         assert ( async != NULL );
300         assert ( async->aop != NULL );
301         assert ( list_empty ( &async->children ) );
302
303         /* Unlink from hierarchy */
304         if ( async->parent )
305                 list_del ( &async->siblings );
306         async->parent = NULL;
307
308         /* Release all resources */
309         if ( async->aop->reap ) {
310                 async->aop->reap ( async );
311         } else {
312                 async_reap_default ( async );
313         }
314 }
315
316 /**
317  * Mark asynchronous operation as complete
318  *
319  * @v async             Asynchronous operation
320  * @v rc                Return status code
321  *
322  * An asynchronous operation should call this once it has completed.
323  * After calling async_done(), it must be prepared to be reaped by
324  * having its reap() method called.
325  */
326 void async_done ( struct async *async, int rc ) {
327         struct async *child;
328         struct async *tmp;
329
330         DBGC ( async, "ASYNC %p completing with status %d (%s)\n",
331                async, rc, strerror ( rc ) );
332
333         assert ( async != NULL );
334         assert ( async->parent != NULL );
335         assert ( rc != -EINPROGRESS );
336
337         /* Store return status code */
338         async->rc = rc;
339
340         /* Disown all of our children */
341         list_for_each_entry_safe ( child, tmp, &async->children, siblings ) {
342                 DBGC ( async, "ASYNC %p disowning child ASYNC %p\n",
343                        async, child );
344                 list_del ( &child->siblings );
345                 child->parent = NULL;
346         }
347
348         /* Send SIGCHLD to parent.  If we don't have a parent then we
349          * have to take care of our own funeral arrangements.
350          */
351         if ( async->parent ) {
352                 async_signal ( async->parent, SIGCHLD );
353         } else {
354                 async_reap ( async );
355         }
356 }
357
358 /**
359  * Wait for any child asynchronous operation to complete
360  * 
361  * @v child             Child asynchronous operation
362  * @v rc                Child exit status to fill in, or NULL
363  * @v block             Block waiting for child operation to complete
364  * @ret aid             Asynchronous operation ID, or -1 on error
365  */
366 aid_t async_wait ( struct async *async, int *rc, int block ) {
367         struct async *child;
368         aid_t child_aid;
369         int dummy_rc;
370
371         DBGC ( async, "ASYNC %p performing %sblocking wait%s\n", async,
372                ( block ? "" : "non-" ), ( rc ? "" : " (ignoring status)" ) );
373
374         assert ( async != NULL );
375
376         /* Avoid multiple tests for "if ( rc )" */
377         if ( ! rc )
378                 rc = &dummy_rc;
379
380         while ( 1 ) {
381
382                 /* Return immediately if we have no children */
383                 if ( list_empty ( &async->children ) ) {
384                         DBGC ( async, "ASYNC %p has no more children\n",
385                                async );
386                         *rc = -ECHILD;
387                         return -1;
388                 }
389
390                 /* Look for a completed child */
391                 list_for_each_entry ( child, &async->children, siblings ) {
392                         if ( child->rc == -EINPROGRESS )
393                                 continue;
394
395                         /* Found a completed child */
396                         *rc = child->rc;
397                         child_aid = child->aid;
398
399                         DBGC ( async, "ASYNC %p reaping child ASYNC %p "
400                                "(ID %ld)\n", async, child, child_aid );
401
402                         /* Reap the child and return */
403                         async_reap ( child );
404                         return child_aid;
405                 }
406
407                 /* Return immediately if non-blocking */
408                 if ( ! block ) {
409                         *rc = -EINPROGRESS;
410                         return -1;
411                 }
412
413                 /* Allow processes to run */
414                 step();
415         }
416 }
417
418 /**
419  * Wait for any child asynchronous operation to complete, with progress bar
420  * 
421  * @v child             Child asynchronous operation
422  * @v rc                Child exit status to fill in, or NULL
423  * @ret aid             Asynchronous operation ID, or -1 on error
424  */
425 aid_t async_wait_progress ( struct async *async, int *rc ) {
426         struct async *child;
427         long last_progress = -1;
428         long progress;
429         aid_t child_aid;
430
431         do {
432                 step();
433                 async_signal ( async, SIGUPDATE );
434                 if ( async->total ) {
435                         progress = ( async->completed / (async->total / 100) );
436                         if ( progress != last_progress )
437                                 printf ( "\rProgress: %d%%", progress );
438                         last_progress = progress;
439                 }
440                 child_aid = async_wait ( async, rc, 0 );
441         } while ( *rc == -EINPROGRESS );
442
443         printf ( "\n" );
444         return child_aid;
445 }
446
447 /**
448  * Default asynchronous operations
449  *
450  * The default is to ignore SIGCHLD (i.e. to automatically reap
451  * children) and to use the default handler (i.e. do nothing) for all
452  * other signals.
453  */
454 struct async_operations default_async_operations = {
455         .signal = {
456                 [SIGCHLD]       = SIG_IGN,
457                 [SIGUPDATE]     = SIG_IGN,
458         },
459 };
460
461 /**
462  * Default asynchronous operations for orphan asynchronous operations
463  *
464  * The default for orphan asynchronous operations is to do nothing for
465  * SIGCHLD (i.e. to not automatically reap children), on the
466  * assumption that you're probably creating the orphan solely in order
467  * to async_wait() on it.
468  */
469 struct async_operations orphan_async_operations = {
470         .signal = {
471                 [SIGCHLD]       = SIG_DFL,
472                 [SIGUPDATE]     = SIG_IGN,
473         },
474 };