011948a158cc4e20333573b742c16696ae02a08b
[ira/wip.git] / lib / async_req / async_req.c
1 /*
2    Unix SMB/CIFS implementation.
3    Infrastructure for async requests
4    Copyright (C) Volker Lendecke 2008
5
6    This program 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 3 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, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "includes.h"
21
22 /**
23  * @brief Print an async_req structure
24  * @param[in] mem_ctx   The memory context for the result
25  * @param[in] req       The request to be printed
26  * @retval              Text representation of req
27  *
28  * This is a default print function for async requests. Implementations should
29  * override this with more specific information.
30  *
31  * This function should not be used by async API users, this is non-static
32  * only to allow implementations to easily provide default information in
33  * their specific functions.
34  */
35
36 char *async_req_print(TALLOC_CTX *mem_ctx, struct async_req *req)
37 {
38         return talloc_asprintf(mem_ctx, "async_req: state=%d, status=%s, "
39                                "priv=%s", req->state, nt_errstr(req->status),
40                                talloc_get_name(req->private_data));
41 }
42
43 /**
44  * @brief Create an async request
45  * @param[in] mem_ctx   The memory context for the result
46  * @param[in] ev        The event context this async request will be driven by
47  * @retval              A new async request
48  *
49  * The new async request will be initialized in state ASYNC_REQ_IN_PROGRESS
50  */
51
52 struct async_req *async_req_new(TALLOC_CTX *mem_ctx)
53 {
54         struct async_req *result;
55
56         result = TALLOC_ZERO_P(mem_ctx, struct async_req);
57         if (result == NULL) {
58                 return NULL;
59         }
60         result->state = ASYNC_REQ_IN_PROGRESS;
61         result->print = async_req_print;
62         return result;
63 }
64
65 /**
66  * @brief An async request has successfully finished
67  * @param[in] req       The finished request
68  *
69  * async_req_done is to be used by implementors of async requests. When a
70  * request is successfully finished, this function calls the user's completion
71  * function.
72  */
73
74 void async_req_done(struct async_req *req)
75 {
76         req->status = NT_STATUS_OK;
77         req->state = ASYNC_REQ_DONE;
78         if (req->async.fn != NULL) {
79                 req->async.fn(req);
80         }
81 }
82
83 /**
84  * @brief An async request has seen an error
85  * @param[in] req       The request with an error
86  * @param[in] status    The error code
87  *
88  * async_req_done is to be used by implementors of async requests. When a
89  * request can not successfully completed, the implementation should call this
90  * function with the appropriate status code.
91  */
92
93 void async_req_error(struct async_req *req, NTSTATUS status)
94 {
95         req->status = status;
96         req->state = ASYNC_REQ_ERROR;
97         if (req->async.fn != NULL) {
98                 req->async.fn(req);
99         }
100 }
101
102 /**
103  * @brief Timed event callback
104  * @param[in] ev        Event context
105  * @param[in] te        The timed event
106  * @param[in] now       zero time
107  * @param[in] priv      The async request to be finished
108  */
109
110 static void async_trigger(struct event_context *ev, struct timed_event *te,
111                           struct timeval now, void *priv)
112 {
113         struct async_req *req = talloc_get_type_abort(priv, struct async_req);
114
115         TALLOC_FREE(te);
116         if (NT_STATUS_IS_OK(req->status)) {
117                 async_req_done(req);
118         }
119         else {
120                 async_req_error(req, req->status);
121         }
122 }
123
124 /**
125  * @brief Finish a request before it started processing
126  * @param[in] req       The finished request
127  * @param[in] status    The success code
128  *
129  * An implementation of an async request might find that it can either finish
130  * the request without waiting for an external event, or it can't even start
131  * the engine. To present the illusion of a callback to the user of the API,
132  * the implementation can call this helper function which triggers an
133  * immediate timed event. This way the caller can use the same calling
134  * conventions, independent of whether the request was actually deferred.
135  */
136
137 bool async_post_status(struct async_req *req, struct event_context *ev,
138                        NTSTATUS status)
139 {
140         req->status = status;
141
142         if (event_add_timed(ev, req, timeval_zero(),
143                             async_trigger, req) == NULL) {
144                 return false;
145         }
146         return true;
147 }
148
149 /**
150  * @brief Helper function for nomem check
151  * @param[in] p         The pointer to be checked
152  * @param[in] req       The request being processed
153  *
154  * Convenience helper to easily check alloc failure within a callback
155  * implementing the next step of an async request.
156  *
157  * Call pattern would be
158  * \code
159  * p = talloc(mem_ctx, bla);
160  * if (async_req_nomem(p, req)) {
161  *      return;
162  * }
163  * \endcode
164  */
165
166 bool async_req_nomem(const void *p, struct async_req *req)
167 {
168         if (p != NULL) {
169                 return false;
170         }
171         async_req_error(req, NT_STATUS_NO_MEMORY);
172         return true;
173 }
174
175 bool async_req_is_error(struct async_req *req, NTSTATUS *status)
176 {
177         if (req->state < ASYNC_REQ_DONE) {
178                 *status = NT_STATUS_INTERNAL_ERROR;
179                 return true;
180         }
181         if (req->state == ASYNC_REQ_ERROR) {
182                 *status = req->status;
183                 return true;
184         }
185         return false;
186 }
187
188 NTSTATUS async_req_simple_recv(struct async_req *req)
189 {
190         NTSTATUS status;
191
192         if (async_req_is_error(req, &status)) {
193                 return status;
194         }
195         return NT_STATUS_OK;
196 }
197
198 static void async_req_timedout(struct event_context *ev,
199                                struct timed_event *te,
200                                struct timeval now,
201                                void *priv)
202 {
203         struct async_req *req = talloc_get_type_abort(
204                 priv, struct async_req);
205         TALLOC_FREE(te);
206         async_req_error(req, NT_STATUS_IO_TIMEOUT);
207 }
208
209 bool async_req_set_timeout(struct async_req *req, struct event_context *ev,
210                            struct timeval to)
211 {
212         return (event_add_timed(ev, req,
213                                 timeval_current_ofs(to.tv_sec, to.tv_usec),
214                                 async_req_timedout, req)
215                 != NULL);
216 }
217
218 struct async_req *async_wait_send(TALLOC_CTX *mem_ctx,
219                                   struct event_context *ev,
220                                   struct timeval to)
221 {
222         struct async_req *result;
223
224         result = async_req_new(mem_ctx);
225         if (result == NULL) {
226                 return result;
227         }
228         if (!async_req_set_timeout(result, ev, to)) {
229                 TALLOC_FREE(result);
230                 return NULL;
231         }
232         return result;
233 }
234
235 NTSTATUS async_wait_recv(struct async_req *req)
236 {
237         return NT_STATUS_OK;
238 }
239
240 struct async_queue_entry {
241         struct async_queue_entry *prev, *next;
242         struct async_req_queue *queue;
243         struct async_req *req;
244         void (*trigger)(struct async_req *req);
245 };
246
247 struct async_req_queue {
248         struct async_queue_entry *queue;
249 };
250
251 struct async_req_queue *async_req_queue_init(TALLOC_CTX *mem_ctx)
252 {
253         return TALLOC_ZERO_P(mem_ctx, struct async_req_queue);
254 }
255
256 static int async_queue_entry_destructor(struct async_queue_entry *e)
257 {
258         struct async_req_queue *queue = e->queue;
259
260         DLIST_REMOVE(queue->queue, e);
261
262         if (queue->queue != NULL) {
263                 queue->queue->trigger(queue->queue->req);
264         }
265
266         return 0;
267 }
268
269 static void async_req_immediate_trigger(struct event_context *ev,
270                                         struct timed_event *te,
271                                         struct timeval now,
272                                         void *priv)
273 {
274         struct async_queue_entry *e = talloc_get_type_abort(
275                 priv, struct async_queue_entry);
276
277         TALLOC_FREE(te);
278         e->trigger(e->req);
279 }
280
281 bool async_req_enqueue(struct async_req_queue *queue, struct event_context *ev,
282                        struct async_req *req,
283                        void (*trigger)(struct async_req *req))
284 {
285         struct async_queue_entry *e;
286         bool busy;
287
288         busy = (queue->queue != NULL);
289
290         e = talloc(req, struct async_queue_entry);
291         if (e == NULL) {
292                 return false;
293         }
294
295         e->req = req;
296         e->trigger = trigger;
297         e->queue = queue;
298
299         DLIST_ADD_END(queue->queue, e, struct async_queue_entry *);
300         talloc_set_destructor(e, async_queue_entry_destructor);
301
302         if (!busy) {
303                 struct timed_event *te;
304
305                 te = event_add_timed(ev, e, timeval_zero(),
306                                      async_req_immediate_trigger,
307                                      e);
308                 if (te == NULL) {
309                         TALLOC_FREE(e);
310                         return false;
311                 }
312         }
313
314         return true;
315 }
316
317 bool _async_req_setup(TALLOC_CTX *mem_ctx, struct async_req **preq,
318                       void *pstate, size_t state_size, const char *typename)
319 {
320         struct async_req *req;
321         void **ppstate = (void **)pstate;
322         void *state;
323
324         req = async_req_new(mem_ctx);
325         if (req == NULL) {
326                 return false;
327         }
328         state = talloc_size(req, state_size);
329         if (state == NULL) {
330                 TALLOC_FREE(req);
331                 return false;
332         }
333         talloc_set_name(state, typename);
334         req->private_data = state;
335
336         *preq = req;
337         *ppstate = state;
338
339         return true;
340 }