From: Rob Hoes <rob.hoes@citrix.com>
To: xen-devel@lists.xen.org
Cc: ian.jackson@eu.citrix.com, ian.campbell@citrix.com,
	Rob Hoes <rob.hoes@citrix.com>
Subject: [PATCH v3 3/3] libxl: ocaml: use 'for_app_registration' in osevent callbacks
Date: Wed, 8 Jan 2014 16:17:43 +0000	[thread overview]
Message-ID: <1389197863-30692-1-git-send-email-rob.hoes@citrix.com> (raw)
In-Reply-To: <1386955247-22437-1-git-send-email-rob.hoes@citrix.com>
This allows the application to pass a token to libxl in the fd/timeout
registration callbacks, which it receives back in modification or
deregistration callbacks.
It turns out that this is essential for timeout handling, in order to
identify which timeout to change on a modify event.
Signed-off-by: Rob Hoes <rob.hoes@citrix.com>
Acked-by: David Scott <dave.scott@eu.citrix.com>
---
v2:
* assert if for_app == NULL
* catch any exceptions from callbacks
* use goto-style error handling ;)
v3:
* for timeouts, cleanup for_app in occurred_timeout (not in
  timeout_deregister)
* improve comments
* abort in fd_deregister when the app raises an exception
---
 tools/ocaml/libs/xl/xenlight.mli.in  |   10 +--
 tools/ocaml/libs/xl/xenlight_stubs.c |  146 +++++++++++++++++++++++++++++-----
 2 files changed, 133 insertions(+), 23 deletions(-)
diff --git a/tools/ocaml/libs/xl/xenlight.mli.in b/tools/ocaml/libs/xl/xenlight.mli.in
index b9819e1..277e81d 100644
--- a/tools/ocaml/libs/xl/xenlight.mli.in
+++ b/tools/ocaml/libs/xl/xenlight.mli.in
@@ -68,11 +68,11 @@ module Async : sig
 
 	val osevent_register_hooks : ctx ->
 		user:'a ->
