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