/* * Copyright (C) 2008 Philippe Gerum . * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ #include #include #include #include #include #include #include "boilerplate/lock.h" #include "copperplate/heapobj.h" #include "copperplate/debug.h" #define MALLOC_MAGIC 0xabbfcddc struct pool_header { pthread_mutex_t lock; size_t used; }; struct block_header { unsigned int magic; size_t size; }; int __heapobj_init_private(struct heapobj *hobj, const char *name, size_t size, void *mem) { pthread_mutexattr_t mattr; struct pool_header *ph; int ret; /* * There is no local pool when working with malloc, we just * use the global process arena. This should not be an issue * since this mode is aimed at debugging, particularly to be * used along with Valgrind. * * However, we maintain a control header to track the amount * of memory currently consumed in each heap. */ ph = malloc(sizeof(*ph)); if (ph == NULL) return __bt(-ENOMEM); pthread_mutexattr_init(&mattr); pthread_mutexattr_settype(&mattr, mutex_type_attribute); pthread_mutexattr_setprotocol(&mattr, PTHREAD_PRIO_INHERIT); pthread_mutexattr_setpshared(&mattr, PTHREAD_PROCESS_PRIVATE); ret = __bt(-__RT(pthread_mutex_init(&ph->lock, &mattr))); pthread_mutexattr_destroy(&mattr); if (ret) { free(ph); return ret; } ph->used = 0; hobj->pool = ph; hobj->size = size; if (name) snprintf(hobj->name, sizeof(hobj->name), "%s", name); else snprintf(hobj->name, sizeof(hobj->name), "%p", hobj); return 0; } int heapobj_init_array_private(struct heapobj *hobj, const char *name, size_t size, int elems) { if (size == 0 || elems <= 0) return __bt(-EINVAL); return __bt(__heapobj_init_private(hobj, name, size * elems, NULL)); } void pvheapobj_destroy(struct heapobj *hobj) { struct pool_header *ph = hobj->pool; __RT(pthread_mutex_destroy(&ph->lock)); free(ph); } int pvheapobj_extend(struct heapobj *hobj, size_t size, void *mem) { struct pool_header *ph = hobj->pool; write_lock_nocancel(&ph->lock); hobj->size += size; write_unlock(&ph->lock); return 0; } void *pvheapobj_alloc(struct heapobj *hobj, size_t size) { struct pool_header *ph = hobj->pool; struct block_header *bh; void *ptr; write_lock(&ph->lock); ph->used += size; /* Enforce hard limit. */ if (ph->used > hobj->size) goto fail; write_unlock(&ph->lock); /* malloc(3) is not a cancellation point. */ ptr = malloc(size + sizeof(*bh)); if (ptr == NULL) { write_lock(&ph->lock); goto fail; } bh = ptr; bh->magic = MALLOC_MAGIC; bh->size = size; return bh + 1; fail: ph->used -= size; write_unlock(&ph->lock); return NULL; } void pvheapobj_free(struct heapobj *hobj, void *ptr) { struct block_header *bh = ptr - sizeof(*bh); struct pool_header *ph = hobj->pool; assert(hobj->size >= bh->size); write_lock(&ph->lock); ph->used -= bh->size; write_unlock(&ph->lock); free(bh); } size_t pvheapobj_inquire(struct heapobj *hobj) { struct pool_header *ph = hobj->pool; return ph->used; } size_t pvheapobj_validate(struct heapobj *hobj, void *ptr) { struct block_header *bh; /* Catch trivially wrong cases: NULL or unaligned. */ if (ptr == NULL) return 0; if ((unsigned long)ptr & (sizeof(unsigned long)-1)) return 0; /* * We will likely get hard validation here, i.e. crash or * abort if the pointer is out of the address space. TLSF is a * bit smarter, and pshared definitely does the right thing. */ bh = ptr - sizeof(*bh); if (bh->magic != MALLOC_MAGIC) return 0; return bh->size; } int heapobj_pkg_init_private(void) { return 0; }