]> git.ozlabs.org Git - ccan/blobdiff - ccan/tal/link/link.c
tal/link: new module for reference-count style coding for tal.
[ccan] / ccan / tal / link / link.c
diff --git a/ccan/tal/link/link.c b/ccan/tal/link/link.c
new file mode 100644 (file)
index 0000000..c25d97e
--- /dev/null
@@ -0,0 +1,105 @@
+/* Licensed under BSD-MIT - see LICENSE file for details */
+#include <ccan/tal/link/link.h>
+#include <ccan/container_of/container_of.h>
+#include <ccan/list/list.h>
+#include <assert.h>
+
+/* 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();
+}