]> git.ozlabs.org Git - ccan/blob - ccan/tal/link/link.c
tal/link: new module for reference-count style coding for tal.
[ccan] / ccan / tal / link / link.c
1 /* Licensed under BSD-MIT - see LICENSE file for details */
2 #include <ccan/tal/link/link.h>
3 #include <ccan/container_of/container_of.h>
4 #include <ccan/list/list.h>
5 #include <assert.h>
6
7 /* Our linkable parent. */
8 struct linkable {
9         struct list_head links;
10 };
11
12 struct link {
13         struct list_node list;
14 };
15
16 static void linkable_notifier(tal_t *linkable,
17                               enum tal_notify_type type,
18                               void *info)
19 {
20         struct linkable *l = tal_parent(linkable);
21         assert(type == TAL_NOTIFY_STEAL || type == TAL_NOTIFY_FREE);
22
23         /* We let you free it if you haven't linked it yet. */
24         if (type == TAL_NOTIFY_FREE && list_empty(&l->links)) {
25                 tal_free(l);
26                 return;
27         }
28
29         /* Don't try to steal or free this: it has multiple links! */
30         abort();
31 }
32
33 void *tal_linkable_(tal_t *newobj)
34 {
35         struct linkable *l;
36
37         /* Must be a fresh object. */
38         assert(!tal_parent(newobj));
39
40         l = tal(NULL, struct linkable);
41         if (!l)
42                 goto fail;
43         list_head_init(&l->links);
44
45         if (!tal_steal(l, newobj))
46                 goto fail;
47
48         if (!tal_add_notifier(newobj, TAL_NOTIFY_STEAL|TAL_NOTIFY_FREE,
49                               linkable_notifier)) {
50                 tal_steal(NULL, newobj);
51                 goto fail;
52         }
53
54         return (void *)newobj;
55
56 fail:
57         tal_free(l);
58         return NULL;
59 }
60
61 static void destroy_link(struct link *lnk)
62 {
63         struct linkable *l;
64
65         /* Only true if we're first in list! */
66         l = container_of(lnk->list.prev, struct linkable, links.n);
67
68         list_del(&lnk->list);
69
70         if (list_empty(&l->links))
71                 tal_free(l);
72 }
73
74 void *tal_link_(const tal_t *ctx, const tal_t *link)
75 {
76         struct linkable *l = tal_parent(link);
77         struct link *lnk = tal(ctx, struct link);
78
79         if (!lnk)
80                 return NULL;
81         if (!tal_add_destructor(lnk, destroy_link)) {
82                 tal_free(lnk);
83                 return NULL;
84         }
85         list_add(&l->links, &lnk->list);
86         return (void *)link;
87 }
88
89 void tal_delink_(const tal_t *ctx, const tal_t *link)
90 {
91         struct linkable *l = tal_parent(link);
92         struct link *i;
93
94         if (!link)
95                 return;
96
97         /* FIXME: slow, but hopefully unusual. */
98         list_for_each(&l->links, i, list) {
99                 if (tal_parent(i) == ctx) {
100                         tal_free(i);
101                         return;
102                 }
103         }
104         abort();
105 }