lib/util: add tevent_coroutine infrastructure based on Portable Coroutine Library
authorStefan Metzmacher <metze@samba.org>
Sun, 3 May 2009 17:57:35 +0000 (19:57 +0200)
committerStefan Metzmacher <metze@samba.org>
Fri, 15 May 2009 11:11:57 +0000 (13:11 +0200)
metze

lib/util/config.mk
lib/util/tevent.m4 [new file with mode: 0644]
lib/util/tevent_coroutine.c [new file with mode: 0644]
lib/util/tevent_coroutine.h [new file with mode: 0644]
source4/configure.ac

index 1b620d1464c6a0018c8fc333e9c1ef89e639b8ba..b27b70541cfb116584c7e094d0980d5aaeb57919 100644 (file)
@@ -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 (file)
index 0000000..bc2f4d3
--- /dev/null
@@ -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 (file)
index 0000000..ff9bca0
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "replace.h"
+#include <tevent.h>
+#include "tevent_coroutine.h"
+#include <pcl.h>
+
+/*
+
+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 (file)
index 0000000..e9be2ee
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+*/
+
+#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_ */
+
index 3c23f27d6879a2c63ce20b8207f7f9a4b2d8b522..e6c07623d93605feda6a3b25c938462b18206a78 100644 (file)
@@ -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)