root/lib/async_req/async_req.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. async_req_print
  2. async_req_new
  3. async_req_finish
  4. async_req_done
  5. async_req_error
  6. async_trigger
  7. async_req_nomem
  8. async_post_error
  9. async_req_is_error
  10. async_req_queue_init
  11. async_queue_entry_destructor
  12. async_req_immediate_trigger
  13. async_req_enqueue
  14. _async_req_setup

   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)
     /* [<][>][^][v][top][bottom][index][help] */
  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)
     /* [<][>][^][v][top][bottom][index][help] */
  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)
     /* [<][>][^][v][top][bottom][index][help] */
  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)
     /* [<][>][^][v][top][bottom][index][help] */
  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)
     /* [<][>][^][v][top][bottom][index][help] */
 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,
     /* [<][>][^][v][top][bottom][index][help] */
 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)
     /* [<][>][^][v][top][bottom][index][help] */
 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,
     /* [<][>][^][v][top][bottom][index][help] */
 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,
     /* [<][>][^][v][top][bottom][index][help] */
 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)
     /* [<][>][^][v][top][bottom][index][help] */
 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)
     /* [<][>][^][v][top][bottom][index][help] */
 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,
     /* [<][>][^][v][top][bottom][index][help] */
 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,
     /* [<][>][^][v][top][bottom][index][help] */
 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,
     /* [<][>][^][v][top][bottom][index][help] */
 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 }

/* [<][>][^][v][top][bottom][index][help] */