/* Licensed under BSD-MIT - see LICENSE file for details */ #include #include #include #include /* Our linkable parent. */ struct linkable { struct list_head links; }; struct link { struct list_node list; }; static void linkable_notifier(tal_t *linkable, enum tal_notify_type type, void *info) { struct linkable *l = tal_parent(linkable); assert(type == TAL_NOTIFY_STEAL || type == TAL_NOTIFY_FREE); /* We let you free it if you haven't linked it yet. */ if (type == TAL_NOTIFY_FREE && list_empty(&l->links)) { tal_free(l); return; } /* Don't try to steal or free this: it has multiple links! */ abort(); } void *tal_linkable_(tal_t *newobj) { struct linkable *l; /* Must be a fresh object. */ assert(!tal_parent(newobj)); l = tal(NULL, struct linkable); if (!l) goto fail; list_head_init(&l->links); if (!tal_steal(l, newobj)) goto fail; if (!tal_add_notifier(newobj, TAL_NOTIFY_STEAL|TAL_NOTIFY_FREE, linkable_notifier)) { tal_steal(NULL, newobj); goto fail; } return (void *)newobj; fail: tal_free(l); return NULL; } static void destroy_link(struct link *lnk) { struct linkable *l; /* Only true if we're first in list! */ l = container_of(lnk->list.prev, struct linkable, links.n); list_del(&lnk->list); if (list_empty(&l->links)) tal_free(l); } void *tal_link_(const tal_t *ctx, const tal_t *link) { struct linkable *l = tal_parent(link); struct link *lnk = tal(ctx, struct link); if (!lnk) return NULL; if (!tal_add_destructor(lnk, destroy_link)) { tal_free(lnk); return NULL; } list_add(&l->links, &lnk->list); return (void *)link; } void tal_delink_(const tal_t *ctx, const tal_t *link) { struct linkable *l = tal_parent(link); struct link *i; if (!link) return; /* FIXME: slow, but hopefully unusual. */ list_for_each(&l->links, i, list) { if (tal_parent(i) == ctx) { tal_free(i); return; } } abort(); }