From: "Richard W.M. Jones" <rjones(a)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