From 97ac5832db26f5f836fff979b08f01e17d7216bd Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 29 Dec 2016 15:04:30 +1030 Subject: [PATCH 1/1] tal/autoptr: new module. Helps with the common case of wanting to NULL out a pointer when the object freed. We could also track it if resized, but that's TODO. Signed-off-by: Rusty Russell --- ccan/tal/autoptr/LICENSE | 1 + ccan/tal/autoptr/_info | 44 +++++++++++++++++++++++++++++++++ ccan/tal/autoptr/autoptr.c | 35 ++++++++++++++++++++++++++ ccan/tal/autoptr/autoptr.h | 47 +++++++++++++++++++++++++++++++++++ ccan/tal/autoptr/test/run.c | 49 +++++++++++++++++++++++++++++++++++++ 5 files changed, 176 insertions(+) create mode 120000 ccan/tal/autoptr/LICENSE create mode 100644 ccan/tal/autoptr/_info create mode 100644 ccan/tal/autoptr/autoptr.c create mode 100644 ccan/tal/autoptr/autoptr.h create mode 100644 ccan/tal/autoptr/test/run.c diff --git a/ccan/tal/autoptr/LICENSE b/ccan/tal/autoptr/LICENSE new file mode 120000 index 00000000..2b1feca5 --- /dev/null +++ b/ccan/tal/autoptr/LICENSE @@ -0,0 +1 @@ +../../../licenses/BSD-MIT \ No newline at end of file diff --git a/ccan/tal/autoptr/_info b/ccan/tal/autoptr/_info new file mode 100644 index 00000000..b233ce58 --- /dev/null +++ b/ccan/tal/autoptr/_info @@ -0,0 +1,44 @@ +#include "config.h" +#include +#include + +/** + * tal/autoptr - automatic updates of pointers to tal objects. + * + * This code updates pointers when the pointed-to object is freed. + * + * Maintainer: Rusty Russell + * License: BSD-MIT + * Example: + * #include + * #include + * + * static void *p; + * + * int main(void) + * { + * char *c = tal(NULL, char); + * + * // Sets p to point to c. + * autonull_set_ptr(NULL, &p, c); + * assert(p == c); + * + * // Automatically clears p. + * tal_free(c); + * assert(p == NULL); + * return 0; + * } + */ +int main(int argc, char *argv[]) +{ + /* Expect exactly one argument */ + if (argc != 2) + return 1; + + if (strcmp(argv[1], "depends") == 0) { + printf("ccan/tal\n"); + return 0; + } + + return 1; +} diff --git a/ccan/tal/autoptr/autoptr.c b/ccan/tal/autoptr/autoptr.c new file mode 100644 index 00000000..5f22ef2c --- /dev/null +++ b/ccan/tal/autoptr/autoptr.c @@ -0,0 +1,35 @@ +/* MIT (BSD) license - see LICENSE file for details */ +#include + +struct autonull { + void **pp; +}; + +static void autonull_remove(struct autonull *a); +static void autonull_null_out(tal_t *p UNNEEDED, struct autonull *a) +{ + void **pp = a->pp; + tal_del_destructor(a, autonull_remove); + tal_free(a); + *pp = NULL; +} + +static void autonull_remove(struct autonull *a) +{ + /* Don't NULL us out now. */ + tal_del_destructor2(*a->pp, autonull_null_out, a); +} + +struct autonull *autonull_set_ptr_(const tal_t *ctx, void *pp, const tal_t *p) +{ + struct autonull *a = tal(ctx, struct autonull); + a->pp = (void **)pp; + *a->pp = (void *)p; + + /* If p is freed, NULL out a->pp */ + tal_add_destructor2(*a->pp, autonull_null_out, a); + + /* If they free autonull, it removes other destructor. */ + tal_add_destructor(a, autonull_remove); + return a; +} diff --git a/ccan/tal/autoptr/autoptr.h b/ccan/tal/autoptr/autoptr.h new file mode 100644 index 00000000..57996653 --- /dev/null +++ b/ccan/tal/autoptr/autoptr.h @@ -0,0 +1,47 @@ +/* MIT (BSD) license - see LICENSE file for details */ +#ifndef CCAN_TAL_AUTOPTR_H +#define CCAN_TAL_AUTOPTR_H +#include + +struct autonull; + +/** + * autonull_set_ptr - set a pointer, NULL it when pointer tal_free'd. + * @ctx: the tal context which owns this autonull. + * @pp: pointer to tal pointer + * @p: the tal pointer to set *@pp to. + * + * *@pp is set to @p. When @p is tal_free'd directly or indirectly, *@pp will + * be set to NULL. Or, if the returned object is freed, the callback is + * deactivated. + * + * Example: + * struct parent { + * struct child *c; + * }; + * struct child { + * const char *name; + * }; + * + * int main(void) + * { + * struct parent *p = tal(NULL, struct parent); + * struct child *c = tal(p, struct child); + * c->name = "Child"; + * + * autonull_set_ptr(p, &p->c, c); + * assert(p->c == c); + * + * // Automatically clears p->c. + * tal_free(c); + * assert(p->c == NULL); + * return 0; + * } + * + */ +#define autonull_set_ptr(ctx, pp, p) \ + autonull_set_ptr_((ctx), (pp) + 0*sizeof(*(pp) = (p)), (p)) + +struct autonull *autonull_set_ptr_(const tal_t *ctx, void *pp, const tal_t *p); + +#endif /* CCAN_TAL_AUTOPTR_H */ diff --git a/ccan/tal/autoptr/test/run.c b/ccan/tal/autoptr/test/run.c new file mode 100644 index 00000000..33f1b042 --- /dev/null +++ b/ccan/tal/autoptr/test/run.c @@ -0,0 +1,49 @@ +#include +/* Include the C files directly. */ +#include +#include + +int main(void) +{ + char *p1, *p2, *p3; + struct autonull *a; + + /* This is how many tests you plan to run */ + plan_tests(8); + + p1 = tal(NULL, char); + + // Sets p1 to point to p2. + autonull_set_ptr(NULL, &p2, p1); + ok1(p2 == p1); + tal_free(p1); + ok1(p2 == NULL); + + // Using p1 as the parent is the same. */ + p1 = tal(NULL, char); + autonull_set_ptr(p1, &p2, p1); + ok1(p2 == p1); + tal_free(p1); + ok1(p2 == NULL); + + // Freeing autodata deactivates it. + p1 = tal(NULL, char); + a = autonull_set_ptr(NULL, &p2, p1); + ok1(p2 == p1); + tal_free(a); + tal_free(p1); + ok1(p2 == p1); + + // Making p3 the parent means freeing p3 deactivates it. + p3 = tal(NULL, char); + p1 = tal(NULL, char); + autonull_set_ptr(p3, &p2, p1); + ok1(p2 == p1); + tal_free(p3); + tal_free(p1); + ok1(p2 == p1); + + + /* This exits depending on whether all tests passed */ + return exit_status(); +} -- 2.39.2