Browse Source

ccan: add tal/link.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
ppa-0.6.1
Rusty Russell 7 years ago
parent
commit
237a65d000
  1. 4
      Makefile
  2. 1
      ccan/ccan/tal/link/LICENSE
  3. 139
      ccan/ccan/tal/link/_info
  4. 105
      ccan/ccan/tal/link/link.c
  5. 69
      ccan/ccan/tal/link/link.h
  6. 73
      ccan/ccan/tal/link/test/run.c

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

@ -0,0 +1 @@
../../../licenses/BSD-MIT

139
ccan/ccan/tal/link/_info

@ -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

@ -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

@ -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

@ -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();
}
Loading…
Cancel
Save