diff options
Diffstat (limited to 'ta')
-rw-r--r-- | ta/README | 2 | ||||
-rw-r--r-- | ta/ta.c | 226 | ||||
-rw-r--r-- | ta/ta.h | 33 | ||||
-rw-r--r-- | ta/ta_talloc.h | 14 | ||||
-rw-r--r-- | ta/ta_utils.c | 54 |
5 files changed, 126 insertions, 203 deletions
@@ -31,7 +31,7 @@ will be replaced with TA calls, and the talloc wrapper will be removed. Documentation for the talloc API is here: - http://talloc.samba.org/talloc/doc/html/modules.html + https://talloc.samba.org/talloc/doc/html/group__talloc.html There are some minor differences with mpv's talloc bridge. mpv calls abort() on allocation failures, and the talloc_set_destructor() signature is slightly @@ -13,28 +13,33 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include <assert.h> +#include <stddef.h> +#include <stdio.h> #include <stdlib.h> #include <string.h> -#include <stdio.h> -#include <assert.h> #define TA_NO_WRAPPERS #include "ta.h" -// Note: the actual minimum alignment is dictated by malloc(). It doesn't -// make sense to set this value higher than malloc's alignment. -#define MIN_ALIGN 16 - -#ifndef NDEBUG -#define TA_MEMORY_DEBUGGING +#if !defined(TA_MEMORY_DEBUGGING) + #if !defined(NDEBUG) + #define TA_MEMORY_DEBUGGING 1 + #else + #define TA_MEMORY_DEBUGGING 0 + #endif #endif struct ta_header { size_t size; // size of the user allocation - struct ta_header *prev; // ring list containing siblings + // Invariant: parent!=NULL => prev==NULL + struct ta_header *prev; // siblings list (by destructor order) struct ta_header *next; - struct ta_ext_header *ext; -#ifdef TA_MEMORY_DEBUGGING + // Invariant: parent==NULL || parent->child==this + struct ta_header *child; // points to first child + struct ta_header *parent; // set for _first_ child only, NULL otherwise + void (*destructor)(void *); +#if TA_MEMORY_DEBUGGING unsigned int canary; struct ta_header *leak_next; struct ta_header *leak_prev; @@ -44,6 +49,7 @@ struct ta_header { #define CANARY 0xD3ADB3EF +#define MIN_ALIGN _Alignof(max_align_t) union aligned_header { struct ta_header ta; // Make sure to satisfy typical alignment requirements @@ -59,16 +65,6 @@ union aligned_header { #define MAX_ALLOC (((size_t)-1) - sizeof(union aligned_header)) -// Needed for non-leaf allocations, or extended features such as destructors. -struct ta_ext_header { - struct ta_header *header; // points back to normal header - struct ta_header children; // list of children, with this as sentinel - void (*destructor)(void *); -}; - -// ta_ext_header.children.size is set to this -#define CHILDREN_SENTINEL ((size_t)-1) - static void ta_dbg_add(struct ta_header *h); static void ta_dbg_check_header(struct ta_header *h); static void ta_dbg_remove(struct ta_header *h); @@ -80,61 +76,55 @@ static struct ta_header *get_header(void *ptr) return h; } -static struct ta_ext_header *get_or_alloc_ext_header(void *ptr) -{ - struct ta_header *h = get_header(ptr); - if (!h) - return NULL; - if (!h->ext) { - h->ext = malloc(sizeof(struct ta_ext_header)); - if (!h->ext) - return NULL; - *h->ext = (struct ta_ext_header) { - .header = h, - .children = { - .next = &h->ext->children, - .prev = &h->ext->children, - // Needed by ta_find_parent(): - .size = CHILDREN_SENTINEL, - .ext = h->ext, - }, - }; - } - return h->ext; -} - /* Set the parent allocation of ptr. If parent==NULL, remove the parent. - * Setting parent==NULL (with ptr!=NULL) always succeeds, and unsets the - * parent of ptr. Operations ptr==NULL always succeed and do nothing. - * Returns true on success, false on OOM. + * Setting parent==NULL (with ptr!=NULL) unsets the parent of ptr. + * With ptr==NULL, the function does nothing. * * Warning: if ta_parent is a direct or indirect child of ptr, things will go * wrong. The function will apparently succeed, but creates circular * parent links, which are not allowed. */ -bool ta_set_parent(void *ptr, void *ta_parent) +void ta_set_parent(void *ptr, void *ta_parent) { struct ta_header *ch = get_header(ptr); if (!ch) - return true; - struct ta_ext_header *parent_eh = get_or_alloc_ext_header(ta_parent); - if (ta_parent && !parent_eh) // do nothing on OOM - return false; + return; + struct ta_header *new_parent = get_header(ta_parent); // Unlink from previous parent - if (ch->next) { - ch->next->prev = ch->prev; + if (ch->prev) ch->prev->next = ch->next; - ch->next = ch->prev = NULL; + if (ch->next) + ch->next->prev = ch->prev; + // If ch was the first child, change child link of old parent + if (ch->parent) { + assert(ch->parent->child == ch); + ch->parent->child = ch->next; + if (ch->parent->child) { + assert(!ch->parent->child->parent); + ch->parent->child->parent = ch->parent; + } } - // Link to new parent - insert at end of list (possibly orders destructors) - if (parent_eh) { - struct ta_header *children = &parent_eh->children; - ch->next = children; - ch->prev = children->prev; - children->prev->next = ch; - children->prev = ch; + ch->next = ch->prev = ch->parent = NULL; + // Link to new parent - insert at start of list (LIFO destructor order) + if (new_parent) { + ch->next = new_parent->child; + if (ch->next) { + ch->next->prev = ch; + ch->next->parent = NULL; + } + new_parent->child = ch; + ch->parent = new_parent; } - return true; +} + +/* Return the parent allocation, or NULL if none or if ptr==NULL. + * + * Warning: do not use this for program logic, or I'll be sad. + */ +void *ta_get_parent(void *ptr) +{ + struct ta_header *ch = get_header(ptr); + return ch ? ch->parent : NULL; } /* Allocate size bytes of memory. If ta_parent is not NULL, this is used as @@ -152,10 +142,7 @@ void *ta_alloc_size(void *ta_parent, size_t size) *h = (struct ta_header) {.size = size}; ta_dbg_add(h); void *ptr = PTR_FROM_HEADER(h); - if (!ta_set_parent(ptr, ta_parent)) { - ta_free(ptr); - return NULL; - } + ta_set_parent(ptr, ta_parent); return ptr; } @@ -172,10 +159,7 @@ void *ta_zalloc_size(void *ta_parent, size_t size) *h = (struct ta_header) {.size = size}; ta_dbg_add(h); void *ptr = PTR_FROM_HEADER(h); - if (!ta_set_parent(ptr, ta_parent)) { - ta_free(ptr); - return NULL; - } + ta_set_parent(ptr, ta_parent); return ptr; } @@ -212,17 +196,17 @@ void *ta_realloc_size(void *ta_parent, void *ptr, size_t size) return NULL; h->size = size; if (h != old_h) { - if (h->next) { - // Relink siblings + // Relink parent + if (h->parent) + h->parent->child = h; + // Relink siblings + if (h->next) h->next->prev = h; + if (h->prev) h->prev->next = h; - } - if (h->ext) { - // Relink children - h->ext->header = h; - h->ext->children.next->prev = &h->ext->children; - h->ext->children.prev->next = &h->ext->children; - } + // Relink children + if (h->child) + h->child->parent = h; } return PTR_FROM_HEADER(h); } @@ -243,11 +227,8 @@ size_t ta_get_size(void *ptr) void ta_free_children(void *ptr) { struct ta_header *h = get_header(ptr); - struct ta_ext_header *eh = h ? h->ext : NULL; - if (!eh) - return; - while (eh->children.prev != &eh->children) - ta_free(PTR_FROM_HEADER(eh->children.prev)); + while (h && h->child) + ta_free(PTR_FROM_HEADER(h->child)); } /* Free the given allocation, and all of its direct and indirect children. @@ -257,16 +238,11 @@ void ta_free(void *ptr) struct ta_header *h = get_header(ptr); if (!h) return; - if (h->ext && h->ext->destructor) - h->ext->destructor(ptr); + if (h->destructor) + h->destructor(ptr); ta_free_children(ptr); - if (h->next) { - // Unlink from sibling list - h->next->prev = h->prev; - h->prev->next = h->next; - } + ta_set_parent(ptr, NULL); ta_dbg_remove(h); - free(h->ext); free(h); } @@ -279,39 +255,19 @@ void ta_free(void *ptr) * almost anything, but it must not attempt to free or realloc ptr. The * destructor is run before the allocation's children are freed (also, before * their destructors are run). - * - * Returns false if ptr==NULL, or on OOM. */ -bool ta_set_destructor(void *ptr, void (*destructor)(void *)) -{ - struct ta_ext_header *eh = get_or_alloc_ext_header(ptr); - if (!eh) - return false; - eh->destructor = destructor; - return true; -} - -/* Return the ptr's parent allocation, or NULL if there isn't any. - * - * Warning: this has O(N) runtime complexity with N sibling allocations! - */ -void *ta_find_parent(void *ptr) +void ta_set_destructor(void *ptr, void (*destructor)(void *)) { struct ta_header *h = get_header(ptr); - if (!h || !h->next) - return NULL; - for (struct ta_header *cur = h->next; cur != h; cur = cur->next) { - if (cur->size == CHILDREN_SENTINEL) - return PTR_FROM_HEADER(cur->ext->header); - } - return NULL; + if (h) + h->destructor = destructor; } -#ifdef TA_MEMORY_DEBUGGING +#if TA_MEMORY_DEBUGGING -#include <pthread.h> +#include "osdep/threads.h" -static pthread_mutex_t ta_dbg_mutex = PTHREAD_MUTEX_INITIALIZER; +static mp_static_mutex ta_dbg_mutex = MP_STATIC_MUTEX_INITIALIZER; static bool enable_leak_check; // pretty much constant static struct ta_header leak_node; static char allocation_is_string; @@ -320,29 +276,34 @@ static void ta_dbg_add(struct ta_header *h) { h->canary = CANARY; if (enable_leak_check) { - pthread_mutex_lock(&ta_dbg_mutex); + mp_mutex_lock(&ta_dbg_mutex); h->leak_next = &leak_node; h->leak_prev = leak_node.leak_prev; leak_node.leak_prev->leak_next = h; leak_node.leak_prev = h; - pthread_mutex_unlock(&ta_dbg_mutex); + mp_mutex_unlock(&ta_dbg_mutex); } } static void ta_dbg_check_header(struct ta_header *h) { - if (h) + if (h) { assert(h->canary == CANARY); + if (h->parent) { + assert(!h->prev); + assert(h->parent->child == h); + } + } } static void ta_dbg_remove(struct ta_header *h) { ta_dbg_check_header(h); if (h->leak_next) { // assume checking for !=NULL invariant ok without lock - pthread_mutex_lock(&ta_dbg_mutex); + mp_mutex_lock(&ta_dbg_mutex); h->leak_next->leak_prev = h->leak_prev; h->leak_prev->leak_next = h->leak_next; - pthread_mutex_unlock(&ta_dbg_mutex); + mp_mutex_unlock(&ta_dbg_mutex); h->leak_next = h->leak_prev = NULL; } h->canary = 0; @@ -351,17 +312,14 @@ static void ta_dbg_remove(struct ta_header *h) static size_t get_children_size(struct ta_header *h) { size_t size = 0; - if (h->ext) { - struct ta_header *s; - for (s = h->ext->children.next; s != &h->ext->children; s = s->next) - size += s->size + get_children_size(s); - } + for (struct ta_header *s = h->child; s; s = s->next) + size += s->size + get_children_size(s); return size; } static void print_leak_report(void) { - pthread_mutex_lock(&ta_dbg_mutex); + mp_mutex_lock(&ta_dbg_mutex); if (leak_node.leak_next && leak_node.leak_next != &leak_node) { size_t size = 0; size_t num_blocks = 0; @@ -373,7 +331,7 @@ static void print_leak_report(void) // Don't list those with parent; logically, only parents are listed if (!cur->next) { size_t c_size = get_children_size(cur); - char name[30] = {0}; + char name[50] = {0}; if (cur->name) snprintf(name, sizeof(name), "%s", cur->name); if (cur->name == &allocation_is_string) { @@ -396,19 +354,19 @@ static void print_leak_report(void) } fprintf(stderr, "%zu bytes in %zu blocks.\n", size, num_blocks); } - pthread_mutex_unlock(&ta_dbg_mutex); + mp_mutex_unlock(&ta_dbg_mutex); } void ta_enable_leak_report(void) { - pthread_mutex_lock(&ta_dbg_mutex); + mp_mutex_lock(&ta_dbg_mutex); enable_leak_check = true; if (!leak_node.leak_prev && !leak_node.leak_next) { leak_node.leak_prev = &leak_node; leak_node.leak_next = &leak_node; atexit(print_leak_report); } - pthread_mutex_unlock(&ta_dbg_mutex); + mp_mutex_unlock(&ta_dbg_mutex); } /* Set a (static) string that will be printed if the memory allocation in ptr @@ -50,9 +50,9 @@ void *ta_realloc_size(void *ta_parent, void *ptr, size_t size); size_t ta_get_size(void *ptr); void ta_free(void *ptr); void ta_free_children(void *ptr); -bool ta_set_destructor(void *ptr, void (*destructor)(void *)); -bool ta_set_parent(void *ptr, void *ta_parent); -void *ta_find_parent(void *ptr); +void ta_set_destructor(void *ptr, void (*destructor)(void *)); +void ta_set_parent(void *ptr, void *ta_parent); +void *ta_get_parent(void *ptr); // Utility functions size_t ta_calc_array_size(size_t element_size, size_t count); @@ -106,8 +106,6 @@ bool ta_vasprintf_append_buffer(char **str, const char *fmt, va_list ap) TA_PRF( // code. #define ta_xalloc_size(...) ta_oom_p(ta_alloc_size(__VA_ARGS__)) #define ta_xzalloc_size(...) ta_oom_p(ta_zalloc_size(__VA_ARGS__)) -#define ta_xset_destructor(...) ta_oom_b(ta_set_destructor(__VA_ARGS__)) -#define ta_xset_parent(...) ta_oom_b(ta_set_parent(__VA_ARGS__)) #define ta_xnew_context(...) ta_oom_p(ta_new_context(__VA_ARGS__)) #define ta_xstrdup_append(...) ta_oom_b(ta_strdup_append(__VA_ARGS__)) #define ta_xstrdup_append_buffer(...) ta_oom_b(ta_strdup_append_buffer(__VA_ARGS__)) @@ -128,14 +126,12 @@ bool ta_vasprintf_append_buffer(char **str, const char *fmt, va_list ap) TA_PRF( #define ta_xnew_array_ptrtype(...) ta_oom_g(ta_new_array_ptrtype(__VA_ARGS__)) #define ta_xdup(...) ta_oom_g(ta_dup(__VA_ARGS__)) -#define ta_xsteal(ta_parent, ptr) (TA_TYPEOF(ptr))ta_xsteal_(ta_parent, ptr) #define ta_xrealloc(ta_parent, ptr, type, count) \ (type *)ta_xrealloc_size(ta_parent, ptr, ta_calc_array_size(sizeof(type), count)) // Can't be macros, because the OOM logic is slightly less trivial. char *ta_xstrdup(void *ta_parent, const char *str); char *ta_xstrndup(void *ta_parent, const char *str, size_t n); -void *ta_xsteal_(void *ta_parent, void *ptr); void *ta_xmemdup(void *ta_parent, void *ptr, size_t size); void *ta_xrealloc_size(void *ta_parent, void *ptr, size_t size); @@ -148,9 +144,26 @@ void *ta_xrealloc_size(void *ta_parent, void *ptr, size_t size); #define ta_xrealloc_size(...) ta_dbg_set_loc(ta_xrealloc_size(__VA_ARGS__), TA_LOC) #endif -void ta_oom_b(bool b); -char *ta_oom_s(char *s); -void *ta_oom_p(void *p); +static inline void *ta_oom_p(void *p) +{ + if (!p) + abort(); + return p; +} + +static inline void ta_oom_b(bool b) +{ + if (!b) + abort(); +} + +static inline char *ta_oom_s(char *s) +{ + if (!s) + abort(); + return s; +} + // Generic pointer #define ta_oom_g(ptr) (TA_TYPEOF(ptr))ta_oom_p(ptr) diff --git a/ta/ta_talloc.h b/ta/ta_talloc.h index c2c8ac953d..cacc72ebe4 100644 --- a/ta/ta_talloc.h +++ b/ta/ta_talloc.h @@ -34,11 +34,10 @@ #define talloc_ptrtype ta_xnew_ptrtype #define talloc_array_ptrtype ta_xnew_array_ptrtype -#define talloc_steal ta_xsteal +#define talloc_steal ta_steal #define talloc_realloc_size ta_xrealloc_size #define talloc_new ta_xnew_context -#define talloc_set_destructor ta_xset_destructor -#define talloc_parent ta_find_parent +#define talloc_set_destructor ta_set_destructor #define talloc_enable_leak_report ta_enable_leak_report #define talloc_size ta_xalloc_size #define talloc_zero_size ta_xzalloc_size @@ -80,8 +79,6 @@ char *ta_talloc_asprintf_append_buffer(char *s, const char *fmt, ...) TA_PRF(2, #define TA_FREEP(pctx) do {talloc_free(*(pctx)); *(pctx) = NULL;} while(0) -#define TA_EXPAND_ARGS(...) __VA_ARGS__ - // Return number of allocated entries in typed array p[]. #define MP_TALLOC_AVAIL(p) (talloc_get_size(p) / sizeof((p)[0])) @@ -106,7 +103,7 @@ char *ta_talloc_asprintf_append_buffer(char *s, const char *fmt, ...) TA_PRF(2, #define MP_TARRAY_APPEND(ctx, p, idxvar, ...) \ do { \ MP_TARRAY_GROW(ctx, p, idxvar); \ - (p)[(idxvar)] = (TA_EXPAND_ARGS(__VA_ARGS__));\ + (p)[(idxvar)] = (__VA_ARGS__); \ (idxvar)++; \ } while (0) @@ -121,7 +118,7 @@ char *ta_talloc_asprintf_append_buffer(char *s, const char *fmt, ...) TA_PRF(2, memmove((p) + at_ + 1, (p) + at_, \ ((idxvar) - at_) * sizeof((p)[0])); \ (idxvar)++; \ - (p)[at_] = (TA_EXPAND_ARGS(__VA_ARGS__)); \ + (p)[at_] = (__VA_ARGS__); \ } while (0) // Given an array p with count idxvar, insert c elements at p[at], so that @@ -157,7 +154,4 @@ char *ta_talloc_asprintf_append_buffer(char *s, const char *fmt, ...) TA_PRF(2, : false \ ) -#define talloc_struct(ctx, type, ...) \ - talloc_memdup(ctx, &(type) TA_EXPAND_ARGS(__VA_ARGS__), sizeof(type)) - #endif diff --git a/ta/ta_utils.c b/ta/ta_utils.c index cc122b58a2..294ad8d7f2 100644 --- a/ta/ta_utils.c +++ b/ta/ta_utils.c @@ -44,32 +44,19 @@ size_t ta_calc_prealloc_elems(size_t nextidx) return (nextidx + 1) * 2; } -static void dummy_dtor(void *p){} - -/* Create an empty (size 0) TA allocation, which is prepared in a way such that - * using it as parent with ta_set_parent() always succeed. Calling - * ta_set_destructor() on it will always succeed as well. +/* Create an empty (size 0) TA allocation. */ void *ta_new_context(void *ta_parent) { - void *new = ta_alloc_size(ta_parent, 0); - // Force it to allocate an extended header. - if (!ta_set_destructor(new, dummy_dtor)) { - ta_free(new); - new = NULL; - } - return new; + return ta_alloc_size(ta_parent, 0); } /* Set parent of ptr to ta_parent, return the ptr. * Note that ta_parent==NULL will simply unset the current parent of ptr. - * If the operation fails (on OOM), return NULL. (That's pretty bad behavior, - * but the only way to signal failure.) */ void *ta_steal_(void *ta_parent, void *ptr) { - if (!ta_set_parent(ptr, ta_parent)) - return NULL; + ta_set_parent(ptr, ta_parent); return ptr; } @@ -138,10 +125,7 @@ char *ta_strndup(void *ta_parent, const char *str, size_t n) return NULL; char *new = NULL; strndup_append_at(&new, 0, str, n); - if (!ta_set_parent(new, ta_parent)) { - ta_free(new); - new = NULL; - } + ta_set_parent(new, ta_parent); return new; } @@ -229,7 +213,8 @@ char *ta_vasprintf(void *ta_parent, const char *fmt, va_list ap) { char *res = NULL; ta_vasprintf_append_at(&res, 0, fmt, ap); - if (!res || !ta_set_parent(res, ta_parent)) { + ta_set_parent(res, ta_parent); + if (!res) { ta_free(res); return NULL; } @@ -280,33 +265,6 @@ bool ta_vasprintf_append_buffer(char **str, const char *fmt, va_list ap) return ta_vasprintf_append_at(str, size, fmt, ap); } - -void *ta_oom_p(void *p) -{ - if (!p) - abort(); - return p; -} - -void ta_oom_b(bool b) -{ - if (!b) - abort(); -} - -char *ta_oom_s(char *s) -{ - if (!s) - abort(); - return s; -} - -void *ta_xsteal_(void *ta_parent, void *ptr) -{ - ta_oom_b(ta_set_parent(ptr, ta_parent)); - return ptr; -} - void *ta_xmemdup(void *ta_parent, void *ptr, size_t size) { void *new = ta_memdup(ta_parent, ptr, size); |