mirror of
https://github.com/aljazceru/lightning.git
synced 2025-12-22 16:44:20 +01:00
ccan: add tal/link.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
4
Makefile
4
Makefile
@@ -72,6 +72,7 @@ CCAN_OBJS := \
|
|||||||
ccan-str.o \
|
ccan-str.o \
|
||||||
ccan-take.o \
|
ccan-take.o \
|
||||||
ccan-tal-grab_file.o \
|
ccan-tal-grab_file.o \
|
||||||
|
ccan-tal-link.o \
|
||||||
ccan-tal-path.o \
|
ccan-tal-path.o \
|
||||||
ccan-tal-str.o \
|
ccan-tal-str.o \
|
||||||
ccan-tal.o \
|
ccan-tal.o \
|
||||||
@@ -131,6 +132,7 @@ CCAN_HEADERS := \
|
|||||||
$(CCANDIR)/ccan/structeq/structeq.h \
|
$(CCANDIR)/ccan/structeq/structeq.h \
|
||||||
$(CCANDIR)/ccan/take/take.h \
|
$(CCANDIR)/ccan/take/take.h \
|
||||||
$(CCANDIR)/ccan/tal/grab_file/grab_file.h \
|
$(CCANDIR)/ccan/tal/grab_file/grab_file.h \
|
||||||
|
$(CCANDIR)/ccan/tal/link/link.h \
|
||||||
$(CCANDIR)/ccan/tal/path/path.h \
|
$(CCANDIR)/ccan/tal/path/path.h \
|
||||||
$(CCANDIR)/ccan/tal/str/str.h \
|
$(CCANDIR)/ccan/tal/str/str.h \
|
||||||
$(CCANDIR)/ccan/tal/tal.h \
|
$(CCANDIR)/ccan/tal/tal.h \
|
||||||
@@ -426,6 +428,8 @@ ccan-tal.o: $(CCANDIR)/ccan/tal/tal.c
|
|||||||
$(CC) $(CFLAGS) -c -o $@ $<
|
$(CC) $(CFLAGS) -c -o $@ $<
|
||||||
ccan-tal-str.o: $(CCANDIR)/ccan/tal/str/str.c
|
ccan-tal-str.o: $(CCANDIR)/ccan/tal/str/str.c
|
||||||
$(CC) $(CFLAGS) -c -o $@ $<
|
$(CC) $(CFLAGS) -c -o $@ $<
|
||||||
|
ccan-tal-link.o: $(CCANDIR)/ccan/tal/link/link.c
|
||||||
|
$(CC) $(CFLAGS) -c -o $@ $<
|
||||||
ccan-tal-path.o: $(CCANDIR)/ccan/tal/path/path.c
|
ccan-tal-path.o: $(CCANDIR)/ccan/tal/path/path.c
|
||||||
$(CC) $(CFLAGS) -c -o $@ $<
|
$(CC) $(CFLAGS) -c -o $@ $<
|
||||||
ccan-tal-grab_file.o: $(CCANDIR)/ccan/tal/grab_file/grab_file.c
|
ccan-tal-grab_file.o: $(CCANDIR)/ccan/tal/grab_file/grab_file.c
|
||||||
|
|||||||
1
ccan/ccan/tal/link/LICENSE
Symbolic link
1
ccan/ccan/tal/link/LICENSE
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
../../../licenses/BSD-MIT
|
||||||
139
ccan/ccan/tal/link/_info
Normal file
139
ccan/ccan/tal/link/_info
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
#include "config.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* tal/link - link helper for tal
|
||||||
|
*
|
||||||
|
* Tal does not support talloc-style references. In the cases where
|
||||||
|
* an object needs multiple parents, all parents need to be aware of
|
||||||
|
* the situation; thus tal/link is a helper where all "parents"
|
||||||
|
* tal_link an object they agree to share ownership of.
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
* // Silly program which keeps a cache of uppercased strings.
|
||||||
|
* // The cache wants to keep strings around even after they may have
|
||||||
|
* // been "freed" by the caller.
|
||||||
|
* // Given "hello" outputs "1 cache hits HELLO \n"
|
||||||
|
* // Given "hello hello there" outputs "4 cache hits HELLO HELLO THERE \n"
|
||||||
|
* #include <stdio.h>
|
||||||
|
* #include <err.h>
|
||||||
|
* #include <string.h>
|
||||||
|
* #include <ctype.h>
|
||||||
|
* #include <ccan/tal/link/link.h>
|
||||||
|
* #include <ccan/tal/str/str.h>
|
||||||
|
*
|
||||||
|
* struct upcache {
|
||||||
|
* const char *str;
|
||||||
|
* const char *upstr;
|
||||||
|
* };
|
||||||
|
*
|
||||||
|
* static struct upcache *cache;
|
||||||
|
* static unsigned int cache_hits = 0;
|
||||||
|
* #define CACHE_SIZE 4
|
||||||
|
* static void init_upcase(void)
|
||||||
|
* {
|
||||||
|
* cache = tal_arrz(NULL, struct upcache, CACHE_SIZE);
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* static struct upcache *lookup_upcase(const char *str)
|
||||||
|
* {
|
||||||
|
* unsigned int i;
|
||||||
|
* for (i = 0; i < CACHE_SIZE; i++)
|
||||||
|
* if (cache[i].str && !strcmp(cache[i].str, str)) {
|
||||||
|
* cache_hits++;
|
||||||
|
* return &cache[i];
|
||||||
|
* }
|
||||||
|
* return NULL;
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* static struct upcache *new_upcase(const char *str)
|
||||||
|
* {
|
||||||
|
* unsigned int i;
|
||||||
|
* char *upstr;
|
||||||
|
*
|
||||||
|
* upstr = tal_linkable(tal_strdup(NULL, str));
|
||||||
|
* i = random() % CACHE_SIZE;
|
||||||
|
*
|
||||||
|
* // Throw out old: works fine if cache[i].upstr is NULL.
|
||||||
|
* tal_delink(cache, cache[i].upstr);
|
||||||
|
*
|
||||||
|
* // Replace with new.
|
||||||
|
* cache[i].str = str;
|
||||||
|
* cache[i].upstr = tal_link(cache, upstr);
|
||||||
|
* while (*upstr) {
|
||||||
|
* *upstr = toupper(*upstr);
|
||||||
|
* upstr++;
|
||||||
|
* }
|
||||||
|
* return &cache[i];
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* // If you want to keep the result, tal_link it.
|
||||||
|
* static const char *get_upcase(const char *str)
|
||||||
|
* {
|
||||||
|
* struct upcache *uc = lookup_upcase(str);
|
||||||
|
* if (!uc)
|
||||||
|
* uc = new_upcase(str);
|
||||||
|
* if (!uc)
|
||||||
|
* return NULL;
|
||||||
|
* return uc->upstr;
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* static void exit_upcase(void)
|
||||||
|
* {
|
||||||
|
* tal_free(cache);
|
||||||
|
* printf("%u cache hits ", cache_hits);
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* int main(int argc, char *argv[])
|
||||||
|
* {
|
||||||
|
* int i;
|
||||||
|
* const char **values;
|
||||||
|
*
|
||||||
|
* // Initialize cache.
|
||||||
|
* init_upcase();
|
||||||
|
*
|
||||||
|
* // Throw values in.
|
||||||
|
* values = tal_arr(NULL, const char *, argc);
|
||||||
|
* for (i = 1; i < argc; i++)
|
||||||
|
* values[i-1] = tal_link(values, get_upcase(argv[i]));
|
||||||
|
*
|
||||||
|
* // This will free all the values, but cache will still work.
|
||||||
|
* tal_free(values);
|
||||||
|
*
|
||||||
|
* // Repeat!
|
||||||
|
* values = tal_arr(NULL, const char *, argc);
|
||||||
|
* for (i = 1; i < argc; i++)
|
||||||
|
* values[i-1] = tal_link(values, get_upcase(argv[i]));
|
||||||
|
*
|
||||||
|
* // This will remove cache links, but we still have a link.
|
||||||
|
* exit_upcase();
|
||||||
|
*
|
||||||
|
* // Show values, so we output something.
|
||||||
|
* for (i = 0; i < argc - 1; i++)
|
||||||
|
* printf("%s ", values[i]);
|
||||||
|
* printf("\n");
|
||||||
|
*
|
||||||
|
* // This will finally free the upcase strings (last link).
|
||||||
|
* tal_free(values);
|
||||||
|
*
|
||||||
|
* return 0;
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* License: BSD-MIT
|
||||||
|
* Author: Rusty Russell <rusty@rustcorp.com.au>
|
||||||
|
*/
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
if (argc != 2)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
if (strcmp(argv[1], "depends") == 0) {
|
||||||
|
printf("ccan/container_of\n");
|
||||||
|
printf("ccan/list\n");
|
||||||
|
printf("ccan/tal\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
105
ccan/ccan/tal/link/link.c
Normal file
105
ccan/ccan/tal/link/link.c
Normal file
@@ -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 UNNEEDED)
|
||||||
|
{
|
||||||
|
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();
|
||||||
|
}
|
||||||
69
ccan/ccan/tal/link/link.h
Normal file
69
ccan/ccan/tal/link/link.h
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
/* Licensed under BSD-MIT - see LICENSE file for details */
|
||||||
|
#ifndef TAL_LINK_H
|
||||||
|
#define TAL_LINK_H
|
||||||
|
#include "config.h"
|
||||||
|
#include <ccan/tal/tal.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* tal_linkable - set up a tal object to be linkable.
|
||||||
|
* @newobj - the newly allocated object (with a NULL parent)
|
||||||
|
*
|
||||||
|
* The object will be freed when @newobj is freed or the last tal_link()
|
||||||
|
* is tal_delink'ed.
|
||||||
|
*
|
||||||
|
* Returns @newobj or NULL (if an allocation fails).
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
* int *shared_count;
|
||||||
|
*
|
||||||
|
* shared_count = tal_linkable(talz(NULL, int));
|
||||||
|
* assert(shared_count);
|
||||||
|
*/
|
||||||
|
#define tal_linkable(newobj) \
|
||||||
|
(tal_typeof(newobj) tal_linkable_((newobj)))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* tal_link - add a(nother) link to a linkable object.
|
||||||
|
* @ctx - the context to link to (parent of the resulting link)
|
||||||
|
* @obj - the object previously made linkable with tal_linked().
|
||||||
|
*
|
||||||
|
* If @ctx is non-NULL, the link will be a child of @ctx, and this freed
|
||||||
|
* when @ctx is.
|
||||||
|
*
|
||||||
|
* Returns NULL on failure (out of memory).
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
* void *my_ctx = NULL;
|
||||||
|
*
|
||||||
|
* tal_link(my_ctx, shared_count);
|
||||||
|
*/
|
||||||
|
#if HAVE_STATEMENT_EXPR
|
||||||
|
/* Weird macro avoids gcc's 'warning: value computed is not used'. */
|
||||||
|
#define tal_link(ctx, obj) \
|
||||||
|
({ tal_typeof(obj) tal_link_((ctx), (obj)); })
|
||||||
|
#else
|
||||||
|
#define tal_link(ctx, obj) \
|
||||||
|
(tal_typeof(obj) tal_link_((ctx), (obj)))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* tal_delink - explicitly remove a link from a linkable object.
|
||||||
|
* @ctx - the context to link to (parent of the resulting link)
|
||||||
|
* @obj - the object previously made linkable with tal_linked().
|
||||||
|
*
|
||||||
|
* Explicitly remove a link: normally it is implied by freeing @ctx.
|
||||||
|
* Removing the last link frees the object. If @obj is NULL, nothing
|
||||||
|
* is done.
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
* tal_delink(my_ctx, shared_count);
|
||||||
|
*/
|
||||||
|
#define tal_delink(ctx, obj) \
|
||||||
|
tal_delink_((ctx), (obj))
|
||||||
|
|
||||||
|
/* Internal helpers. */
|
||||||
|
void *tal_linkable_(tal_t *newobj);
|
||||||
|
void *tal_link_(const tal_t *ctx, const tal_t *dest);
|
||||||
|
void tal_delink_(const tal_t *ctx, const tal_t *dest);
|
||||||
|
|
||||||
|
#endif /* TAL_LINK_H */
|
||||||
73
ccan/ccan/tal/link/test/run.c
Normal file
73
ccan/ccan/tal/link/test/run.c
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
#include <ccan/tal/link/link.c>
|
||||||
|
#include <ccan/tap/tap.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <err.h>
|
||||||
|
|
||||||
|
static unsigned int destroy_count = 0;
|
||||||
|
static void destroy_obj(void *obj UNNEEDED)
|
||||||
|
{
|
||||||
|
destroy_count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
char *linkable, *p1, *p2, *p3;
|
||||||
|
void **voidpp;
|
||||||
|
|
||||||
|
plan_tests(23);
|
||||||
|
|
||||||
|
linkable = tal(NULL, char);
|
||||||
|
ok1(tal_linkable(linkable) == linkable);
|
||||||
|
ok1(tal_add_destructor(linkable, destroy_obj));
|
||||||
|
/* First, free it immediately. */
|
||||||
|
tal_free(linkable);
|
||||||
|
ok1(destroy_count == 1);
|
||||||
|
|
||||||
|
/* Now create and remove a single link. */
|
||||||
|
linkable = tal_linkable(tal(NULL, char));
|
||||||
|
ok1(tal_add_destructor(linkable, destroy_obj));
|
||||||
|
ok1(p1 = tal_link(NULL, linkable));
|
||||||
|
ok1(p1 == linkable);
|
||||||
|
tal_delink(NULL, linkable);
|
||||||
|
ok1(destroy_count == 2);
|
||||||
|
|
||||||
|
/* Two links.*/
|
||||||
|
linkable = tal_linkable(tal(NULL, char));
|
||||||
|
ok1(tal_add_destructor(linkable, destroy_obj));
|
||||||
|
ok1(p1 = tal_link(NULL, linkable));
|
||||||
|
ok1(p1 == linkable);
|
||||||
|
ok1(p2 = tal_link(NULL, linkable));
|
||||||
|
ok1(p2 == linkable);
|
||||||
|
tal_delink(NULL, linkable);
|
||||||
|
tal_delink(NULL, linkable);
|
||||||
|
ok1(destroy_count == 3);
|
||||||
|
|
||||||
|
/* Three links.*/
|
||||||
|
linkable = tal_linkable(tal(NULL, char));
|
||||||
|
ok1(tal_add_destructor(linkable, destroy_obj));
|
||||||
|
ok1(p1 = tal_link(NULL, linkable));
|
||||||
|
ok1(p1 == linkable);
|
||||||
|
ok1(p2 = tal_link(NULL, linkable));
|
||||||
|
ok1(p2 == linkable);
|
||||||
|
ok1(p3 = tal_link(NULL, linkable));
|
||||||
|
ok1(p3 == linkable);
|
||||||
|
tal_delink(NULL, linkable);
|
||||||
|
tal_delink(NULL, linkable);
|
||||||
|
tal_delink(NULL, linkable);
|
||||||
|
ok1(destroy_count == 4);
|
||||||
|
|
||||||
|
/* Now, indirectly. */
|
||||||
|
voidpp = tal(NULL, void *);
|
||||||
|
linkable = tal_linkable(tal(NULL, char));
|
||||||
|
ok1(tal_add_destructor(linkable, destroy_obj));
|
||||||
|
/* Suppress gratuitous warning with tests_compile_without_features */
|
||||||
|
#if HAVE_STATEMENT_EXPR
|
||||||
|
tal_link(voidpp, linkable);
|
||||||
|
#else
|
||||||
|
(void)tal_link(voidpp, linkable);
|
||||||
|
#endif
|
||||||
|
tal_free(voidpp);
|
||||||
|
ok1(destroy_count == 5);
|
||||||
|
|
||||||
|
return exit_status();
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user