hmm.. Re: talloc issues

Rusty Russell rusty at rustcorp.com.au
Thu Jul 30 01:28:01 MDT 2009


On Wed, 29 Jul 2009 02:17:00 am Jeremy Allison wrote:
> +1. talloc_reference broke my brain a long time ago,
> and I don't want to maintain any code that contains
> it.

There are patterns which require it; the clearest to me is the "cache"
pattern where we want to keep an object around for later reuse, so we
hand out references.

Here's a trivial 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.
#include <stdio.h>
#include <err.h>
#include <string.h>
#include <ctype.h>
#include <talloc.h>

struct upcache {
	const char *str;
	const char *upstr;
};

static struct upcache *cache;
static unsigned int cache_hits = 0;
#define CACHE_SIZE 4
void init_upcase(void)
{
	cache = talloc_zero_array(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 = talloc_strdup(cache, str);
	if (!upstr)
		return NULL;

	i = random() % CACHE_SIZE;

	// Throw out old: works fine if cache[i].upstr is NULL.
	talloc_unlink(cache, cache[i].upstr);

	// Replace with new.
	cache[i].str = str;
	cache[i].upstr = upstr;
	while (*upstr) {
		*upstr = toupper(*upstr);
		upstr++;
	}
	return &cache[i];
}

// If you want to keep the result, talloc_reference it.
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;
}

void exit_upcase(void)
{
	talloc_free(cache);
	printf("Cache hits: %u\n", cache_hits);
}

And heres a little demo program which uses it: runs clean under
valgrind.  Note the caller here has to *know* they are dealing with
references, so no stealing or freeing!


int main(int argc, char *argv[])
{
	unsigned int i;
	const char **values;

	// Will dump any memory leaks to stderr on exit.
	talloc_enable_leak_report();

	// Initialize cache.
	init_upcase();

	// Throw values in.
	values = talloc_array(NULL, const char *, argc);
	for (i = 1; i < argc; i++) {
		values[i-1] = talloc_reference(values, get_upcase(argv[i]));
		if (!values[i-1])
			err(1, "Out of memory");
	}
	// This will free all the values, but cache will still work.
	talloc_free(values);

	// Repeat!
	values = talloc_array(NULL, const char *, argc);
	for (i = 1; i < argc; i++) {
		values[i-1] = talloc_reference(values, get_upcase(argv[i]));
		if (!values[i-1])
			err(1, "Out of memory");
	}

	// This will remove cache parents, but we still have a reference.
	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).
	talloc_free(values);

	return 0;
}


More information about the samba-technical mailing list