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