2 * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
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.
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.
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.
22 #include <gpxe/process.h>
23 #include <gpxe/async.h>
27 * Asynchronous operations
34 * @v signal Signal number
35 * @ret name Name of signal
37 static inline __attribute__ (( always_inline
)) const char *
38 signal_name ( enum signal signal
) {
40 case SIGCHLD
: return "SIGCHLD";
41 case SIGKILL
: return "SIGKILL";
42 case SIGUPDATE
: return "SIGUPDATE";
43 default: return "SIG<UNKNOWN>";
48 * Initialise an asynchronous operation
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
55 * It is valid to create an asynchronous operation with no parent
56 * operation; see async_init_orphan().
58 aid_t
async_init ( struct async
*async
, struct async_operations
*aop
,
59 struct async
*parent
) {
62 /* Assign identifier. Negative IDs are used to indicate
63 * errors, so avoid assigning them.
66 aid
&= ( ( ~( ( aid_t
) 0 ) ) >> 1 );
68 DBGC ( async
, "ASYNC %p (type %p) initialising as", async
, aop
);
70 DBGC ( async
, " child of ASYNC %p", parent
);
72 DBGC ( async
, " orphan" );
74 DBGC ( async
, " with ID %ld\n", aid
);
76 assert ( async
!= NULL
);
77 assert ( aop
!= NULL
);
79 /* Add to hierarchy */
81 async
->parent
= parent
;
82 list_add ( &async
->siblings
, &parent
->children
);
84 INIT_LIST_HEAD ( &async
->children
);
86 /* Initialise fields */
87 async
->rc
= -EINPROGRESS
;
97 * Uninitialise an asynchronous operation
99 * @v async Asynchronous operation
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.
107 * An example may help:
109 * int start_something ( ..., struct async *parent ) {
110 * struct my_data_structure *myself;
112 * ... allocate memory for myself ...
114 * async_init ( &myself->async, &my_async_operations, parent );
115 * if ( ( rc = start_child_operation ( ..., &myself->async ) ) != 0 ) {
116 * async_uninit ( &myself->async );
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
127 void async_uninit ( struct async
*async
) {
129 assert ( async
!= NULL
);
131 if ( async
->parent
) {
132 assert ( list_empty ( &async
->children
) );
134 DBGC ( async
, "ASYNC %p uninitialising\n", async
);
135 list_del ( &async
->siblings
);
140 * SIGCHLD 'ignore' handler
142 * @v async Asynchronous operation
143 * @v signal Signal received
145 static void async_ignore_sigchld ( struct async
*async
, enum signal signal
) {
148 assert ( async
!= NULL
);
149 assert ( signal
== SIGCHLD
);
152 waited_aid
= async_wait ( async
, NULL
, 0 );
153 assert ( waited_aid
>= 0 );
157 * SIGUPDATE 'ignore' handler
159 * @v async Asynchronous operation
160 * @v signal Signal received
162 static void async_ignore_sigupdate ( struct async
*async
,
163 enum signal signal
) {
166 assert ( async
!= NULL
);
167 assert ( signal
== SIGUPDATE
);
169 async_signal_children ( async
, signal
);
170 async
->completed
= 0;
172 list_for_each_entry ( child
, &async
->children
, siblings
) {
173 async
->completed
+= child
->completed
;
174 async
->total
+= child
->total
;
179 * 'Ignore' signal handler
181 * @v async Asynchronous operation
182 * @v signal Signal received
184 void async_ignore_signal ( struct async
*async
, enum signal signal
) {
186 DBGC ( async
, "ASYNC %p using ignore handler for %s\n",
187 async
, signal_name ( signal
) );
189 assert ( async
!= NULL
);
193 async_ignore_sigchld ( async
, signal
);
196 async_ignore_sigupdate ( async
, signal
);
206 * Default signal handler
208 * @v async Asynchronous operation
209 * @v signal Signal received
211 static void async_default_signal ( struct async
*async
, enum signal signal
) {
213 DBGC ( async
, "ASYNC %p using default handler for %s\n",
214 async
, signal_name ( signal
) );
216 assert ( async
!= NULL
);
229 * Send signal to asynchronous operation
231 * @v async Asynchronous operation
232 * @v signal Signal to send
234 void async_signal ( struct async
*async
, enum signal signal
) {
235 signal_handler_t handler
;
237 DBGC ( async
, "ASYNC %p receiving %s\n",
238 async
, signal_name ( signal
) );
240 assert ( async
!= NULL
);
241 assert ( async
->aop
!= NULL
);
242 assert ( signal
< SIGMAX
);
244 handler
= async
->aop
->signal
[signal
];
246 /* Use the asynchronous operation's signal handler */
247 handler ( async
, signal
);
249 /* Use the default handler */
250 async_default_signal ( async
, signal
);
255 * Send signal to all child asynchronous operations
257 * @v async Asynchronous operation
258 * @v signal Signal to send
260 void async_signal_children ( struct async
*async
, enum signal signal
) {
264 assert ( async
!= NULL
);
266 list_for_each_entry_safe ( child
, tmp
, &async
->children
, siblings
) {
267 async_signal ( child
, signal
);
272 * Reap default handler
274 * @v async Asynchronous operation
276 static void async_reap_default ( struct async
*async
) {
278 DBGC ( async
, "ASYNC %p ignoring REAP\n", async
);
280 assert ( async
!= NULL
);
286 * Reap asynchronous operation
288 * @v async Asynchronous operation
290 * Note that the asynchronous operation should have been freed by
291 * calling this function; you may not dereference @c async after this
294 static void async_reap ( struct async
*async
) {
296 DBGC ( async
, "ASYNC %p being reaped, exit status %d (%s)\n",
297 async
, async
->rc
, strerror ( async
->rc
) );
299 assert ( async
!= NULL
);
300 assert ( async
->aop
!= NULL
);
301 assert ( list_empty ( &async
->children
) );
303 /* Unlink from hierarchy */
305 list_del ( &async
->siblings
);
306 async
->parent
= NULL
;
308 /* Release all resources */
309 if ( async
->aop
->reap
) {
310 async
->aop
->reap ( async
);
312 async_reap_default ( async
);
317 * Mark asynchronous operation as complete
319 * @v async Asynchronous operation
320 * @v rc Return status code
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.
326 void async_done ( struct async
*async
, int rc
) {
330 DBGC ( async
, "ASYNC %p completing with status %d (%s)\n",
331 async
, rc
, strerror ( rc
) );
333 assert ( async
!= NULL
);
334 assert ( async
->parent
!= NULL
);
335 assert ( rc
!= -EINPROGRESS
);
337 /* Store return status code */
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",
344 list_del ( &child
->siblings
);
345 child
->parent
= NULL
;
348 /* Send SIGCHLD to parent. If we don't have a parent then we
349 * have to take care of our own funeral arrangements.
351 if ( async
->parent
) {
352 async_signal ( async
->parent
, SIGCHLD
);
354 async_reap ( async
);
359 * Wait for any child asynchronous operation to complete
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
366 aid_t
async_wait ( struct async
*async
, int *rc
, int block
) {
371 DBGC ( async
, "ASYNC %p performing %sblocking wait%s\n", async
,
372 ( block
? "" : "non-" ), ( rc
? "" : " (ignoring status)" ) );
374 assert ( async
!= NULL
);
376 /* Avoid multiple tests for "if ( rc )" */
382 /* Return immediately if we have no children */
383 if ( list_empty ( &async
->children
) ) {
384 DBGC ( async
, "ASYNC %p has no more children\n",
390 /* Look for a completed child */
391 list_for_each_entry ( child
, &async
->children
, siblings
) {
392 if ( child
->rc
== -EINPROGRESS
)
395 /* Found a completed child */
397 child_aid
= child
->aid
;
399 DBGC ( async
, "ASYNC %p reaping child ASYNC %p "
400 "(ID %ld)\n", async
, child
, child_aid
);
402 /* Reap the child and return */
403 async_reap ( child
);
407 /* Return immediately if non-blocking */
413 /* Allow processes to run */
419 * Wait for any child asynchronous operation to complete, with progress bar
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
425 aid_t
async_wait_progress ( struct async
*async
, int *rc
) {
427 long last_progress
= -1;
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
;
440 child_aid
= async_wait ( async
, rc
, 0 );
441 } while ( *rc
== -EINPROGRESS
);
448 * Default asynchronous operations
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
454 struct async_operations default_async_operations
= {
457 [SIGUPDATE
] = SIG_IGN
,
462 * Default asynchronous operations for orphan asynchronous operations
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.
469 struct async_operations orphan_async_operations
= {
472 [SIGUPDATE
] = SIG_IGN
,