Simplify async programming a bit with helper routines
[kai/samba.git] / source3 / lib / 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, struct event_context *ev)
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->event_ctx = ev;
62         result->print = async_req_print;
63         return result;
64 }
65
66 /**
67  * @brief An async request has successfully finished
68  * @param[in] req       The finished request
69  *
70  * async_req_done is to be used by implementors of async requests. When a
71  * request is successfully finished, this function calls the user's completion
72  * function.
73  */
74
75 void async_req_done(struct async_req *req)
76 {
77         req->status = NT_STATUS_OK;
78         req->state = ASYNC_REQ_DONE;
79         if (req->async.fn != NULL) {
80                 req->async.fn(req);
81         }
82 }
83
84 /**
85  * @brief An async request has seen an error
86  * @param[in] req       The request with an error
87  * @param[in] status    The error code
88  *
89  * async_req_done is to be used by implementors of async requests. When a
90  * request can not successfully completed, the implementation should call this
91  * function with the appropriate status code.
92  */
93
94 void async_req_error(struct async_req *req, NTSTATUS status)
95 {
96         req->status = status;
97         req->state = ASYNC_REQ_ERROR;
98         if (req->async.fn != NULL) {
99                 req->async.fn(req);
100         }
101 }
102
103 /**
104  * @brief Timed event callback
105  * @param[in] ev        Event context
106  * @param[in] te        The timed event
107  * @param[in] now       current time
108  * @param[in] priv      The async request to be finished
109  */
110
111 static void async_trigger(struct event_context *ev, struct timed_event *te,
112                           const struct timeval *now, void *priv)
113 {
114         struct async_req *req = talloc_get_type_abort(priv, struct async_req);
115
116         TALLOC_FREE(te);
117         if (NT_STATUS_IS_OK(req->status)) {
118                 async_req_done(req);
119         }
120         else {
121                 async_req_error(req, req->status);
122         }
123 }
124
125 /**
126  * @brief Finish a request before it started processing
127  * @param[in] req       The finished request
128  * @param[in] status    The success code
129  *
130  * An implementation of an async request might find that it can either finish
131  * the request without waiting for an external event, or it can't even start
132  * the engine. To present the illusion of a callback to the user of the API,
133  * the implementation can call this helper function which triggers an
134  * immediate timed event. This way the caller can use the same calling
135  * conventions, independent of whether the request was actually deferred.
136  */
137
138 bool async_post_status(struct async_req *req, NTSTATUS status)
139 {
140         req->status = status;
141
142         if (event_add_timed(req->event_ctx, req, timeval_zero(),
143                             "async_trigger",
144                             async_trigger, req) == NULL) {
145                 return false;
146         }
147         return true;
148 }
149
150 /**
151  * @brief Helper function for nomem check
152  * @param[in] p         The pointer to be checked
153  * @param[in] req       The request being processed
154  *
155  * Convenience helper to easily check alloc failure within a callback
156  * implementing the next step of an async request.
157  *
158  * Call pattern would be
159  * \code
160  * p = talloc(mem_ctx, bla);
161  * if (async_req_nomem(p, req)) {
162  *      return;
163  * }
164  * \endcode
165  */
166
167 bool async_req_nomem(const void *p, struct async_req *req)
168 {
169         if (p != NULL) {
170                 return false;
171         }
172         async_req_error(req, NT_STATUS_NO_MEMORY);
173         return true;
174 }
175
176 bool async_req_is_error(struct async_req *req, NTSTATUS *status)
177 {
178         if (req->state < ASYNC_REQ_DONE) {
179                 *status = NT_STATUS_INTERNAL_ERROR;
180                 return true;
181         }
182         if (req->state == ASYNC_REQ_ERROR) {
183                 *status = req->status;
184                 return true;
185         }
186         return false;
187 }
188
189 NTSTATUS async_req_simple_recv(struct async_req *req)
190 {
191         NTSTATUS status;
192
193         if (async_req_is_error(req, &status)) {
194                 return status;
195         }
196         return NT_STATUS_OK;
197 }