From 68bc62a7fa5febbf5c8ab2fe8f6171121d18690f Mon Sep 17 00:00:00 2001 From: Joshua Haberman Date: Tue, 19 Apr 2016 14:57:31 -0700 Subject: Split upb::Arena/upb::Allocator from upb::Environment. (#58) * Split upb::Arena/upb::Allocator from upb::Environment. This will allow arenas and allocators to be used independently of environments, which will be important for an upcoming change (a message representation). Overall this design feels cleaner that the previous Environment/SeededAllocator design. As part of this change, moved all allocations in upb to use a global allocator instead of hard-coding malloc/free. This will allow injecting OOM faults for more robust testing. One place that doesn't use the global allocator is the tracked ref code. Instead of its previous approach of CHECK_OOM() after every malloc() or table insert, it simply uses an allocator that does this automatically. I moved Allocator/Arena/Environment into upb.h. This seems principled since these are the only types in upb whose size is directly exposed to users, since they form the basis of memory allocation strategy. * Cleaned up some header includes and fixed more malloc -> upb_gmalloc(). * Changes from PR review. * Don't use UINTPTR_MAX or UINT64_MAX. * Punt on adding line/file for now. * We actually can't store (uint64_t)-1, update comment and test. --- upb/env.c | 273 -------------------------------------------------------------- 1 file changed, 273 deletions(-) delete mode 100644 upb/env.c (limited to 'upb/env.c') diff --git a/upb/env.c b/upb/env.c deleted file mode 100644 index 973472e..0000000 --- a/upb/env.c +++ /dev/null @@ -1,273 +0,0 @@ - -#include "upb/env.h" - -#include -#include -#include - -typedef struct cleanup_ent { - upb_cleanup_func *cleanup; - void *ud; - struct cleanup_ent *next; -} cleanup_ent; - -static void *seeded_alloc(void *ud, void *ptr, size_t oldsize, size_t size); - -/* Default allocator **********************************************************/ - -/* Just use realloc, keeping all allocated blocks in a linked list to destroy at - * the end. */ - -typedef struct mem_block { - /* List is doubly-linked, because in cases where realloc() moves an existing - * block, we need to be able to remove the old pointer from the list - * efficiently. */ - struct mem_block *prev, *next; -#ifndef NDEBUG - size_t size; /* Doesn't include mem_block structure. */ -#endif -} mem_block; - -typedef struct { - mem_block *head; -} default_alloc_ud; - -static void *default_alloc(void *_ud, void *ptr, size_t oldsize, size_t size) { - default_alloc_ud *ud = _ud; - mem_block *from, *block; - void *ret; - UPB_UNUSED(oldsize); - - from = ptr ? (void*)((char*)ptr - sizeof(mem_block)) : NULL; - -#ifndef NDEBUG - if (from) { - assert(oldsize <= from->size); - } -#endif - - /* TODO(haberman): we probably need to provide even better alignment here, - * like 16-byte alignment of the returned data pointer. */ - block = realloc(from, size + sizeof(mem_block)); - if (!block) return NULL; - ret = (char*)block + sizeof(*block); - -#ifndef NDEBUG - block->size = size; -#endif - - if (from) { - if (block != from) { - /* The block was moved, so pointers in next and prev blocks must be - * updated to its new location. */ - if (block->next) block->next->prev = block; - if (block->prev) block->prev->next = block; - if (ud->head == from) ud->head = block; - } - } else { - /* Insert at head of linked list. */ - block->prev = NULL; - block->next = ud->head; - if (block->next) block->next->prev = block; - ud->head = block; - } - - return ret; -} - -static void default_alloc_cleanup(void *_ud) { - default_alloc_ud *ud = _ud; - mem_block *block = ud->head; - - while (block) { - void *to_free = block; - block = block->next; - free(to_free); - } -} - - -/* Standard error functions ***************************************************/ - -static bool default_err(void *ud, const upb_status *status) { - UPB_UNUSED(ud); - UPB_UNUSED(status); - return false; -} - -static bool write_err_to(void *ud, const upb_status *status) { - upb_status *copy_to = ud; - upb_status_copy(copy_to, status); - return false; -} - - -/* upb_env ********************************************************************/ - -void upb_env_init(upb_env *e) { - default_alloc_ud *ud = (default_alloc_ud*)&e->default_alloc_ud; - e->ok_ = true; - e->bytes_allocated = 0; - e->cleanup_head = NULL; - - ud->head = NULL; - - /* Set default functions. */ - upb_env_setallocfunc(e, default_alloc, ud); - upb_env_seterrorfunc(e, default_err, NULL); -} - -void upb_env_uninit(upb_env *e) { - cleanup_ent *ent = e->cleanup_head; - - while (ent) { - ent->cleanup(ent->ud); - ent = ent->next; - } - - /* Must do this after running cleanup functions, because this will delete - the memory we store our cleanup entries in! */ - if (e->alloc == default_alloc) { - default_alloc_cleanup(e->alloc_ud); - } -} - -UPB_FORCEINLINE void upb_env_setallocfunc(upb_env *e, upb_alloc_func *alloc, - void *ud) { - e->alloc = alloc; - e->alloc_ud = ud; -} - -UPB_FORCEINLINE void upb_env_seterrorfunc(upb_env *e, upb_error_func *func, - void *ud) { - e->err = func; - e->err_ud = ud; -} - -void upb_env_reporterrorsto(upb_env *e, upb_status *status) { - e->err = write_err_to; - e->err_ud = status; -} - -bool upb_env_ok(const upb_env *e) { - return e->ok_; -} - -bool upb_env_reporterror(upb_env *e, const upb_status *status) { - e->ok_ = false; - return e->err(e->err_ud, status); -} - -bool upb_env_addcleanup(upb_env *e, upb_cleanup_func *func, void *ud) { - cleanup_ent *ent = upb_env_malloc(e, sizeof(cleanup_ent)); - if (!ent) return false; - - ent->cleanup = func; - ent->ud = ud; - ent->next = e->cleanup_head; - e->cleanup_head = ent; - - return true; -} - -void *upb_env_malloc(upb_env *e, size_t size) { - e->bytes_allocated += size; - if (e->alloc == seeded_alloc) { - /* This is equivalent to the next branch, but allows inlining for a - * measurable perf benefit. */ - return seeded_alloc(e->alloc_ud, NULL, 0, size); - } else { - return e->alloc(e->alloc_ud, NULL, 0, size); - } -} - -void *upb_env_realloc(upb_env *e, void *ptr, size_t oldsize, size_t size) { - char *ret; - assert(oldsize <= size); - ret = e->alloc(e->alloc_ud, ptr, oldsize, size); - -#ifndef NDEBUG - /* Overwrite non-preserved memory to ensure callers are passing the oldsize - * that they truly require. */ - memset(ret + oldsize, 0xff, size - oldsize); -#endif - - return ret; -} - -size_t upb_env_bytesallocated(const upb_env *e) { - return e->bytes_allocated; -} - - -/* upb_seededalloc ************************************************************/ - -/* Be conservative and choose 16 in case anyone is using SSE. */ -static const size_t maxalign = 16; - -static size_t align_up(size_t size) { - return ((size + maxalign - 1) / maxalign) * maxalign; -} - -UPB_FORCEINLINE static void *seeded_alloc(void *ud, void *ptr, size_t oldsize, - size_t size) { - upb_seededalloc *a = ud; - - size = align_up(size); - - assert(a->mem_limit >= a->mem_ptr); - - if (oldsize == 0 && size <= (size_t)(a->mem_limit - a->mem_ptr)) { - /* Fast path: we can satisfy from the initial allocation. */ - void *ret = a->mem_ptr; - a->mem_ptr += size; - return ret; - } else { - char *chptr = ptr; - /* Slow path: fallback to other allocator. */ - a->need_cleanup = true; - /* Is `ptr` part of the user-provided initial block? Don't pass it to the - * default allocator if so; otherwise, it may try to realloc() the block. */ - if (chptr >= a->mem_base && chptr < a->mem_limit) { - void *ret; - assert(chptr + oldsize <= a->mem_limit); - ret = a->alloc(a->alloc_ud, NULL, 0, size); - if (ret) memcpy(ret, ptr, oldsize); - return ret; - } else { - return a->alloc(a->alloc_ud, ptr, oldsize, size); - } - } -} - -void upb_seededalloc_init(upb_seededalloc *a, void *mem, size_t len) { - default_alloc_ud *ud = (default_alloc_ud*)&a->default_alloc_ud; - a->mem_base = mem; - a->mem_ptr = mem; - a->mem_limit = (char*)mem + len; - a->need_cleanup = false; - a->returned_allocfunc = false; - - ud->head = NULL; - - upb_seededalloc_setfallbackalloc(a, default_alloc, ud); -} - -void upb_seededalloc_uninit(upb_seededalloc *a) { - if (a->alloc == default_alloc && a->need_cleanup) { - default_alloc_cleanup(a->alloc_ud); - } -} - -UPB_FORCEINLINE void upb_seededalloc_setfallbackalloc(upb_seededalloc *a, - upb_alloc_func *alloc, - void *ud) { - assert(!a->returned_allocfunc); - a->alloc = alloc; - a->alloc_ud = ud; -} - -upb_alloc_func *upb_seededalloc_getallocfunc(upb_seededalloc *a) { - a->returned_allocfunc = true; - return seeded_alloc; -} -- cgit v1.2.3