42ee93d59a2fb6a5fbc4a56c72ab52298953d708
[people/xl0/gpxe.git] / src / include / gpxe / async.h
1 #ifndef _GPXE_ASYNC_H
2 #define _GPXE_ASYNC_H
3
4 /** @file
5  *
6  * Asynchronous operations
7  *
8  */
9
10 #include <gpxe/list.h>
11
12 struct async;
13
14 /** An asynchronous operation ID
15  *
16  * Only positive identifiers are valid; negative values are used to
17  * indicate errors.
18  */
19 typedef long aid_t;
20
21 /** Signals that can be delivered to asynchronous operations */
22 enum signal {
23         /** A child asynchronous operation has completed
24          *
25          * The parent should call async_wait() to reap the completed
26          * child.  async_wait() will return the exit status and
27          * operation identifier of the child.
28          *
29          * The handler for this signal can be set to @c NULL; if it
30          * is, then the children will accumulate as zombies until
31          * async_wait() is called.
32          *
33          * The handler for this signal can also be set to @c SIG_IGN;
34          * if it is, then the children will automatically be reaped.
35          * Note that if you use @c SIG_IGN then you will not be able
36          * to retrieve the return status of the children; the call to
37          * async_wait() will simply return -ECHILD.
38          */
39         SIGCHLD = 0,
40         /** Cancel asynchronous operation
41          *
42          * This signal should trigger the asynchronous operation to
43          * cancel itself (including killing all its own children, if
44          * any), and then call async_done().  The asynchronous
45          * operation is allowed to not complete immediately.
46          *
47          * The handler for this signal can be set to @c NULL; if it
48          * is, then attempts to cancel the asynchronous operation will
49          * fail and the operation will complete normally.  Anything
50          * waiting for the operation to cancel will block.
51          */
52         SIGKILL,
53         /** Update progress of asynchronous operation
54          *
55          * This signal should cause the asynchronous operation to
56          * immediately update the @c completed and @c total fields.
57          *
58          * The handler for this signal can be set to @c NULL; if it
59          * is, then the asynchronous operation is expected to keep its
60          * @c completed and @c total fields up to date at all times.
61          */
62         SIGUPDATE,
63         SIGMAX
64 };
65
66 /**
67  * A signal handler
68  *
69  * @v async             Asynchronous operation
70  * @v signal            Signal received
71  */
72 typedef void ( * signal_handler_t ) ( struct async *async,
73                                       enum signal signal );
74
75 /** Asynchronous operation operations */
76 struct async_operations {
77         /** Reap asynchronous operation
78          *
79          * @v async             Asynchronous operation
80          *
81          * Release all resources associated with the asynchronous
82          * operation.  This will be called only after the asynchronous
83          * operation itself calls async_done(), so the only remaining
84          * resources will probably be the memory used by the struct
85          * async itself.
86          *
87          * This method can be set to @c NULL; if it is, then no
88          * resources will be freed.  This may be suitable for
89          * asynchronous operations that consume no dynamically
90          * allocated memory.
91          */
92         void ( * reap ) ( struct async *async );
93         /** Handle signals */
94         signal_handler_t signal[SIGMAX];
95 };
96
97 /** An asynchronous operation */
98 struct async {
99         /** Other asynchronous operations with the same parent */
100         struct list_head siblings;
101         /** Child asynchronous operations */
102         struct list_head children;
103         /** Parent asynchronous operation
104          *
105          * This field is optional; if left to NULL then the owner must
106          * never call async_done().
107          */
108         struct async *parent;
109         /** Asynchronous operation ID */
110         aid_t aid;
111         /** Final return status code */
112         int rc;
113
114         /** Amount of operation completed so far
115          *
116          * The units for this quantity are arbitrary.  @c completed
117          * divded by @total should give something which approximately
118          * represents the progress through the operation.  For a
119          * download operation, using byte counts would make sense.
120          *
121          * This progress indicator should also incorporate the status
122          * of any child asynchronous operations.
123          */
124         unsigned long completed;
125         /** Total operation size
126          *
127          * See @c completed.  A zero value means "total size unknown"
128          * and is explcitly permitted; users should take this into
129          * account before calculating @c completed/total.
130          */
131         unsigned long total;
132
133         struct async_operations *aop;
134 };
135
136 extern struct async_operations default_async_operations;
137 extern struct async_operations orphan_async_operations;
138
139 extern aid_t async_init ( struct async *async, struct async_operations *aop,
140                           struct async *parent );
141 extern void async_uninit ( struct async *async );
142 extern void async_ignore_signal ( struct async *async, enum signal signal );
143 extern void async_signal ( struct async *async, enum signal signal );
144 extern void async_signal_children ( struct async *async, enum signal signal );
145 extern void async_done ( struct async *async, int rc );
146 extern aid_t async_wait ( struct async *async, int *rc, int block );
147
148 /** Default signal handler */
149 #define SIG_DFL NULL
150
151 /** Ignore signal */
152 #define SIG_IGN async_ignore_signal
153
154 /**
155  * Initialise orphan asynchronous operation
156  *
157  * @v async             Asynchronous operation
158  * @ret aid             Asynchronous operation ID
159  *
160  * An orphan asynchronous operation can act as a context for child
161  * operations.  However, you must not call async_done() on such an
162  * operation, since this would attempt to send a signal to its
163  * (non-existent) parent.  Instead, simply free the structure (after
164  * calling async_wait() to ensure that any child operations have
165  * completed).
166  */
167 static inline aid_t async_init_orphan ( struct async *async ) {
168         return async_init ( async, &orphan_async_operations, NULL );
169 }
170
171 /**
172  * Execute and block on an asynchronous operation
173  *
174  * @v async_temp        Temporary asynchronous operation structure to use
175  * @v START             Code used to start the asynchronous operation
176  * @ret rc              Return status code
177  *
178  * This is a notational shorthand for writing
179  *
180  *      async_init_orphan ( &async_temp );
181  *      if ( ( rc = START ) == 0 )
182  *              async_wait ( &async_temp );
183  *      if ( rc != 0 ) {
184  *         ...handle failure...
185  *      }
186  *
187  * and allows you instead to write
188  *
189  *      if ( ( rc = async_block ( &async_temp, START ) ) != 0 ) {
190  *         ...handle failure...
191  *      }
192  *
193  * The argument START is a code snippet; it should initiate an
194  * asynchronous operation as a child of @c async_temp and return an
195  * error status code if it failed to do so (e.g. due to malloc()
196  * failure).
197  */
198 #define async_block( async_temp, START ) ( {                    \
199                 int rc;                                         \
200                                                                 \
201                 async_init_orphan ( async_temp );               \
202                 if ( ( rc = START ) == 0 )                      \
203                         async_wait ( async_temp, &rc, 1 );      \
204                 rc;                                             \
205         } )
206
207 #endif /* _GPXE_ASYNC_H */