[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]

[Libguestfs] [PATCH 1/2] lib: Add CLEANUP_FREE macro which automatically calls 'free' when leaving scope.



From: "Richard W.M. Jones" <rjones redhat com>

Use the macro like this to create temporary variables which are
automatically cleaned up when the scope is exited:

  {
    CLEANUP_FREE (char *, foo, strdup (bar)); /* char *foo = strdup (bar) */
    ...
    // no need to call free (foo)!
  }

On GCC and LLVM, this is implemented using __attribute__((cleanup(...))).

On other compilers, we fall back to a less efficient implementation
which saves up the memory allocations and frees them all when the
handle is closed.
---
 configure.ac           | 35 +++++++++++++++++++++++++++++++++++
 src/alloc.c            | 19 +++++++++++++++++++
 src/guestfs-internal.h | 33 +++++++++++++++++++++++++++++++++
 src/handle.c           | 15 +++++++++++++++
 4 files changed, 102 insertions(+)

diff --git a/configure.ac b/configure.ac
index 39c79cf..7745ab5 100644
--- a/configure.ac
+++ b/configure.ac
@@ -191,6 +191,41 @@ AC_SYS_LARGEFILE
 dnl Check sizeof long.
 AC_CHECK_SIZEOF([long])
 
+dnl Check if __attribute__((cleanup(...))) works.
+dnl XXX It would be nice to use AC_COMPILE_IFELSE here, but gcc just
+dnl emits a warning for attributes that it doesn't understand.
+AC_MSG_CHECKING([if __attribute__((cleanup(...))) works with this compiler])
+AC_RUN_IFELSE([
+AC_LANG_SOURCE([[
+#include <stdio.h>
+#include <stdlib.h>
+
+void
+freep (void *ptr)
+{
+  exit (0);
+}
+
+void
+test (void)
+{
+  __attribute__((cleanup(freep))) char *ptr = malloc (100);
+}
+
+int
+main (int argc, char *argv[])
+{
+  test ();
+  exit (1);
+}
+]])
+    ],[
+    AC_MSG_RESULT([yes])
+    AC_DEFINE([HAVE_ATTRIBUTE_CLEANUP],[1],[Define to 1 if '__attribute__((cleanup(...)))' works with this compiler.])
+    ],[
+    AC_MSG_RESULT([no])
+    ])
+
 dnl Check if dirent (readdir) supports d_type member.
 AC_STRUCT_DIRENT_D_TYPE
 
diff --git a/src/alloc.c b/src/alloc.c
index 25d7f42..cf3741a 100644
--- a/src/alloc.c
+++ b/src/alloc.c
@@ -122,3 +122,22 @@ guestfs___safe_asprintf (guestfs_h *g, const char *fs, ...)
 
   return msg;
 }
+
+void
+guestfs___cleanup_free (void *ptr)
+{
+  free (* (void **) ptr);
+}
+
+#ifndef HAVE_ATTRIBUTE_CLEANUP
+void
+guestfs___defer_free (guestfs_h *g, void (*freefn) (void *), void *data)
+{
+  struct deferred_free *p = safe_malloc (g, sizeof *p);
+
+  p->freefn = freefn;
+  p->data = data;
+  p->next = g->deferred_frees;
+  g->deferred_frees = p;
+}
+#endif
diff --git a/src/guestfs-internal.h b/src/guestfs-internal.h
index 870207b..d27a3c2 100644
--- a/src/guestfs-internal.h
+++ b/src/guestfs-internal.h
@@ -72,6 +72,19 @@
 #define TRACE4(name, arg1, arg2, arg3, arg4)
 #endif
 
+#ifdef HAVE_ATTRIBUTE_CLEANUP
+#define CLEANUP(type, var, init, freefn)                \
+  __attribute__((cleanup(freefn))) type var = init
+#else
+#define CLEANUP(type, var, init, freefn)                \
+  type var = init;                                      \
+  guestfs___defer_free ((g), (freefn), (var))
+#endif
+
+#define CLEANUP_FREE(type, var, init)                   \
+  CLEANUP(type, var, (init), guestfs___cleanup_free)
+/* other CLEANUP_* macros to follow */
+
 #define TMP_TEMPLATE_ON_STACK(g,var)                      \
   char *ttos_tmpdir = guestfs_get_tmpdir (g);             \
   char var[strlen (ttos_tmpdir) + 32];                    \
@@ -324,6 +337,10 @@ struct guestfs_h
   virConnectCredentialPtr requested_credentials;
 #endif
 
+#ifndef HAVE_ATTRIBUTE_CLEANUP
+  struct deferred_free *deferred_frees;
+#endif
+
   /**** Private data for attach-methods. ****/
   /* NB: This cannot be a union because of a pathological case where
    * the user changes attach-method while reusing the handle to launch
@@ -356,6 +373,14 @@ struct guestfs_h
 #endif
 };
 
+#ifndef HAVE_ATTRIBUTE_CLEANUP
+struct deferred_free {
+  struct deferred_free *next;
+  void (*freefn) (void *);
+  void *data;
+};
+#endif
+
 /* Per-filesystem data stored for inspect_os. */
 enum inspect_os_format {
   OS_FORMAT_UNKNOWN = 0,
@@ -474,6 +499,14 @@ extern char *guestfs___safe_asprintf (guestfs_h *g, const char *fs, ...)
 #define safe_memdup guestfs___safe_memdup
 #define safe_asprintf guestfs___safe_asprintf
 
+/* These functions are used internally by the CLEANUP_* macros.
+ * Don't call them directly.
+ */
+extern void guestfs___cleanup_free (void *ptr);
+#ifndef HAVE_ATTRIBUTE_CLEANUP
+extern void guestfs___defer_free (guestfs_h *g, void (*freefn) (void *), void *data);
+#endif
+
 /* errors.c */
 extern void guestfs___init_error_handler (guestfs_h *g);
 
diff --git a/src/handle.c b/src/handle.c
index 39e30c7..8cf7a40 100644
--- a/src/handle.c
+++ b/src/handle.c
@@ -253,6 +253,9 @@ void
 guestfs_close (guestfs_h *g)
 {
   struct qemu_param *qp, *qp_next;
+#ifndef HAVE_ATTRIBUTE_CLEANUP
+  struct deferred_free *dfp, *dfp_next;
+#endif
   guestfs_h **gg;
 
   if (g->state == NO_HANDLE) {
@@ -324,6 +327,18 @@ guestfs_close (guestfs_h *g)
   while (g->error_cb_stack)
     guestfs_pop_error_handler (g);
 
+#ifndef HAVE_ATTRIBUTE_CLEANUP
+  /* For compilers that don't support __attribute__((cleanup(...))),
+   * free any temporary data that we allocated in CLEANUP_* macros
+   * here.
+   */
+  for (dfp = g->deferred_frees; dfp; dfp = dfp_next) {
+    dfp->freefn (&dfp->data);
+    dfp_next = dfp->next;
+    free (dfp);
+  }
+#endif
+
   if (g->pda)
     hash_free (g->pda);
   free (g->tmpdir);
-- 
1.8.1


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]