diff options
-rw-r--r-- | lib/tevent/libtevent.m4 | 2 | ||||
-rw-r--r-- | lib/tevent/tevent.h | 163 | ||||
-rw-r--r-- | lib/tevent/tevent_internal.h | 2 | ||||
-rw-r--r-- | lib/tevent/tevent_req.c | 338 |
4 files changed, 303 insertions, 202 deletions
diff --git a/lib/tevent/libtevent.m4 b/lib/tevent/libtevent.m4 index 02701c254c..cbfb981692 100644 --- a/lib/tevent/libtevent.m4 +++ b/lib/tevent/libtevent.m4 @@ -26,7 +26,7 @@ AC_SUBST(TEVENT_LIBS) TEVENT_CFLAGS="-I$teventdir" -TEVENT_OBJ="tevent.o tevent_fd.o tevent_timed.o tevent_signal.o tevent_debug.o tevent_util.o" +TEVENT_OBJ="tevent.o tevent_fd.o tevent_timed.o tevent_signal.o tevent_req.o tevent_debug.o tevent_util.o" TEVENT_OBJ="$TEVENT_OBJ tevent_standard.o tevent_select.o" AC_CHECK_HEADERS(sys/epoll.h) diff --git a/lib/tevent/tevent.h b/lib/tevent/tevent.h index 92ce8a7ce1..2ab1864d32 100644 --- a/lib/tevent/tevent.h +++ b/lib/tevent/tevent.h @@ -4,6 +4,8 @@ generalised event loop handling Copyright (C) Andrew Tridgell 2005 + Copyright (C) Stefan Metzmacher 2005-2009 + Copyright (C) Volker Lendecke 2008 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -131,6 +133,167 @@ int tevent_set_debug(struct tevent_context *ev, void *context); int tevent_set_debug_stderr(struct tevent_context *ev); +/** + * An async request moves between the following 4 states: + */ +enum tevent_req_state { + /** + * we are creating the request + */ + TEVENT_REQ_INIT, + /** + * we are waiting the request to complete + */ + TEVENT_REQ_IN_PROGRESS, + /** + * the request is finished + */ + TEVENT_REQ_DONE, + /** + * A user error has occured + */ + TEVENT_REQ_USER_ERROR, + /** + * Request timed out + */ + TEVENT_REQ_TIMED_OUT, + /** + * No memory in between + */ + TEVENT_REQ_NO_MEMORY +}; + +/** + * @brief An async request + * + * This represents an async request being processed by callbacks via an event + * context. A user can issue for example a write request to a socket, giving + * an implementation function the fd, the buffer and the number of bytes to + * transfer. The function issuing the request will immediately return without + * blocking most likely without having sent anything. The API user then fills + * in req->async.fn and req->async.private_data, functions that are called + * when the request is finished. + * + * It is up to the user of the async request to talloc_free it after it has + * finished. This can happen while the completion function is called. + */ + +struct tevent_req { + /** + * @brief What to do on completion + * + * This is used for the user of an async request, fn is called when + * the request completes, either successfully or with an error. + */ + struct { + /** + * @brief Completion function + * Completion function, to be filled by the API user + */ + void (*fn)(struct tevent_req *); + /** + * @brief Private data for the completion function + */ + void *private_data; + } async; + + /** + * @brief Private state pointer for the actual implementation + * + * The implementation doing the work for the async request needs a + * current state like for example a fd event. The user of an async + * request should not touch this. + */ + void *private_state; + + /** + * @brief Internal state of the request + * + * Callers should only access this via functions and never directly. + */ + struct { + /** + * @brief The talloc type of the private_state pointer + * + * This is filled by the tevent_req_create() macro. + * + * This for debugging only. + */ + const char *private_type; + + /** + * @brief The location where the request was created + * + * This uses the __location__ macro via the tevent_req_create() + * macro. + * + * This for debugging only. + */ + const char *location; + + /** + * @brief The external state - will be queried by the caller + * + * While the async request is being processed, state will remain in + * TEVENT_REQ_IN_PROGRESS. A request is finished if + * req->state>=TEVENT_REQ_DONE. + */ + enum tevent_req_state state; + + /** + * @brief status code when finished + * + * This status can be queried in the async completion function. It + * will be set to 0 when everything went fine. + */ + uint64_t error; + + /** + * @brief the timer event if tevent_req_post was used + * + */ + struct tevent_timer *trigger; + + /** + * @brief the timer event if tevent_req_set_timeout was used + * + */ + struct tevent_timer *timer; + } internal; +}; + +struct tevent_req *_tevent_req_create(TALLOC_CTX *mem_ctx, + void *pstate, + size_t state_size, + const char *type, + const char *location); + +#define tevent_req_create(_mem_ctx, _pstate, _type) \ + _tevent_req_create((_mem_ctx), (_pstate), sizeof(_type), \ + #_type, __location__) + +bool tevent_req_set_timeout(struct tevent_req *req, + struct tevent_context *ev, + struct timeval endtime); + +void tevent_req_done(struct tevent_req *req); + +bool tevent_req_error(struct tevent_req *req, + uint64_t error); + +bool tevent_req_nomem(const void *p, + struct tevent_req *req); + +struct tevent_req *tevent_req_post(struct tevent_req *req, + struct tevent_context *ev); + +bool tevent_req_is_in_progress(struct tevent_req *req); + +bool tevent_req_is_error(struct tevent_req *req, + enum tevent_req_state *state, + uint64_t *error); + + #ifdef TEVENT_COMPAT_DEFINES #define event_context tevent_context diff --git a/lib/tevent/tevent_internal.h b/lib/tevent/tevent_internal.h index ff7f4951f1..b71a724a7f 100644 --- a/lib/tevent/tevent_internal.h +++ b/lib/tevent/tevent_internal.h @@ -157,7 +157,7 @@ void tevent_common_fd_set_close_fn(struct tevent_fd *fde, uint16_t tevent_common_fd_get_flags(struct tevent_fd *fde); void tevent_common_fd_set_flags(struct tevent_fd *fde, uint16_t flags); -struct timeval ev_timeval_zero(); +struct timeval ev_timeval_zero(void); bool ev_timeval_is_zero(const struct timeval *tv); struct tevent_timer *tevent_common_add_timer(struct tevent_context *ev, TALLOC_CTX *mem_ctx, diff --git a/lib/tevent/tevent_req.c b/lib/tevent/tevent_req.c index db47bd93ed..e0cd0bec0f 100644 --- a/lib/tevent/tevent_req.c +++ b/lib/tevent/tevent_req.c @@ -2,6 +2,7 @@ Unix SMB/CIFS implementation. Infrastructure for async requests Copyright (C) Volker Lendecke 2008 + Copyright (C) Stefan Metzmacher 2009 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,35 +18,32 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include "includes.h" -#include "lib/tevent/tevent.h" -#include "lib/talloc/talloc.h" -#include "lib/util/dlinklist.h" -#include "lib/async_req/async_req.h" - -#ifndef TALLOC_FREE -#define TALLOC_FREE(ctx) do { talloc_free(ctx); ctx=NULL; } while(0) -#endif +#include "replace.h" +#include "tevent.h" +#include "tevent_internal.h" +#include "tevent_util.h" /** - * @brief Print an async_req structure + * @brief Print an tevent_req structure in debug messages * @param[in] mem_ctx The memory context for the result * @param[in] req The request to be printed * @retval Text representation of req * - * This is a default print function for async requests. Implementations should - * override this with more specific information. - * - * This function should not be used by async API users, this is non-static - * only to allow implementations to easily provide default information in - * their specific functions. */ -char *async_req_print(TALLOC_CTX *mem_ctx, struct async_req *req) +char *tevent_req_print(TALLOC_CTX *mem_ctx, struct tevent_req *req) { - return talloc_asprintf(mem_ctx, "async_req: state=%d, error=%d, " - "priv=%s", req->state, (int)req->error, - talloc_get_name(req->private_data)); + return talloc_asprintf(mem_ctx, + "tevent_req[%p/%s]: state[%d] error[%lld (0x%llX)] " + " state[%s (%p)] timer[%p]", + req, req->internal.location, + req->internal.state, + (unsigned long long)req->internal.error, + (unsigned long long)req->internal.error, + talloc_get_name(req->private_state), + req->private_state, + req->internal.timer + ); } /** @@ -57,22 +55,40 @@ char *async_req_print(TALLOC_CTX *mem_ctx, struct async_req *req) * The new async request will be initialized in state ASYNC_REQ_IN_PROGRESS */ -struct async_req *async_req_new(TALLOC_CTX *mem_ctx) +struct tevent_req *_tevent_req_create(TALLOC_CTX *mem_ctx, + void *pstate, + size_t state_size, + const char *type, + const char *location) { - struct async_req *result; + struct tevent_req *req; + void **ppstate = (void **)pstate; + void *state; + + req = talloc_zero(mem_ctx, struct tevent_req); + if (req == NULL) { + return NULL; + } + req->internal.private_type = type; + req->internal.location = location; + req->internal.state = TEVENT_REQ_IN_PROGRESS; - result = talloc_zero(mem_ctx, struct async_req); - if (result == NULL) { + state = talloc_size(req, state_size); + if (state == NULL) { + talloc_free(req); return NULL; } - result->state = ASYNC_REQ_IN_PROGRESS; - result->print = async_req_print; - return result; + talloc_set_name_const(state, type); + + req->private_state = state; + + *ppstate = state; + return req; } -static void async_req_finish(struct async_req *req, enum async_req_state state) +static void tevent_req_finish(struct tevent_req *req, enum tevent_req_state state) { - req->state = state; + req->internal.state = state; if (req->async.fn != NULL) { req->async.fn(req); } @@ -87,9 +103,9 @@ static void async_req_finish(struct async_req *req, enum async_req_state state) * function. */ -void async_req_done(struct async_req *req) +void tevent_req_done(struct tevent_req *req) { - async_req_finish(req, ASYNC_REQ_DONE); + tevent_req_finish(req, TEVENT_REQ_DONE); } /** @@ -97,37 +113,38 @@ void async_req_done(struct async_req *req) * @param[in] req The request with an error * @param[in] error The error code * - * async_req_done is to be used by implementors of async requests. When a + * tevent_req_done is to be used by implementors of async requests. When a * request can not successfully completed, the implementation should call this * function with the appropriate status code. + * + * If error is 0 the function returns false and does nothing more. + * + * Call pattern would be + * \code + * int error = first_function(); + * if (tevent_req_error(req, error)) { + * return; + * } + * + * error = second_function(); + * if (tevent_req_error(req, error)) { + * return; + * } + * + * tevent_req_done(req); + * return; + * \endcode */ -void async_req_error(struct async_req *req, uint64_t error) -{ - req->error = error; - async_req_finish(req, ASYNC_REQ_USER_ERROR); -} - -/** - * @brief Timed event callback - * @param[in] ev Event context - * @param[in] te The timed event - * @param[in] now zero time - * @param[in] priv The async request to be finished - */ - -static void async_trigger(struct tevent_context *ev, struct tevent_timer *te, - struct timeval now, void *priv) +bool tevent_req_error(struct tevent_req *req, uint64_t error) { - struct async_req *req = talloc_get_type_abort(priv, struct async_req); - - TALLOC_FREE(te); - if (req->error == 0) { - async_req_done(req); - } - else { - async_req_error(req, req->error); + if (error == 0) { + return false; } + + req->internal.error = error; + tevent_req_finish(req, TEVENT_REQ_USER_ERROR); + return true; } /** @@ -141,25 +158,48 @@ static void async_trigger(struct tevent_context *ev, struct tevent_timer *te, * Call pattern would be * \code * p = talloc(mem_ctx, bla); - * if (async_req_ntnomem(p, req)) { + * if (tevent_req_nomem(p, req)) { * return; * } * \endcode */ -bool async_req_nomem(const void *p, struct async_req *req) +bool tevent_req_nomem(const void *p, struct tevent_req *req) { if (p != NULL) { return false; } - async_req_finish(req, ASYNC_REQ_NO_MEMORY); + tevent_req_finish(req, TEVENT_REQ_NO_MEMORY); return true; } /** - * @brief Finish a request before it started processing + * @brief Timed event callback + * @param[in] ev Event context + * @param[in] te The timed event + * @param[in] now zero time + * @param[in] priv The async request to be finished + */ +static void tevent_req_trigger(struct tevent_context *ev, + struct tevent_timer *te, + struct timeval zero, + void *private_data) +{ + struct tevent_req *req = talloc_get_type(private_data, + struct tevent_req); + + talloc_free(req->internal.trigger); + req->internal.trigger = NULL; + + tevent_req_finish(req, req->internal.state); +} + +/** + * @brief Finish a request before the caller had the change to set the callback * @param[in] req The finished request - * @param[in] status The success code + * @param[in] ev The tevent_context for the timed event + * @retval On success req will be returned, + * on failure req will be destroyed * * An implementation of an async request might find that it can either finish * the request without waiting for an external event, or it can't even start @@ -169,170 +209,68 @@ bool async_req_nomem(const void *p, struct async_req *req) * conventions, independent of whether the request was actually deferred. */ -bool async_post_error(struct async_req *req, struct tevent_context *ev, - uint64_t error) +struct tevent_req *tevent_req_post(struct tevent_req *req, + struct tevent_context *ev) { - req->error = error; - - if (tevent_add_timer(ev, req, timeval_zero(), - async_trigger, req) == NULL) { - return false; + req->internal.trigger = tevent_add_timer(ev, req, ev_timeval_zero(), + tevent_req_trigger, req); + if (!req->internal.trigger) { + talloc_free(req); + return NULL; } - return true; -} -bool async_req_is_error(struct async_req *req, enum async_req_state *state, - uint64_t *error) -{ - if (req->state == ASYNC_REQ_DONE) { - return false; - } - if (req->state == ASYNC_REQ_USER_ERROR) { - *error = req->error; - } - *state = req->state; - return true; + return req; } -static void async_req_timedout(struct tevent_context *ev, - struct tevent_timer *te, - struct timeval now, - void *priv) +bool tevent_req_is_in_progress(struct tevent_req *req) { - struct async_req *req = talloc_get_type_abort(priv, struct async_req); - TALLOC_FREE(te); - async_req_finish(req, ASYNC_REQ_TIMED_OUT); -} + if (req->internal.state == TEVENT_REQ_IN_PROGRESS) { + return true; + } -bool async_req_set_timeout(struct async_req *req, struct tevent_context *ev, - struct timeval to) -{ - return (tevent_add_timer( - ev, req, timeval_current_ofs(to.tv_sec, to.tv_usec), - async_req_timedout, req) - != NULL); + return false; } -struct async_req *async_wait_send(TALLOC_CTX *mem_ctx, - struct tevent_context *ev, - struct timeval to) +bool tevent_req_is_error(struct tevent_req *req, enum tevent_req_state *state, + uint64_t *error) { - struct async_req *result; - - result = async_req_new(mem_ctx); - if (result == NULL) { - return result; + if (req->internal.state == TEVENT_REQ_DONE) { + return false; } - if (!async_req_set_timeout(result, ev, to)) { - TALLOC_FREE(result); - return NULL; + if (req->internal.state == TEVENT_REQ_USER_ERROR) { + *error = req->internal.error; } - return result; -} - -bool async_wait_recv(struct async_req *req) -{ + *state = req->internal.state; return true; } -struct async_queue_entry { - struct async_queue_entry *prev, *next; - struct async_req_queue *queue; - struct async_req *req; - void (*trigger)(struct async_req *req); -}; - -struct async_req_queue { - struct async_queue_entry *queue; -}; - -struct async_req_queue *async_req_queue_init(TALLOC_CTX *mem_ctx) -{ - return talloc_zero(mem_ctx, struct async_req_queue); -} - -static int async_queue_entry_destructor(struct async_queue_entry *e) +static void tevent_req_timedout(struct tevent_context *ev, + struct tevent_timer *te, + struct timeval now, + void *private_data) { - struct async_req_queue *queue = e->queue; + struct tevent_req *req = talloc_get_type(private_data, + struct tevent_req); - DLIST_REMOVE(queue->queue, e); + talloc_free(req->internal.timer); + req->internal.timer = NULL; - if (queue->queue != NULL) { - queue->queue->trigger(queue->queue->req); - } - - return 0; + tevent_req_finish(req, TEVENT_REQ_TIMED_OUT); } -static void async_req_immediate_trigger(struct tevent_context *ev, - struct tevent_timer *te, - struct timeval now, - void *priv) +bool tevent_req_set_timeout(struct tevent_req *req, + struct tevent_context *ev, + struct timeval endtime) { - struct async_queue_entry *e = talloc_get_type_abort( - priv, struct async_queue_entry); - - TALLOC_FREE(te); - e->trigger(e->req); -} - -bool async_req_enqueue(struct async_req_queue *queue, struct tevent_context *ev, - struct async_req *req, - void (*trigger)(struct async_req *req)) -{ - struct async_queue_entry *e; - bool busy; - - busy = (queue->queue != NULL); + talloc_free(req->internal.timer); - e = talloc(req, struct async_queue_entry); - if (e == NULL) { + req->internal.timer = tevent_add_timer(ev, req, endtime, + tevent_req_timedout, + req); + if (tevent_req_nomem(req->internal.timer, req)) { return false; } - e->req = req; - e->trigger = trigger; - e->queue = queue; - - DLIST_ADD_END(queue->queue, e, struct async_queue_entry *); - talloc_set_destructor(e, async_queue_entry_destructor); - - if (!busy) { - struct tevent_timer *te; - - te = tevent_add_timer(ev, e, timeval_zero(), - async_req_immediate_trigger, - e); - if (te == NULL) { - TALLOC_FREE(e); - return false; - } - } - return true; } -bool _async_req_setup(TALLOC_CTX *mem_ctx, struct async_req **preq, - void *pstate, size_t state_size, const char *typename) -{ - struct async_req *req; - void **ppstate = (void **)pstate; - void *state; - - req = async_req_new(mem_ctx); - if (req == NULL) { - return false; - } - state = talloc_size(req, state_size); - if (state == NULL) { - TALLOC_FREE(req); - return false; - } - talloc_set_name_const(state, typename); - req->private_data = state; - - *preq = req; - *ppstate = state; - - return true; -} |