1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
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();
}
|