-		fd_register:('a -> Unix.file_descr -> event list -> for_libxl -> unit) ->
-		fd_modify:('a -> Unix.file_descr -> event list -> unit) ->
-		fd_deregister:('a -> Unix.file_descr -> unit) ->
-		timeout_register:('a -> int64 -> int64 -> for_libxl -> unit) ->
-		timeout_modify:('a -> unit) ->
+		fd_register:('a -> Unix.file_descr -> event list -> for_libxl -> 'b) ->
+		fd_modify:('a -> Unix.file_descr -> 'b -> event list -> 'b) ->
+		fd_deregister:('a -> Unix.file_descr -> 'b -> unit) ->
+		timeout_register:('a -> int64 -> int64 -> for_libxl -> 'c) ->
+		timeout_modify:('a -> 'c -> 'c) ->
 		osevent_hooks
 
 	external osevent_occurred_fd : ctx -> for_libxl -> Unix.file_descr -> event list -> event list -> unit = "stub_libxl_osevent_occurred_fd"
diff --git a/tools/ocaml/libs/xl/xenlight_stubs.c b/tools/ocaml/libs/xl/xenlight_stubs.c
index 2e2606a..50cd223 100644
--- a/tools/ocaml/libs/xl/xenlight_stubs.c
+++ b/tools/ocaml/libs/xl/xenlight_stubs.c
@@ -31,6 +31,7 @@
 #include <libxl_utils.h>
 
 #include <unistd.h>
+#include <assert.h>
 
 #include "caml_xentoollog.h"
 
@@ -1211,14 +1212,20 @@ value Val_poll_events(short events)
 	CAMLreturn(event_list);
 }
 
+/* The process for dealing with the for_app_registration_  values in the
+ * callbacks below (GC registrations etc) is similar to the way for_callback is
+ * handled in the asynchronous operations above. */
+
 int fd_register(void *user, int fd, void **for_app_registration_out,
                      short events, void *for_libxl)
 {
 	caml_leave_blocking_section();
 	CAMLparam0();
 	CAMLlocalN(args, 4);
+	int ret = 0;
 	static value *func = NULL;
 	value *p = (value *) user;
+	value *for_app;
 
 	if (func == NULL) {
 		/* First time around, lookup by name */
@@ -1230,10 +1237,25 @@ int fd_register(void *user, int fd, void **for_app_registration_out,
 	args[2] = Val_poll_events(events);
 	args[3] = (value) for_libxl;
 
-	caml_callbackN(*func, 4, args);
+	for_app = malloc(sizeof(value));
+	if (!for_app) {
+		ret = ERROR_OSEVENT_REG_FAIL;
+		goto err;
+	}
+
+	*for_app = caml_callbackN_exn(*func, 4, args);
+	if (Is_exception_result(*for_app)) {
+		ret = ERROR_OSEVENT_REG_FAIL;
+		goto err;
+	}
+
+	caml_register_global_root(for_app);
+	*for_app_registration_out = for_app;
+
+err:
 	CAMLdone;
 	caml_enter_blocking_section();
-	return 0;
+	return ret;
 }
 
 int fd_modify(void *user, int fd, void **for_app_registration_update,
@@ -1241,9 +1263,14 @@ int fd_modify(void *user, int fd, void **for_app_registration_update,
 {
 	caml_leave_blocking_section();
 	CAMLparam0();
-	CAMLlocalN(args, 3);
+	CAMLlocalN(args, 4);
+	int ret = 0;
 	static value *func = NULL;
 	value *p = (value *) user;
+	value *for_app = *for_app_registration_update;
+
+	/* If for_app == NULL, then something is very wrong */
+	assert(for_app);
 
 	if (func == NULL) {
 		/* First time around, lookup by name */
@@ -1252,21 +1279,37 @@ int fd_modify(void *user, int fd, void **for_app_registration_update,
 
 	args[0] = *p;
 	args[1] = Val_int(fd);
-	args[2] = Val_poll_events(events);
+	args[2] = *for_app;
+	args[3] = Val_poll_events(events);
+
+	*for_app = caml_callbackN_exn(*func, 4, args);
+	if (Is_exception_result(*for_app)) {
+		/* If an exception is caught, *for_app_registration_update is not
+		 * changed. It remains a valid pointer to a value that is registered
+		 * with the GC. */
+		ret = ERROR_OSEVENT_REG_FAIL;
+		goto err;
+	}
+
+	*for_app_registration_update = for_app;
 
-	caml_callbackN(*func, 3, args);
+err:
 	CAMLdone;
 	caml_enter_blocking_section();
-	return 0;
+	return ret;
 }
 
 void fd_deregister(void *user, int fd, void *for_app_registration)
 {
 	caml_leave_blocking_section();
 	CAMLparam0();
-	CAMLlocalN(args, 2);
+	CAMLlocalN(args, 3);
 	static value *func = NULL;
 	value *p = (value *) user;
+	value *for_app = for_app_registration;
+
+	/* If for_app == NULL, then something is very wrong */
+	assert(for_app);
 
 	if (func == NULL) {
 		/* First time around, lookup by name */
@@ -1275,12 +1318,26 @@ void fd_deregister(void *user, int fd, void *for_app_registration)
 
 	args[0] = *p;
 	args[1] = Val_int(fd);
+	args[2] = *for_app;
+
+	caml_callbackN_exn(*func, 3, args);
+	/* This hook does not return error codes, so the best thing we can do
+	 * to avoid trouble, if we catch an exception from the app, is abort. */
+	if (Is_exception_result(*for_app))
+		abort();
+
+	caml_remove_global_root(for_app);
+	free(for_app);
 
-	caml_callbackN(*func, 2, args);
 	CAMLdone;
 	caml_enter_blocking_section();
 }
 
+struct for_libxl_timeout {
+	void *for_libxl;
+	value *for_app;
+};
+
 int timeout_register(void *user, void **for_app_registration_out,
                           struct timeval abs, void *for_libxl)
 {
@@ -1288,8 +1345,10 @@ int timeout_register(void *user, void **for_app_registration_out,
 	CAMLparam0();
 	CAMLlocal2(sec, usec);
 	CAMLlocalN(args, 4);
+	int ret = 0;
 	static value *func = NULL;
 	value *p = (value *) user;
+	struct for_libxl_timeout *handles;
 
 	if (func == NULL) {
 		/* First time around, lookup by name */
@@ -1299,15 +1358,41 @@ int timeout_register(void *user, void **for_app_registration_out,
 	sec = caml_copy_int64(abs.tv_sec);
 	usec = caml_copy_int64(abs.tv_usec);
 
+	/* This struct of "handles" will contain "for_libxl" as well as "for_app".
+	 * We'll give a pointer to the struct to the app, and get it back in
+	 * occurred_timeout, where we can clean it all up. */
+	handles = malloc(sizeof(*handles));
+	if (!handles) {
+		ret = ERROR_OSEVENT_REG_FAIL;
+		goto err;
+	}
+
+	handles->for_libxl = for_libxl;
+
 	args[0] = *p;
 	args[1] = sec;
 	args[2] = usec;
-	args[3] = (value) for_libxl;
+	args[3] = (value) handles;
 
-	caml_callbackN(*func, 4, args);
+	handles->for_app = malloc(sizeof(value));
+	if (!handles->for_app) {
+		ret = ERROR_OSEVENT_REG_FAIL;
+		goto err;
+	}
+
+	*(handles->for_app) = caml_callbackN_exn(*func, 4, args);
+	if (Is_exception_result(*(handles->for_app))) {
+		ret = ERROR_OSEVENT_REG_FAIL;
+		goto err;
+	}
+
+	caml_register_global_root(handles->for_app);
+	*for_app_registration_out = handles->for_app;
+
+err:
 	CAMLdone;
 	caml_enter_blocking_section();
-	return 0;
+	return ret;
 }
 
 int timeout_modify(void *user, void **for_app_registration_update,
@@ -1315,25 +1400,45 @@ int timeout_modify(void *user, void **for_app_registration_update,
 {
 	caml_leave_blocking_section();
 	CAMLparam0();
+	CAMLlocalN(args, 2);
+	int ret = 0;
 	static value *func = NULL;
 	value *p = (value *) user;
+	value *for_app = *for_app_registration_update;
+
+	/* If for_app == NULL, then something is very wrong */
+	assert(for_app);
 
 	if (func == NULL) {
 		/* First time around, lookup by name */
 		func = caml_named_value("libxl_timeout_modify");
 	}
 
-	caml_callback(*func, *p);
+	args[0] = *p;
+	args[1] = *for_app;
+
+	*for_app = caml_callbackN_exn(*func, 2, args);
+
+	if (Is_exception_result(*for_app)) {
+		/* If an exception is caught, *for_app_registration_update is not
+		 * changed. It remains a valid pointer to a value that is registered
+		 * with the GC. */
+		ret = ERROR_OSEVENT_REG_FAIL;
+		goto err;
+	}
+
+	*for_app_registration_update = for_app;
+
+err:
 	CAMLdone;
 	caml_enter_blocking_section();
-	return 0;
+	return ret;
 }
 
 void timeout_deregister(void *user, void *for_app_registration)
 {
-	caml_leave_blocking_section();
-	failwith_xl(ERROR_FAIL, "timeout_deregister not yet implemented");
-	caml_enter_blocking_section();
+	/* This hook will never be called by libxl. */
+	abort();
 }
 
 value stub_libxl_osevent_register_hooks(value ctx, value user)
@@ -1386,12 +1491,17 @@ value stub_libxl_osevent_occurred_fd(value ctx, value for_libxl, value fd,
 
 value stub_libxl_osevent_occurred_timeout(value ctx, value for_libxl)
 {
-	CAMLparam2(ctx, for_libxl);
+	CAMLparam1(ctx);
+	struct for_libxl_timeout *handles = (struct for_libxl_timeout *) for_libxl;
 
 	caml_enter_blocking_section();
-	libxl_osevent_occurred_timeout(CTX, (void *) for_libxl);
+	libxl_osevent_occurred_timeout(CTX, (void *) handles->for_libxl);
 	caml_leave_blocking_section();
 
+	caml_remove_global_root(handles->for_app);
+	free(handles->for_app);
+	free(handles);
+
 	CAMLreturn(Val_unit);
 }
 
-- 
1.7.10.4
next prev parent reply	other threads:[~2014-01-08 16:17 UTC|newest]
Thread overview: 53+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2013-12-12 16:36 libxl: ocaml: fix tests makefile and osevent callbacks Rob Hoes
2013-12-12 16:36 ` [PATCH 1/3] ocaml: do not install test binaries Rob Hoes
2013-12-12 16:43   ` Ian Jackson
2013-12-12 17:22     ` Ian Campbell
2013-12-12 16:36 ` [PATCH 2/3] libxl: ocaml: use int64 for timeval fields in the timeout_register callback Rob Hoes
2013-12-12 16:43   ` Ian Jackson
2013-12-12 16:48     ` Rob Hoes
2013-12-12 16:51       ` Ian Jackson
2013-12-12 17:03         ` Rob Hoes
2013-12-12 17:13           ` Ian Jackson
2013-12-12 17:25             ` Rob Hoes
2013-12-12 17:43               ` Ian Jackson
2013-12-12 18:32                 ` Ian Jackson
2013-12-16 16:38                   ` Ian Campbell
2013-12-16 17:06                     ` Ian Jackson
2013-12-20 15:28                       ` David Scott
2013-12-12 17:20     ` Ian Campbell
2014-01-07 17:41   ` Ian Jackson
2014-01-08 14:35     ` Ian Campbell
2013-12-12 16:36 ` [PATCH 3/3] libxl: ocaml: use 'for_app_registration' in osevent callbacks Rob Hoes
2013-12-12 17:40   ` Ian Campbell
2013-12-12 17:53     ` Ian Jackson
2013-12-12 17:54     ` Rob Hoes
2013-12-12 19:18       ` Ian Campbell
2013-12-12 21:27         ` Rob Hoes
2013-12-13  8:07           ` Ian Campbell
2013-12-12 21:40     ` Rob Hoes
2013-12-13 11:44       ` Ian Jackson
2013-12-13 17:20   ` [PATCH v2 " Rob Hoes
2013-12-13 17:24     ` Ian Jackson
2013-12-13 18:04       ` Rob Hoes
2013-12-13 18:21         ` Ian Jackson
2013-12-13 18:48           ` Rob Hoes
2013-12-16 12:02             ` Ian Jackson
2013-12-20 15:29               ` David Scott
2013-12-16 11:35         ` Ian Campbell
2013-12-16 11:37           ` Ian Campbell
2014-01-07 15:11     ` Ian Jackson
2014-01-07 17:25       ` Rob Hoes
2014-01-07 17:39         ` Ian Jackson
2014-01-07 18:05           ` Rob Hoes
2014-01-07 18:18             ` Ian Jackson
2014-01-08 16:17     ` Rob Hoes [this message]
2014-01-08 17:06       ` [PATCH v3 " Ian Jackson
2014-01-09 14:25         ` Rob Hoes
2014-01-09 17:36       ` [PATCH v4 " Rob Hoes
2014-01-09 18:17         ` Ian Jackson
2014-01-10 13:44           ` Rob Hoes
2014-01-10 13:52         ` [PATCH v5 " Rob Hoes
2014-01-10 13:55           ` Ian Jackson
2014-01-07 12:26 ` libxl: ocaml: fix tests makefile and " Ian Campbell
2014-01-07 14:13   ` Rob Hoes
2014-01-07 14:18     ` Ian Campbell
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox
  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):
  git send-email \
    --in-reply-to=1389197863-30692-1-git-send-email-rob.hoes@citrix.com \
    --to=rob.hoes@citrix.com \
    --cc=ian.campbell@citrix.com \
    --cc=ian.jackson@eu.citrix.com \
    --cc=xen-devel@lists.xen.org \
    /path/to/YOUR_REPLY
  https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
  Be sure your reply has a Subject: header at the top and a blank line
  before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).