Richard W.M. Jones
2015-Oct-06 15:05 UTC
[Libguestfs] [PATCH 0/4] ocaml: Allow Guestfs.t handle to be garbage collected.
Allow Guestfs.t handle to be garbage collected, and add a regression test.
Richard W.M. Jones
2015-Oct-06 15:05 UTC
[Libguestfs] [PATCH 1/4] ocaml: Use generational global roots.
These are considerably more efficient than ordinary global roots, but
with the caveat that the program is not allowed to modify them without
calling a special function. We don't modify them, so this change is
safe.
This requires OCaml >= 3.11, but we have that on RHEL 6
(since we dropped support for RHEL 5).
See also:
http://caml.inria.fr/pub/ml-archives/caml-list/2008/03/c3bf86990088236ceeb9a0f0f4c35390.en.html
---
README | 2 +-
ocaml/guestfs-c.c | 16 +++++-----------
2 files changed, 6 insertions(+), 12 deletions(-)
diff --git a/README b/README
index 8c74901..19a1fb2 100644
--- a/README
+++ b/README
@@ -84,7 +84,7 @@ The full requirements are described below.
+--------------+-------------+---+-----------------------------------------+
| Pod::Simple | | R | Part of Perl core. |
+--------------+-------------+---+-----------------------------------------+
-| OCaml | |R/O| Required if compiling from git. |
+| OCaml | 3.11 |R/O| Required if compiling from git. |
| | | | Optional if compiling from tarball. |
| | | | To build generated files and OCaml bindings.
+--------------+-------------+---+-----------------------------------------+
diff --git a/ocaml/guestfs-c.c b/ocaml/guestfs-c.c
index cbff846..1ee5ba7 100644
--- a/ocaml/guestfs-c.c
+++ b/ocaml/guestfs-c.c
@@ -88,12 +88,12 @@ guestfs_finalize (value gv)
/* Now unregister the global roots. */
for (i = 0; i < len; ++i) {
- caml_remove_global_root (roots[i]);
+ caml_remove_generational_global_root (roots[i]);
free (roots[i]);
}
free (roots);
- caml_remove_global_root (v);
+ caml_remove_generational_global_root (v);
free (v);
}
}
@@ -179,10 +179,7 @@ ocaml_guestfs_create (value environmentv, value
close_on_exitv, value unitv)
*/
v = guestfs_int_safe_malloc (g, sizeof *v);
*v = gv;
- /* XXX This global root is generational, but we cannot rely on every
- * user having the OCaml 3.11 version which supports this.
- */
- caml_register_global_root (v);
+ caml_register_generational_global_root (v);
guestfs_set_private (g, "_ocaml_g", v);
CAMLreturn (gv);
@@ -255,10 +252,7 @@ ocaml_guestfs_set_event_callback (value gv, value closure,
value events)
ocaml_guestfs_raise_error (g, "set_event_callback");
}
- /* XXX This global root is generational, but we cannot rely on every
- * user having the OCaml 3.11 version which supports this.
- */
- caml_register_global_root (root);
+ caml_register_generational_global_root (root);
snprintf (key, sizeof key, "_ocaml_event_%d", eh);
guestfs_set_private (g, key, root);
@@ -280,7 +274,7 @@ ocaml_guestfs_delete_event_callback (value gv, value ehv)
value *root = guestfs_get_private (g, key);
if (root) {
- caml_remove_global_root (root);
+ caml_remove_generational_global_root (root);
free (root);
guestfs_set_private (g, key, NULL);
guestfs_delete_event_callback (g, eh);
--
2.5.0
Richard W.M. Jones
2015-Oct-06 15:05 UTC
[Libguestfs] [PATCH 2/4] ocaml: Allow Guestfs.t handle to be garbage collected.
** NB: This is an API break for OCaml programs using Guestfs.event_callback. **
Because of the way I implemented Guestfs.event_callback which had the
Guestfs.t handle as the first parameter, we had to store the (OCaml)
Guestfs.t handle in the C handle's private data area. To do that, we
had to create a global root pointing to the handle.
This of course meant that the handle could not be garbage collected
(thanks Roman Kagan for spotting this).
This changes the API of Guestfs.event_callback so that a handle is no
longer passed. The OCaml handle can now be garbage collected again.
For programs that need the Guestfs.t handle in the callback function
(which turns out to be *none* of the OCaml programs we have written),
you can do:
g#set_event_callback (callback_fn g) [Guestfs.EVENT_FOO];
But since the closure passed to Guestfs.set_event_callback is still
(unavoidably) registered as a global root, that will trap a reference
to the handle, so the handle won't be able to be garbage collected
until you delete the callback.
---
generator/ocaml.ml | 10 +++-------
mllib/progress.ml | 4 ++--
ocaml/guestfs-c.c | 24 +++---------------------
ocaml/t/guestfs_410_close_event.ml | 2 +-
ocaml/t/guestfs_420_log_messages.ml | 2 +-
ocaml/t/guestfs_430_progress_messages.ml | 2 +-
6 files changed, 11 insertions(+), 33 deletions(-)
diff --git a/generator/ocaml.ml b/generator/ocaml.ml
index 8b4e1aa..5d92fcb 100644
--- a/generator/ocaml.ml
+++ b/generator/ocaml.ml
@@ -107,8 +107,7 @@ val event_all : event list
type event_handle
(** The opaque event handle which can be used to delete event callbacks. *)
-type event_callback - t -> event -> event_handle -> string ->
int64 array -> unit
+type event_callback = event -> event_handle -> string -> int64 array
-> unit
(** The event callback. *)
val set_event_callback : t -> event_callback -> event list ->
event_handle
@@ -117,9 +116,7 @@ val set_event_callback : t -> event_callback -> event
list -> event_handle
Note that if the closure captures a reference to the handle,
this reference will prevent the handle from being
- automatically closed by the garbage collector. Since the
- handle is passed to the event callback, with careful programming
- it should be possible to avoid capturing the handle in the closure. *)
+ automatically closed by the garbage collector. *)
val delete_event_callback : t -> event_handle -> unit
(** [delete_event_callback g eh] removes a previously registered
@@ -321,8 +318,7 @@ let event_all = [
type event_handle = int
-type event_callback - t -> event -> event_handle -> string ->
int64 array -> unit
+type event_callback = event -> event_handle -> string -> int64 array
-> unit
external set_event_callback : t -> event_callback -> event list ->
event_handle
= \"ocaml_guestfs_set_event_callback\"
diff --git a/mllib/progress.ml b/mllib/progress.ml
index 8cf5875..b6b3b60 100644
--- a/mllib/progress.ml
+++ b/mllib/progress.ml
@@ -38,13 +38,13 @@ let set_up_progress_bar ?(machine_readable = false) (g :
Guestfs.guestfs) let bar = progress_bar_init ~machine_readable in
(* Reset the progress bar before every libguestfs function. *)
- let enter_callback g event evh buf array + let enter_callback event evh
buf array if event = G.EVENT_ENTER then
progress_bar_reset bar
in
(* A progress event: move the progress bar. *)
- let progress_callback g event evh buf array + let progress_callback
event evh buf array if event = G.EVENT_PROGRESS && Array.length
array >= 4 then (
let position = array.(2)
and total = array.(3) in
diff --git a/ocaml/guestfs-c.c b/ocaml/guestfs-c.c
index 1ee5ba7..08998af 100644
--- a/ocaml/guestfs-c.c
+++ b/ocaml/guestfs-c.c
@@ -78,8 +78,6 @@ guestfs_finalize (value gv)
size_t len, i;
value **roots = get_all_event_callbacks (g, &len);
- value *v = guestfs_get_private (g, "_ocaml_g");
-
/* Close the handle: this could invoke callbacks from the list
* above, which is why we don't want to delete them before
* closing the handle.
@@ -92,9 +90,6 @@ guestfs_finalize (value gv)
free (roots[i]);
}
free (roots);
-
- caml_remove_generational_global_root (v);
- free (v);
}
}
@@ -156,7 +151,6 @@ ocaml_guestfs_create (value environmentv, value
close_on_exitv, value unitv)
CAMLlocal1 (gv);
unsigned flags = 0;
guestfs_h *g;
- value *v;
if (environmentv != Val_int (0) &&
!Bool_val (Field (environmentv, 0)))
@@ -174,14 +168,6 @@ ocaml_guestfs_create (value environmentv, value
close_on_exitv, value unitv)
gv = Val_guestfs (g);
- /* Store the OCaml handle into the C handle. This is only so we can
- * map the C handle to the OCaml handle in event_callback_wrapper.
- */
- v = guestfs_int_safe_malloc (g, sizeof *v);
- *v = gv;
- caml_register_generational_global_root (v);
- guestfs_set_private (g, "_ocaml_g", v);
-
CAMLreturn (gv);
}
@@ -358,14 +344,10 @@ event_callback_wrapper_locked (guestfs_h *g,
const uint64_t *array, size_t array_len)
{
CAMLparam0 ();
- CAMLlocal5 (gv, evv, ehv, bufv, arrayv);
+ CAMLlocal4 (evv, ehv, bufv, arrayv);
CAMLlocal2 (rv, v);
- value *root;
size_t i;
- root = guestfs_get_private (g, "_ocaml_g");
- gv = *root;
-
/* Only one bit should be set in 'event'. Which one? */
evv = Val_int (event_bitmask_to_event (event));
@@ -380,9 +362,9 @@ event_callback_wrapper_locked (guestfs_h *g,
Store_field (arrayv, i, v);
}
- value args[5] = { gv, evv, ehv, bufv, arrayv };
+ value args[4] = { evv, ehv, bufv, arrayv };
- rv = caml_callbackN_exn (*(value*)data, 5, args);
+ rv = caml_callbackN_exn (*(value*)data, 4, args);
/* Callbacks shouldn't throw exceptions. There's not much we can do
* except to print it.
diff --git a/ocaml/t/guestfs_410_close_event.ml
b/ocaml/t/guestfs_410_close_event.ml
index e8dd626..13c3220 100644
--- a/ocaml/t/guestfs_410_close_event.ml
+++ b/ocaml/t/guestfs_410_close_event.ml
@@ -18,7 +18,7 @@
let close_invoked = ref 0
-let close _ _ _ _ _ +let close _ _ _ _ incr close_invoked
let () diff --git a/ocaml/t/guestfs_420_log_messages.ml
b/ocaml/t/guestfs_420_log_messages.ml
index 673a88f..b58dbd9 100644
--- a/ocaml/t/guestfs_420_log_messages.ml
+++ b/ocaml/t/guestfs_420_log_messages.ml
@@ -20,7 +20,7 @@ open Printf
let log_invoked = ref 0
-let log g ev eh buf array +let log ev eh buf array let eh : int = Obj.magic
eh in
printf "event logged: event=%s eh=%d buf=%S array=[%s]\n"
diff --git a/ocaml/t/guestfs_430_progress_messages.ml
b/ocaml/t/guestfs_430_progress_messages.ml
index 26deee0..3d1cc3f 100644
--- a/ocaml/t/guestfs_430_progress_messages.ml
+++ b/ocaml/t/guestfs_430_progress_messages.ml
@@ -18,7 +18,7 @@
let callback_invoked = ref 0
-let callback _ _ _ _ _ = incr callback_invoked
+let callback _ _ _ _ = incr callback_invoked
let () let g = new Guestfs.guestfs () in
--
2.5.0
Richard W.M. Jones
2015-Oct-06 15:05 UTC
[Libguestfs] [PATCH 3/4] ocaml: Add regression test for failure of implicit close.
---
ocaml/Makefile.am | 2 ++
ocaml/t/guestfs_065_implicit_close.ml | 32 ++++++++++++++++++++++++++++++++
src/guestfs.pod | 1 +
3 files changed, 35 insertions(+)
create mode 100644 ocaml/t/guestfs_065_implicit_close.ml
diff --git a/ocaml/Makefile.am b/ocaml/Makefile.am
index d838561..ea41377 100644
--- a/ocaml/Makefile.am
+++ b/ocaml/Makefile.am
@@ -111,6 +111,7 @@ test_progs_bc = \
t/guestfs_040_create_multiple.bc \
t/guestfs_050_handle_properties.bc \
t/guestfs_060_explicit_close.bc \
+ t/guestfs_065_implicit_close.bc \
t/guestfs_070_optargs.bc \
t/guestfs_410_close_event.bc \
t/guestfs_420_log_messages.bc
@@ -122,6 +123,7 @@ test_progs_opt = \
t/guestfs_040_create_multiple.opt \
t/guestfs_050_handle_properties.opt \
t/guestfs_060_explicit_close.opt \
+ t/guestfs_065_implicit_close.opt \
t/guestfs_070_optargs.opt \
t/guestfs_410_close_event.opt \
t/guestfs_420_log_messages.opt
diff --git a/ocaml/t/guestfs_065_implicit_close.ml
b/ocaml/t/guestfs_065_implicit_close.ml
new file mode 100644
index 0000000..764ec63
--- /dev/null
+++ b/ocaml/t/guestfs_065_implicit_close.ml
@@ -0,0 +1,32 @@
+(* libguestfs OCaml tests
+ * Copyright (C) 2009-2015 Red Hat Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
USA.
+ *)
+
+let close_invoked = ref 0
+
+let close _ _ _ _ + incr close_invoked
+
+let () + let g = new Guestfs.guestfs () in
+ ignore (g#set_event_callback close [Guestfs.EVENT_CLOSE]);
+ assert (!close_invoked = 0);
+ (* This should cause the GC to close the handle. *)
+ Gc.compact ();
+ assert (!close_invoked = 1)
+
+let () = Gc.compact ()
diff --git a/src/guestfs.pod b/src/guestfs.pod
index 9ec7bbc..248a4d0 100644
--- a/src/guestfs.pod
+++ b/src/guestfs.pod
@@ -4169,6 +4169,7 @@ This is the numbering scheme used by the tests:
040 create multiple handles
050 test setting and getting config properties
060 explicit close
+ 065 implicit close (in GC'd languages)
070 optargs
- 100 launch, create partitions and LVs and filesystems
--
2.5.0
Richard W.M. Jones
2015-Oct-06 15:05 UTC
[Libguestfs] [PATCH 4/4] ocaml: The implicit close patch only works on native code.
--- ocaml/Makefile.am | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ocaml/Makefile.am b/ocaml/Makefile.am index ea41377..10a38f7 100644 --- a/ocaml/Makefile.am +++ b/ocaml/Makefile.am @@ -111,10 +111,11 @@ test_progs_bc = \ t/guestfs_040_create_multiple.bc \ t/guestfs_050_handle_properties.bc \ t/guestfs_060_explicit_close.bc \ - t/guestfs_065_implicit_close.bc \ t/guestfs_070_optargs.bc \ t/guestfs_410_close_event.bc \ t/guestfs_420_log_messages.bc +# Broken on bytecode, works on native: +# t/guestfs_065_implicit_close.bc test_progs_opt = \ t/guestfs_010_load.opt \ -- 2.5.0
Richard W.M. Jones
2015-Oct-06 16:55 UTC
Re: [Libguestfs] [PATCH 4/4] ocaml: The implicit close patch only works on native code.
On Tue, Oct 06, 2015 at 04:05:47PM +0100, Richard W.M. Jones wrote:> --- > ocaml/Makefile.am | 3 ++- > 1 file changed, 2 insertions(+), 1 deletion(-) > > diff --git a/ocaml/Makefile.am b/ocaml/Makefile.am > index ea41377..10a38f7 100644 > --- a/ocaml/Makefile.am > +++ b/ocaml/Makefile.am > @@ -111,10 +111,11 @@ test_progs_bc = \ > t/guestfs_040_create_multiple.bc \ > t/guestfs_050_handle_properties.bc \ > t/guestfs_060_explicit_close.bc \ > - t/guestfs_065_implicit_close.bc \ > t/guestfs_070_optargs.bc \ > t/guestfs_410_close_event.bc \ > t/guestfs_420_log_messages.bc > +# Broken on bytecode, works on native: > +# t/guestfs_065_implicit_close.bcThe reason the test failed on bytecode was just a mistake in the test which I fixed by modifying it as below. So this patch is no longer required. let () let g = new Guestfs.guestfs () in ignore (g#set_event_callback close [Guestfs.EVENT_CLOSE]); assert (!close_invoked = 0) (* Allow the 'g' handle to go out of scope here, to ensure there is no * reference held on the stack. *) (* This should cause the GC to close the handle. *) let () = Gc.compact () Rich. -- Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones Read my programming and virtualization blog: http://rwmj.wordpress.com libguestfs lets you edit virtual machines. Supports shell scripting, bindings from many languages. http://libguestfs.org
Pino Toscano
2015-Oct-07 12:13 UTC
Re: [Libguestfs] [PATCH 1/4] ocaml: Use generational global roots.
On Tuesday 06 October 2015 16:05:44 Richard W.M. Jones wrote:> These are considerably more efficient than ordinary global roots, but > with the caveat that the program is not allowed to modify them without > calling a special function. We don't modify them, so this change is > safe. > > This requires OCaml >= 3.11, but we have that on RHEL 6 > (since we dropped support for RHEL 5). > > See also: > http://caml.inria.fr/pub/ml-archives/caml-list/2008/03/c3bf86990088236ceeb9a0f0f4c35390.en.html > ---LGTM. Maybe we need to add an OCaml version check in configure? -- Pino Toscano
Apparently Analagous Threads
- [PATCH] configure: Move language binding detection to separate files.
- [PATCH 01/27] build: Make OCaml compiler required for all builds.
- [PATCH 0/9] build: Require OCaml >= 4.02.
- [PATCH 1/4] ocaml: Use generational global roots.
- [PATCH 00/16] Refactoring of configure.ac and guestfs.pod