From: Stefan Metzmacher Date: Sun, 3 May 2009 17:57:35 +0000 (+0200) Subject: lib/util: add tevent_coroutine infrastructure based on Portable Coroutine Library X-Git-Url: http://git.samba.org/?p=metze%2Fsamba%2Fwip.git;a=commitdiff_plain;h=f6c3ea8784156e3697c3930fe46cb2c49aba37d2 lib/util: add tevent_coroutine infrastructure based on Portable Coroutine Library metze --- diff --git a/lib/util/config.mk b/lib/util/config.mk index 1b620d1464c6..b27b70541cfb 100644 --- a/lib/util/config.mk +++ b/lib/util/config.mk @@ -71,10 +71,12 @@ UTIL_TDB_OBJ_FILES = $(libutilsrcdir)/util_tdb.o [SUBSYSTEM::UTIL_TEVENT] PUBLIC_DEPENDENCIES = LIBTEVENT +PRIVATE_DEPENDENCIES = LIBPCL UTIL_TEVENT_OBJ_FILES = $(addprefix $(libutilsrcdir)/, \ tevent_unix.o \ - tevent_ntstatus.o) + tevent_ntstatus.o \ + tevent_coroutine.o) [SUBSYSTEM::UTIL_LDB] PUBLIC_DEPENDENCIES = LIBLDB diff --git a/lib/util/tevent.m4 b/lib/util/tevent.m4 new file mode 100644 index 000000000000..bc2f4d39e1bb --- /dev/null +++ b/lib/util/tevent.m4 @@ -0,0 +1,9 @@ + +# check for the Portable Coroutine Library +AC_CHECK_HEADERS(pcl.h) +AC_CHECK_LIB_EXT(pcl, PCL_LIBS, co_create) +if test x"$ac_cv_header_pcl_h" = x"yes" -a x"$ac_cv_lib_ext_pcl_co_create" = x"yes"; then + SMB_ENABLE(LIBPCL,YES) + SMB_EXT_LIB(LIBPCL, $PCL_LIBS) +fi + diff --git a/lib/util/tevent_coroutine.c b/lib/util/tevent_coroutine.c new file mode 100644 index 000000000000..ff9bca0072c2 --- /dev/null +++ b/lib/util/tevent_coroutine.c @@ -0,0 +1,261 @@ +/* + Unix SMB/CIFS implementation. + + Coroutine abstraction for tevent. + + 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 + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "replace.h" +#include +#include "tevent_coroutine.h" +#include + +/* + +typedef void *coroutine_t; + + +coroutine_t co_create(void (*func)(void *), void *data, void *stack, int size); +void co_delete(coroutine_t coro); +void co_call(coroutine_t coro); +void co_resume(void); +void co_exit_to(coroutine_t coro); +void co_exit(void); +coroutine_t co_current(void); +*/ + +struct tevent_coroutine; + +struct tevent_coroutine_result { + enum { + TEVENT_COROUTINE_INIT = 0, + TEVENT_COROUTINE_IN_PROGRESS, + TEVENT_COROUTINE_YIELD, + TEVENT_COROUTINE_DONE, + TEVENT_COROUTINE_NO_MEMORY, + TEVENT_COROUTINE_USER_ERROR + } state; + uint64_t error; + const char *location; + const char *return_location; +}; + +struct tevent_coroutine { + struct tevent_req *req; + struct tevent_context *ev; + const char *create_location; + struct tevent_immediate *trigger; + tevent_coroutine_fn_t fn; + struct tevent_coroutine_result result; + coroutine_t coro; + uint8_t stack[4196]; +}; + +static void tevent_coroutine_body(void *data); + +static void tevent_couroutine_start_callback(struct tevent_context *ctx, + struct tevent_immediate *im, + void *private_data); + +struct tevent_coroutine *_tevent_coroutine_create( + struct tevent_req *req, + struct tevent_context *ev, + tevent_coroutine_fn_t fn, + const char *location) +{ + void *state = _tevent_req_data(req); + struct tevent_coroutine *tco; + + tco = talloc(state, struct tevent_coroutine); + if (tco == NULL) { + return NULL; + } + + tco->req = req; + tco->ev = ev; + tco->create_location = location; + tco->trigger = tevent_create_immediate(tco); + if (tco->trigger == NULL) { + talloc_free(tco); + return NULL; + } + tco->fn = fn; + ZERO_STRUCT(tco->result); + tco->coro = co_create(tevent_coroutine_body, tco, + tco->stack, sizeof(tco->stack)); + if (tco->coro == NULL) { + talloc_free(tco); + return NULL; + } + + tevent_schedule_immediate(tco->trigger, + tevent_coroutine_start_callback, + tco); + return tco; +} + +static void tevent_coroutine_body(void *data) +{ + struct tevent_coroutine *tco = talloc_get_type_abort(data, + struct tevent_coroutine); + void *fn_state = _tevent_req_data(tco->req); + struct tevent_coroutine_result *res; + + /* now call the real implementation */ + res = tco->fn(tco, tco->ev, fn_state); + + /* + * now destroy the coroutine_t handle and jump back to + * tevent_coroutine_run() after co_call(). + */ + tco->coro = NULL; + co_exit(); +} + +static void tevent_coroutine_run(struct tevent_coroutine *tco) +{ + coroutine_t cur = co_current(); + if (tco->coro == cur) { + /* this should never happen */ + abort(); + } + + tco->result.state = TEVENT_COROUTINE_IN_PROGRESS; + /* + * now start or continue the given coroutine + * on its own stack. co_call() returns when + * the coroutine has called co_resume() (via tevent_coroutine_yield()) + * or co_exit() (via tevent_coroutine_return() + */ + co_call(tco->coro); + /* now we're back on the original stack */ + + switch (tco->result.state) { + case TEVENT_COROUTINE_INIT: + case TEVENT_COROUTINE_IN_PROGRESS: + /* this should never be reached */ + abort(); + + case TEVENT_COROUTINE_YIELD: + /* we'll resume later */ + return; + + case TEVENT_COROUTINE_DONE: + _tevent_req_done(tco->req, tco->result.location); + return; + + case TEVENT_COROUTINE_NO_MEMORY: + _tevent_req_nomem(NULL, tco->req, tco->result.location); + return; + + case TEVENT_COROUTINE_USER_ERROR: + _tevent_req_error(tco->req, tco->result.error, + tco->result.location); + return; + } + + /* this should never be reached */ + abort(); +} + +static void tevent_couroutine_start_callback(struct tevent_context *ctx, + struct tevent_immediate *im, + void *private_data) +{ + struct tevent_coroutine *tco = talloc_get_type_abort(private_data, + struct tevent_coroutine); + tevent_coroutine_run(tco); +} + +static void tevent_coroutine_yield_callback(struct tevent_req *subreq); + +bool _tevent_coroutine_yield(struct tevent_coroutine *tco, + struct tevent_req *subreq, + const char *location) +{ + bool ok; + + ok = _tevent_coroutine_nomem(subreq, tco, location); + if (!ok) { + return false; + } + + tco->result.state = TEVENT_COROUTINE_YIELD; + tco->result.location = location; + + tevent_req_set_callback(subreq, + tevent_coroutine_yield_callback, + tco); + + /* + * this jumps back to tevent_coroutine_run() after + * co_call(). + */ + co_resume(); + + return true; +} + +static void tevent_coroutine_yield_callback(struct tevent_req *subreq) +{ + struct tevent_coroutine *tco = tevent_req_callback_data(subreq, + struct tevent_coroutine); + tevent_coroutine_run(tco); +} + +void _tevent_coroutine_done(struct tevent_coroutine *tco, + const char *location) +{ + tco->result.state = TEVENT_COROUTINE_DONE; + tco->result.location = location; +} + +bool _tevent_coroutine_nomem(const void *ptr, + struct tevent_coroutine *tco, + const char *location) +{ + if (ptr != NULL) { + return false; + } + + tco->result.state = TEVENT_COROUTINE_NO_MEMORY; + tco->result.location = location; + return true; +} + +bool _tevent_coroutine_error(struct tevent_coroutine *tco, + uint64_t error, + const char *location) +{ + if (error == 0) { + return false; + } + + tco->result.state = TEVENT_COROUTINE_USER_ERROR; + tco->result.error = error; + tco->result.location = location; + return true; +} + +struct tevent_coroutine_result *_tevent_coroutine_return( + struct tevent_coroutine *tco, + const char *location) +{ + tco->result.return_location = location; + return &tco->result; +} + diff --git a/lib/util/tevent_coroutine.h b/lib/util/tevent_coroutine.h new file mode 100644 index 000000000000..e9be2eebd30f --- /dev/null +++ b/lib/util/tevent_coroutine.h @@ -0,0 +1,71 @@ +/* + Unix SMB/CIFS implementation. + + Coroutine abstraction for tevent. + + 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 + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef _TEVENT_COROUTINE_H_ +#define _TEVENT_COROUTINE_H_ + +struct tevent_coroutine; +struct tevent_coroutine_result; + +typedef struct tevent_coroutine_result *(*tevent_coroutine_fn_t)( + struct tevent_coroutine *co, + struct tevent_context *ev, + void *private_data); + +struct tevent_coroutine *_tevent_coroutine_create( + struct tevent_req *req, + struct tevent_context *ev, + tevent_coroutine_fn_t fn, + const char *location); +#define tevent_coroutine_create(req, ev, fn) \ + _tevent_coroutine_create(req, ev, fn, __location__) + +bool _tevent_coroutine_yield(struct tevent_coroutine *tco, + struct tevent_req *subreq, + const char *location); +#define tevent_coroutine_yield(tco, subreq) \ + _tevent_coroutine_yield(tco, subreq, __location__) + +void _tevent_coroutine_done(struct tevent_coroutine *tco, + const char *location); +#define tevent_coroutine_done(tco) \ + _tevent_coroutine_done(tco, __location__) \ + +bool _tevent_coroutine_nomem(const void *ptr, + struct tevent_coroutine *tco, + const char *location); +#define tevent_coroutine_nomem(ptr, tco) \ + _tevent_coroutine_nomem(ptr, tco, __location__) + +bool _tevent_coroutine_error(struct tevent_coroutine *tco, + uint64_t error, + const char *location); +#define tevent_coroutine_error(tco, error) \ + _tevent_coroutine_error(tco, error, __location__) + +struct tevent_coroutine_result *_tevent_coroutine_return( + struct tevent_coroutine *tco, + const char *location); +#define tevent_coroutine_return(tco) \ + _tevent_coroutine_return(tco, __location__) + +#endif /* _TEVENT_COROUTINE_H_ */ + diff --git a/source4/configure.ac b/source4/configure.ac index 3c23f27d6879..e6c07623d936 100644 --- a/source4/configure.ac +++ b/source4/configure.ac @@ -21,6 +21,7 @@ m4_include(../lib/util/fsusage.m4) m4_include(../lib/util/xattr.m4) m4_include(../lib/util/capability.m4) m4_include(../lib/util/time.m4) +m4_include(../lib/util/tevent.m4) m4_include(../lib/popt/samba.m4) m4_include(../lib/util/charset/config.m4) m4_include(lib/socket/config.m4)