diff options
author | 2010-01-08 06:11:00 +0000 | |
---|---|---|
committer | 2010-01-08 06:11:00 +0000 | |
commit | ab68710ce58d99c5275cdbbaefd212a346e819a5 (patch) | |
tree | aba282b9168009b449480b902fd649163faeb07c /src/emu/emualloc.c | |
parent | 91a1b8d634131714ec89a07496606880ea487ece (diff) |
Oops, forgot these files.
Diffstat (limited to 'src/emu/emualloc.c')
-rw-r--r-- | src/emu/emualloc.c | 601 |
1 files changed, 601 insertions, 0 deletions
diff --git a/src/emu/emualloc.c b/src/emu/emualloc.c new file mode 100644 index 00000000000..c053a0f8d69 --- /dev/null +++ b/src/emu/emualloc.c @@ -0,0 +1,601 @@ +/*************************************************************************** + + emualloc.c + + Memory allocation helpers for the core emulator. + +**************************************************************************** + + Copyright Aaron Giles + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + * Neither the name 'MAME' nor the names of its contributors may be + used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY AARON GILES ''AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL AARON GILES BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + +***************************************************************************/ + +#include "emucore.h" +#include "coreutil.h" + + +/*************************************************************************** + CONSTANTS +***************************************************************************/ + +// align all allocated memory to this size +const int memory_align = 16; + +// number of memory_entries to allocate in a block +const int memory_block_alloc_chunk = 256; + + + +/*************************************************************************** + TYPE DEFINITIONS +***************************************************************************/ + +// this struct is allocated in pools to track memory allocations +// it must be a POD type!! +class memory_entry +{ +public: + memory_entry * next; // link to the next entry + memory_entry * prev; // link to the previous entry + size_t size; // size of the allocation (not including this header) + void * base; // base of the allocation + const char * file; // file the allocation was made from + int line; // line number within that file + int id; // unique id + + static const int hash_prime = 193; + + static int curid; // current ID + static osd_lock * lock; // lock for managing the list + static bool lock_alloc; // set to true temporarily during lock allocation + static memory_entry *hash[hash_prime];// hash table based on pointer + static memory_entry *freehead; // pointer to the head of the free list + + static memory_entry *allocate(size_t size, void *base, const char *file, int line); + static memory_entry *find(void *ptr); + static void release(memory_entry *entry); + static void report_unfreed(); + +private: + static void acquire_lock(); + static void release_lock(); +}; + + + +/*************************************************************************** + GLOBALS +***************************************************************************/ + +// global resource pool to handle allocations outside of the emulator context +resource_pool global_resource_pool; + +// dummy zeromem object +const zeromem_t zeromem = { }; + +// globals for memory_entry +int memory_entry::curid = 0; +osd_lock *memory_entry::lock = NULL; +bool memory_entry::lock_alloc = false; +memory_entry *memory_entry::hash[memory_entry::hash_prime] = { NULL }; +memory_entry *memory_entry::freehead = NULL; + + + +/*************************************************************************** + GLOBAL HELPERS +***************************************************************************/ + +/*------------------------------------------------- + malloc_file_line - allocate memory with file + and line number information +-------------------------------------------------*/ + +void *malloc_file_line(size_t size, const char *file, int line) +{ + // allocate the memory and fail if we can't + void *result = osd_malloc(size); + if (result == NULL) + return NULL; + +#ifdef MAME_DEBUG + // add a new entry + memory_entry::allocate(size, result, file, line); + + // randomize the memory + rand_memory(result, size); +#endif + + return result; +} + + +/*------------------------------------------------- + free_file_line - free memory with file + and line number information +-------------------------------------------------*/ + +void free_file_line(void *memory, const char *file, int line) +{ +#ifdef MAME_DEBUG + // find the memory entry + memory_entry *entry = memory_entry::find(memory); + + // warn about untracked frees + if (entry == NULL) + { + fprintf(stderr, "Error: attempt to free untracked memory!\n"); + osd_break_into_debugger("Error: attempt to free untracked memory"); + return; + } + + // free the entry and the memory + if (entry != NULL) + memory_entry::release(entry); +#endif + + osd_free(memory); +} + + +/*------------------------------------------------- + dump_unfreed_mem - called from the exit path + of any code that wants to check for unfreed + memory +-------------------------------------------------*/ + +void dump_unfreed_mem(void) +{ +#ifdef MAME_DEBUG + memory_entry::report_unfreed(); +#endif +} + + + +/*************************************************************************** + RESOURCE POOL +***************************************************************************/ + +/*------------------------------------------------- + resource_pool - constructor for a new resource + pool +-------------------------------------------------*/ + +resource_pool::resource_pool() + : listlock(osd_lock_alloc()) +{ + memset(hash, 0, sizeof(hash)); +} + + +/*------------------------------------------------- + ~resource_pool - destructor for a resource + pool; make sure all tracked objects are + deleted +-------------------------------------------------*/ + +resource_pool::~resource_pool() +{ + clear(); + if (listlock != NULL) + osd_lock_free(listlock); +} + + +/*------------------------------------------------- + add - add a new item to the resource pool +-------------------------------------------------*/ + +void resource_pool::add(resource_pool_item &item) +{ + osd_lock_acquire(listlock); + int hashval = reinterpret_cast<FPTR>(item.ptr) % hash_prime; + item.next = hash[hashval]; + hash[hashval] = &item; + osd_lock_release(listlock); +} + + +/*------------------------------------------------- + remove - remove a specific item from the + resource pool +-------------------------------------------------*/ + +void resource_pool::remove(void *ptr) +{ + // ignore NULLs + if (ptr == NULL) + return; + + // search for the item + osd_lock_acquire(listlock); + + int hashval = reinterpret_cast<FPTR>(ptr) % hash_prime; + for (resource_pool_item **scanptr = &hash[hashval]; *scanptr != NULL; scanptr = &(*scanptr)->next) + + // must match the pointer + if ((*scanptr)->ptr == ptr) + { + resource_pool_item *deleteme = *scanptr; + *scanptr = deleteme->next; + delete deleteme; + break; + } + + osd_lock_release(listlock); +} + + +/*------------------------------------------------- + find - find a specific item in the resource + pool +-------------------------------------------------*/ + +resource_pool_item *resource_pool::find(void *ptr) +{ + // search for the item + osd_lock_acquire(listlock); + + int hashval = reinterpret_cast<FPTR>(ptr) % hash_prime; + resource_pool_item *item; + for (item = hash[hashval]; item != NULL; item = item->next) + if (item->ptr == ptr) + break; + + osd_lock_release(listlock); + + return item; +} + + +/*------------------------------------------------- + contains - return true if given ptr is + contained by one of the objects in the pool +-------------------------------------------------*/ + +bool resource_pool::contains(void *_ptrstart, void *_ptrend) +{ + UINT8 *ptrstart = reinterpret_cast<UINT8 *>(_ptrstart); + UINT8 *ptrend = reinterpret_cast<UINT8 *>(_ptrend); + + // search for the item + osd_lock_acquire(listlock); + + resource_pool_item *item = NULL; + for (int hashval = 0; hashval < hash_prime; hashval++) + for (item = hash[hashval]; item != NULL; item = item->next) + { + UINT8 *objstart = reinterpret_cast<UINT8 *>(item->ptr); + UINT8 *objend = objstart + item->size; + if (ptrstart >= objstart && ptrend <= objend) + goto found; + } + +found: + osd_lock_release(listlock); + + return (item != NULL); +} + + +/*------------------------------------------------- + clear - remove all items from a resource pool +-------------------------------------------------*/ + +void resource_pool::clear() +{ + osd_lock_acquire(listlock); + for (int hashval = 0; hashval < hash_prime; hashval++) + while (hash[hashval] != NULL) + { + resource_pool_item *deleteme = hash[hashval]; + hash[hashval] = deleteme->next; + delete deleteme; + } + osd_lock_release(listlock); +} + + + +/*************************************************************************** + MEMORY ENTRY +***************************************************************************/ + +/*------------------------------------------------- + acquire_lock - acquire the memory entry lock, + creating a new one if needed +-------------------------------------------------*/ + +void memory_entry::acquire_lock() +{ + // allocate a lock on first usage + // note that osd_lock_alloc() may re-enter this path, so protect against recursion! + if (lock == NULL) + { + if (lock_alloc) + return; + lock_alloc = true; + lock = osd_lock_alloc(); + lock_alloc = false; + } + osd_lock_acquire(lock); +} + + +/*------------------------------------------------- + release_lock - release the memory entry lock +-------------------------------------------------*/ + +void memory_entry::release_lock() +{ + osd_lock_release(lock); +} + + +/*------------------------------------------------- + allocate - allocate a new memory entry +-------------------------------------------------*/ + +memory_entry *memory_entry::allocate(size_t size, void *base, const char *file, int line) +{ + acquire_lock(); + + // if we're out of free entries, allocate a new chunk + if (freehead == NULL) + { + // create a new chunk, and fail if we can't + memory_entry *entry = reinterpret_cast<memory_entry *>(osd_malloc(memory_block_alloc_chunk * sizeof(memory_entry))); + if (entry == NULL) + { + release_lock(); + return NULL; + } + + // add all the entries to the list + for (int entrynum = 0; entrynum < memory_block_alloc_chunk; entrynum++) + { + entry->next = freehead; + freehead = entry++; + } + } + + // grab a free entry + memory_entry *entry = freehead; + freehead = entry->next; + + // populate it + entry->size = size; + entry->base = base; + entry->file = file; + entry->line = line; + entry->id = curid++; + + // add it to the alloc list + int hashval = reinterpret_cast<FPTR>(base) % hash_prime; + entry->next = hash[hashval]; + if (entry->next != NULL) + entry->next->prev = entry; + entry->prev = NULL; + hash[hashval] = entry; + + release_lock(); + return entry; +} + + +/*------------------------------------------------- + find - find a memory entry +-------------------------------------------------*/ + +memory_entry *memory_entry::find(void *ptr) +{ + // NULL maps to nothing + if (ptr == NULL) + return NULL; + + // scan the list under the lock + acquire_lock(); + + int hashval = reinterpret_cast<FPTR>(ptr) % hash_prime; + memory_entry *entry; + for (entry = hash[hashval]; entry != NULL; entry = entry->next) + if (entry->base == ptr) + break; + + release_lock(); + return entry; +} + + +/*------------------------------------------------- + release - release a memory entry +-------------------------------------------------*/ + +void memory_entry::release(memory_entry *entry) +{ + acquire_lock(); + + // remove ourselves from the alloc list + int hashval = reinterpret_cast<FPTR>(entry->base) % hash_prime; + if (entry->prev != NULL) + entry->prev->next = entry->next; + else + hash[hashval] = entry->next; + if (entry->next != NULL) + entry->next->prev = entry->prev; + + // add ourself to the free list + entry->next = freehead; + freehead = entry; + + release_lock(); +} + + +/*------------------------------------------------- + report_unfreed - print a list of unfreed + memory to the target file +-------------------------------------------------*/ + +void memory_entry::report_unfreed() +{ + acquire_lock(); + + // check for leaked memory + UINT32 total = 0; + for (int hashval = 0; hashval < hash_prime; hashval++) + for (memory_entry *entry = hash[hashval]; entry; entry = entry->next) + if (entry->file != NULL) + { + if (total == 0) + fprintf(stderr, "--- memory leak warning ---\n"); + total += entry->size; + fprintf(stderr, "allocation #%06d, %d bytes (%s:%d)\n", entry->id, static_cast<UINT32>(entry->size), entry->file, (int)entry->line); + } + + release_lock(); + + if (total > 0) + fprintf(stderr, "a total of %d bytes were not free()'d\n", total); +} + + + +/*************************************************************************** + STANDARD NEW/DELETE OPERATORS +***************************************************************************/ + +void *operator new(std::size_t size) throw (std::bad_alloc) +{ + void *result = malloc_file_line(size, NULL, 0); + if (result == NULL) + throw std::bad_alloc(); + return result; +} + + +void *operator new[](std::size_t size) throw (std::bad_alloc) +{ + void *result = malloc_file_line(size, NULL, 0); + if (result == NULL) + throw std::bad_alloc(); + return result; +} + + +void operator delete(void *ptr) +{ + if (ptr != NULL) + free_file_line(ptr, NULL, 0); +} + + +void operator delete[](void *ptr) +{ + if (ptr != NULL) + free_file_line(ptr, NULL, 0); +} + + + +/*************************************************************************** + POOL NEW/DELETE OPERATORS +***************************************************************************/ + +void *operator new(std::size_t size, const char *file, int line) throw (std::bad_alloc) +{ + void *result = malloc_file_line(size, file, line); + if (result == NULL) + throw std::bad_alloc(); + return result; +} + + +void *operator new[](std::size_t size, const char *file, int line) throw (std::bad_alloc) +{ + void *result = malloc_file_line(size, file, line); + if (result == NULL) + throw std::bad_alloc(); + return result; +} + + +void operator delete(void *ptr, const char *file, int line) +{ + if (ptr != NULL) + free_file_line(ptr, file, line); +} + + +void operator delete[](void *ptr, const char *file, int line) +{ + if (ptr != NULL) + free_file_line(ptr, file, line); +} + + + +/*************************************************************************** + POOL NEW/DELETE OPERATORS WITH ZEROING +***************************************************************************/ + +void *operator new(std::size_t size, const char *file, int line, const zeromem_t &) throw (std::bad_alloc) +{ + void *result = malloc_file_line(size, file, line); + if (result == NULL) + throw std::bad_alloc(); + memset(result, 0, size); + return result; +} + + +void *operator new[](std::size_t size, const char *file, int line, const zeromem_t &) throw (std::bad_alloc) +{ + void *result = malloc_file_line(size, file, line); + if (result == NULL) + throw std::bad_alloc(); + memset(result, 0, size); + return result; +} + + +void operator delete(void *ptr, const char *file, int line, const zeromem_t &) +{ + if (ptr != NULL) + free_file_line(ptr, file, line); +} + + +void operator delete[](void *ptr, const char *file, int line, const zeromem_t &) +{ + if (ptr != NULL) + free_file_line(ptr, file, line); +} |