Merge branch 'master' of ssh://git.samba.org/data/git/samba into wspp-schema
[kai/samba.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 #include "lib/tevent/tevent.h"
22 #include "lib/talloc/talloc.h"
23 #include "lib/util/dlinklist.h"
24 #include "lib/async_req/async_req.h"
25
26 #ifndef TALLOC_FREE
27 #define TALLOC_FREE(ctx) do { talloc_free(ctx); ctx=NULL; } while(0)
28 #endif
29
30 /**
31  * @brief Print an async_req structure
32  * @param[in] mem_ctx   The memory context for the result
33  * @param[in] req       The request to be printed
34  * @retval              Text representation of req
35  *
36  * This is a default print function for async requests. Implementations should
37  * override this with more specific information.
38  *
39  * This function should not be used by async API users, this is non-static
40  * only to allow implementations to easily provide default information in
41  * their specific functions.
42  */
43
44 char *async_req_print(TALLOC_CTX *mem_ctx, struct async_req *req)
45 {
46         return talloc_asprintf(mem_ctx, "async_req: state=%d, error=%d, "
47                                "priv=%s", req->state, (int)req->error,
48                                talloc_get_name(req->private_data));
49 }
50
51 /**
52  * @brief Create an async request
53  * @param[in] mem_ctx   The memory context for the result
54  * @param[in] ev        The event context this async request will be driven by
55  * @retval              A new async request
56  *
57  * The new async request will be initialized in state ASYNC_REQ_IN_PROGRESS
58  */
59
60 struct async_req *async_req_new(TALLOC_CTX *mem_ctx)
61 {
62         struct async_req *result;
63
64         result = talloc_zero(mem_ctx, struct async_req);
65         if (result == NULL) {
66                 return NULL;
67         }
68         result->state = ASYNC_REQ_IN_PROGRESS;
69         result->print = async_req_print;
70         return result;
71 }
72
73 static void async_req_finish(struct async_req *req, enum async_req_state state)
74 {
75         req->state = state;
76         if (req->async.fn != NULL) {
77                 req->async.fn(req);
78         }
79 }
80
81 /**
82  * @brief An async request has successfully finished
83  * @param[in] req       The finished request
84  *
85  * async_req_done is to be used by implementors of async requests. When a
86  * request is successfully finished, this function calls the user's completion
87  * function.
88  */
89
90 void async_req_done(struct async_req *req)
91 {
92         async_req_finish(req, ASYNC_REQ_DONE);
93 }
94
95 /**
96  * @brief An async request has seen an error
97  * @param[in] req       The request with an error
98  * @param[in] error     The error code
99  *
100  * async_req_done is to be used by implementors of async requests. When a
101  * request can not successfully completed, the implementation should call this
102  * function with the appropriate status code.
103  */
104
105 void async_req_error(struct async_req *req, uint64_t error)
106 {
107         req->error = error;
108         async_req_finish(req, ASYNC_REQ_USER_ERROR);
109 }
110
111 /**
112  * @brief Timed event callback
113  * @param[in] ev        Event context
114  * @param[in] te        The timed event
115  * @param[in] now       zero time
116  * @param[in] priv      The async request to be finished
117  */
118
119 static void async_trigger(struct tevent_context *ev, struct tevent_timer *te,
120                           struct timeval now, void *priv)
121 {
122         struct async_req *req = talloc_get_type_abort(priv, struct async_req);
123
124         TALLOC_FREE(te);
125         if (req->error == 0) {
126                 async_req_done(req);
127         }
128         else {
129                 async_req_error(req, req->error);
130         }
131 }
132
133 /**
134  * @brief Helper function for nomem check
135  * @param[in] p         The pointer to be checked
136  * @param[in] req       The request being processed
137  *
138  * Convenience helper to easily check alloc failure within a callback
139  * implementing the next step of an async request.
140  *
141  * Call pattern would be
142  * \code
143  * p = talloc(mem_ctx, bla);
144  * if (async_req_ntnomem(p, req)) {
145  *      return;
146  * }
147  * \endcode
148  */
149
150 bool async_req_nomem(const void *p, struct async_req *req)
151 {
152         if (p != NULL) {
153                 return false;
154         }
155         async_req_finish(req, ASYNC_REQ_NO_MEMORY);
156         return true;
157 }
158
159 /**
160  * @brief Finish a request before it started processing
161  * @param[in] req       The finished request
162  * @param[in] status    The success code
163  *
164  * An implementation of an async request might find that it can either finish
165  * the request without waiting for an external event, or it can't even start
166  * the engine. To present the illusion of a callback to the user of the API,
167  * the implementation can call this helper function which triggers an
168  * immediate timed event. This way the caller can use the same calling
169  * conventions, independent of whether the request was actually deferred.
170  */
171
172 bool async_post_error(struct async_req *req, struct tevent_context *ev,
173                       uint64_t error)
174 {
175         req->error = error;
176
177         if (tevent_add_timer(ev, req, tevent_timeval_zero(),
178                             async_trigger, req) == NULL) {
179                 return false;
180         }
181         return true;
182 }
183
184 bool async_req_is_error(struct async_req *req, enum async_req_state *state,
185                         uint64_t *error)
186 {
187         if (req->state == ASYNC_REQ_DONE) {
188                 return false;
189         }
190         if (req->state == ASYNC_REQ_USER_ERROR) {
191                 *error = req->error;
192         }
193         *state = req->state;
194         return true;
195 }
196
197 struct async_queue_entry {
198         struct async_queue_entry *prev, *next;
199         struct async_req_queue *queue;
200         struct async_req *req;
201         void (*trigger)(struct async_req *req);
202 };
203
204 struct async_req_queue {
205         struct async_queue_entry *queue;
206 };
207
208 struct async_req_queue *async_req_queue_init(TALLOC_CTX *mem_ctx)
209 {
210         return talloc_zero(mem_ctx, struct async_req_queue);
211 }
212
213 static int async_queue_entry_destructor(struct async_queue_entry *e)
214 {
215         struct async_req_queue *queue = e->queue;
216
217         DLIST_REMOVE(queue->queue, e);
218
219         if (queue->queue != NULL) {
220                 queue->queue->trigger(queue->queue->req);
221         }
222
223         return 0;
224 }
225
226 static void async_req_immediate_trigger(struct tevent_context *ev,
227                                         struct tevent_timer *te,
228                                         struct timeval now,
229                                         void *priv)
230 {
231         struct async_queue_entry *e = talloc_get_type_abort(
232                 priv, struct async_queue_entry);
233
234         TALLOC_FREE(te);
235         e->trigger(e->req);
236 }
237
238 bool async_req_enqueue(struct async_req_queue *queue, struct tevent_context *ev,
239                        struct async_req *req,
240                        void (*trigger)(struct async_req *req))
241 {
242         struct async_queue_entry *e;
243         bool busy;
244
245         busy = (queue->queue != NULL);
246
247         e = talloc(req, struct async_queue_entry);
248         if (e == NULL) {
249                 return false;
250         }
251
252         e->req = req;
253         e->trigger = trigger;
254         e->queue = queue;
255
256         DLIST_ADD_END(queue->queue, e, struct async_queue_entry *);
257         talloc_set_destructor(e, async_queue_entry_destructor);
258
259         if (!busy) {
260                 struct tevent_timer *te;
261
262                 te = tevent_add_timer(ev, e, tevent_timeval_zero(),
263                                      async_req_immediate_trigger,
264                                      e);
265                 if (te == NULL) {
266                         TALLOC_FREE(e);
267                         return false;
268                 }
269         }
270
271         return true;
272 }
273
274 bool _async_req_setup(TALLOC_CTX *mem_ctx, struct async_req **preq,
275                       void *pstate, size_t state_size, const char *typename)
276 {
277         struct async_req *req;
278         void **ppstate = (void **)pstate;
279         void *state;
280
281         req = async_req_new(mem_ctx);
282         if (req == NULL) {
283                 return false;
284         }
285         state = talloc_size(req, state_size);
286         if (state == NULL) {
287                 TALLOC_FREE(req);
288                 return false;
289         }
290         talloc_set_name_const(state, typename);
291         req->private_data = state;
292
293         *preq = req;
294         *ppstate = state;
295
296         return true;
297 }