summaryrefslogtreecommitdiffstatshomepage
path: root/trunk/src/emu/emualloc.c
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/src/emu/emualloc.c')
-rw-r--r--trunk/src/emu/emualloc.c614
1 files changed, 614 insertions, 0 deletions
diff --git a/trunk/src/emu/emualloc.c b/trunk/src/emu/emualloc.c
new file mode 100644
index 00000000000..105e4f14eda
--- /dev/null
+++ b/trunk/src/emu/emualloc.c
@@ -0,0 +1,614 @@
+/***************************************************************************
+
+ 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"
+
+
+//**************************************************************************
+// DEBUGGING
+//**************************************************************************
+
+#define LOG_ALLOCS (0)
+
+
+
+//**************************************************************************
+// 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;
+
+
+
+//**************************************************************************
+// MACROS
+//**************************************************************************
+
+// enable deletion
+#undef delete
+
+
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+// this struct is allocated in pools to track memory allocations
+// it must be a POD type!!
+class memory_entry
+{
+public:
+ memory_entry * m_next; // link to the next entry
+ memory_entry * m_prev; // link to the previous entry
+ size_t m_size; // size of the allocation (not including this header)
+ void * m_base; // base of the allocation
+ const char * m_file; // file the allocation was made from
+ int m_line; // line number within that file
+ UINT64 m_id; // unique id
+
+ static const int k_hash_prime = 6151;
+
+ static UINT64 s_curid; // current ID
+ static osd_lock * s_lock; // lock for managing the list
+ static bool s_lock_alloc; // set to true temporarily during lock allocation
+ static bool s_tracking; // set to true when tracking is live
+ static memory_entry *s_hash[k_hash_prime];// hash table based on pointer
+ static memory_entry *s_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
+//**************************************************************************
+
+// dummy zeromem object
+const zeromem_t zeromem = { };
+
+// globals for memory_entry
+UINT64 memory_entry::s_curid = 1;
+osd_lock *memory_entry::s_lock = NULL;
+bool memory_entry::s_lock_alloc = false;
+bool memory_entry::s_tracking = false;
+memory_entry *memory_entry::s_hash[memory_entry::k_hash_prime] = { NULL };
+memory_entry *memory_entry::s_freehead = NULL;
+
+// wrapper for the global resource pool to help ensure construction order
+resource_pool &global_resource_pool()
+{
+ static resource_pool s_pool(6151);
+ return s_pool;
+};
+
+
+//**************************************************************************
+// 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;
+
+ // add a new entry
+ memory_entry::allocate(size, result, file, line);
+
+#ifdef MAME_DEBUG
+ // randomize the memory
+ rand_memory(result, size);
+#endif
+
+ return result;
+}
+
+
+//-------------------------------------------------
+// malloc_array_file_line - allocate memory with
+// file and line number information, and a hint
+// that this object is an array
+//-------------------------------------------------
+
+void *malloc_array_file_line(size_t size, const char *file, int line)
+{
+ // allocate the memory and fail if we can't
+ void *result = osd_malloc_array(size);
+ if (result == NULL)
+ return NULL;
+
+ // add a new entry
+ memory_entry::allocate(size, result, file, line);
+
+#ifdef MAME_DEBUG
+ // 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)
+{
+ // ignore NULL frees/deletes
+ if (memory == NULL)
+ return;
+
+ // 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 in %s(%d)!\n", file, line);
+ osd_break_into_debugger("Error: attempt to free untracked memory");
+ return;
+ }
+
+#ifdef MAME_DEBUG
+ // clear memory to a bogus value
+ memset(memory, 0xfc, entry->m_size);
+#endif
+
+ // free the entry and the memory
+ memory_entry::release(entry);
+ osd_free(memory);
+}
+
+
+//-------------------------------------------------
+// dump_unfreed_mem - called from the exit path
+// of any code that wants to check for unfreed
+// memory
+//-------------------------------------------------
+
+void track_memory(bool track)
+{
+ memory_entry::s_tracking = track;
+}
+
+
+//-------------------------------------------------
+// dump_unfreed_mem - called from the exit path
+// of any code that wants to check for unfreed
+// memory
+//-------------------------------------------------
+
+void dump_unfreed_mem()
+{
+#ifdef MAME_DEBUG
+ memory_entry::report_unfreed();
+#endif
+}
+
+
+
+//**************************************************************************
+// RESOURCE POOL
+//**************************************************************************
+
+//-------------------------------------------------
+// resource_pool - constructor for a new resource
+// pool
+//-------------------------------------------------
+
+resource_pool::resource_pool(int hash_size)
+ : m_hash_size(hash_size),
+ m_listlock(osd_lock_alloc()),
+ m_hash(new resource_pool_item *[hash_size]),
+ m_ordered_head(NULL),
+ m_ordered_tail(NULL)
+{
+ memset(m_hash, 0, hash_size * sizeof(m_hash[0]));
+}
+
+
+//-------------------------------------------------
+// ~resource_pool - destructor for a resource
+// pool; make sure all tracked objects are
+// deleted
+//-------------------------------------------------
+
+resource_pool::~resource_pool()
+{
+ clear();
+ if (m_listlock != NULL)
+ osd_lock_free(m_listlock);
+ delete[] m_hash;
+}
+
+
+//-------------------------------------------------
+// add - add a new item to the resource pool
+//-------------------------------------------------
+
+void resource_pool::add(resource_pool_item &item)
+{
+ osd_lock_acquire(m_listlock);
+
+ // insert into hash table
+ int hashval = reinterpret_cast<FPTR>(item.m_ptr) % m_hash_size;
+ item.m_next = m_hash[hashval];
+ m_hash[hashval] = &item;
+
+ // fetch the ID of this item's pointer; some implementations put hidden data
+ // before, so if we don't find it, check 4 bytes ahead
+ memory_entry *entry = memory_entry::find(item.m_ptr);
+ if (entry == NULL)
+ entry = memory_entry::find(reinterpret_cast<UINT8 *>(item.m_ptr) - sizeof(size_t));
+ assert(entry != NULL);
+ item.m_id = entry->m_id;
+ if (LOG_ALLOCS)
+ fprintf(stderr, "#%06d, add %d bytes (%s:%d)\n", (UINT32)entry->m_id, static_cast<UINT32>(entry->m_size), entry->m_file, (int)entry->m_line);
+
+ // find the entry to insert after
+ resource_pool_item *insert_after;
+ for (insert_after = m_ordered_tail; insert_after != NULL; insert_after = insert_after->m_ordered_prev)
+ if (insert_after->m_id < item.m_id)
+ break;
+
+ // insert into the appropriate spot
+ if (insert_after != NULL)
+ {
+ item.m_ordered_next = insert_after->m_ordered_next;
+ if (item.m_ordered_next != NULL)
+ item.m_ordered_next->m_ordered_prev = &item;
+ else
+ m_ordered_tail = &item;
+ item.m_ordered_prev = insert_after;
+ insert_after->m_ordered_next = &item;
+ }
+ else
+ {
+ item.m_ordered_next = m_ordered_head;
+ if (item.m_ordered_next != NULL)
+ item.m_ordered_next->m_ordered_prev = &item;
+ else
+ m_ordered_tail = &item;
+ item.m_ordered_prev = NULL;
+ m_ordered_head = &item;
+ }
+
+ osd_lock_release(m_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(m_listlock);
+
+ int hashval = reinterpret_cast<FPTR>(ptr) % m_hash_size;
+ for (resource_pool_item **scanptr = &m_hash[hashval]; *scanptr != NULL; scanptr = &(*scanptr)->m_next)
+
+ // must match the pointer
+ if ((*scanptr)->m_ptr == ptr)
+ {
+ // remove from hash table
+ resource_pool_item *deleteme = *scanptr;
+ *scanptr = deleteme->m_next;
+
+ // remove from ordered list
+ if (deleteme->m_ordered_prev != NULL)
+ deleteme->m_ordered_prev->m_ordered_next = deleteme->m_ordered_next;
+ else
+ m_ordered_head = deleteme->m_ordered_next;
+ if (deleteme->m_ordered_next != NULL)
+ deleteme->m_ordered_next->m_ordered_prev = deleteme->m_ordered_prev;
+ else
+ m_ordered_tail = deleteme->m_ordered_prev;
+
+ // delete the object and break
+ if (LOG_ALLOCS)
+ fprintf(stderr, "#%06d, delete %d bytes\n", (UINT32)deleteme->m_id, static_cast<UINT32>(deleteme->m_size));
+ delete deleteme;
+ break;
+ }
+
+ osd_lock_release(m_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(m_listlock);
+
+ int hashval = reinterpret_cast<FPTR>(ptr) % m_hash_size;
+ resource_pool_item *item;
+ for (item = m_hash[hashval]; item != NULL; item = item->m_next)
+ if (item->m_ptr == ptr)
+ break;
+
+ osd_lock_release(m_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(m_listlock);
+
+ resource_pool_item *item = NULL;
+ for (item = m_ordered_head; item != NULL; item = item->m_ordered_next)
+ {
+ UINT8 *objstart = reinterpret_cast<UINT8 *>(item->m_ptr);
+ UINT8 *objend = objstart + item->m_size;
+ if (ptrstart >= objstart && ptrend <= objend)
+ goto found;
+ }
+
+found:
+ osd_lock_release(m_listlock);
+
+ return (item != NULL);
+}
+
+
+//-------------------------------------------------
+// clear - remove all items from a resource pool
+//-------------------------------------------------
+
+void resource_pool::clear()
+{
+ osd_lock_acquire(m_listlock);
+
+ // important: delete from earliest to latest; this allows objects to clean up after
+ // themselves if they wish
+ while (m_ordered_head != NULL)
+ remove(m_ordered_head->m_ptr);
+
+ osd_lock_release(m_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 (s_lock == NULL)
+ {
+ if (s_lock_alloc)
+ return;
+ s_lock_alloc = true;
+ s_lock = osd_lock_alloc();
+ s_lock_alloc = false;
+ }
+ osd_lock_acquire(s_lock);
+}
+
+
+//-------------------------------------------------
+// release_lock - release the memory entry lock
+//-------------------------------------------------
+
+void memory_entry::release_lock()
+{
+ osd_lock_release(s_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 (s_freehead == NULL)
+ {
+ // create a new chunk, and fail if we can't
+ memory_entry *entry = reinterpret_cast<memory_entry *>(osd_malloc_array(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->m_next = s_freehead;
+ s_freehead = entry++;
+ }
+ }
+
+ // grab a free entry
+ memory_entry *entry = s_freehead;
+ s_freehead = entry->m_next;
+
+ // populate it
+ entry->m_size = size;
+ entry->m_base = base;
+ entry->m_file = s_tracking ? file : NULL;
+ entry->m_line = s_tracking ? line : 0;
+ entry->m_id = s_curid++;
+ if (LOG_ALLOCS)
+ fprintf(stderr, "#%06d, alloc %d bytes (%s:%d)\n", (UINT32)entry->m_id, static_cast<UINT32>(entry->m_size), entry->m_file, (int)entry->m_line);
+
+ // add it to the alloc list
+ int hashval = reinterpret_cast<FPTR>(base) % k_hash_prime;
+ entry->m_next = s_hash[hashval];
+ if (entry->m_next != NULL)
+ entry->m_next->m_prev = entry;
+ entry->m_prev = NULL;
+ s_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) % k_hash_prime;
+ memory_entry *entry;
+ for (entry = s_hash[hashval]; entry != NULL; entry = entry->m_next)
+ if (entry->m_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->m_base) % k_hash_prime;
+ if (entry->m_prev != NULL)
+ entry->m_prev->m_next = entry->m_next;
+ else
+ s_hash[hashval] = entry->m_next;
+ if (entry->m_next != NULL)
+ entry->m_next->m_prev = entry->m_prev;
+
+ // add ourself to the free list
+ entry->m_next = s_freehead;
+ s_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 hashnum = 0; hashnum < k_hash_prime; hashnum++)
+ for (memory_entry *entry = s_hash[hashnum]; entry != NULL; entry = entry->m_next)
+ if (entry->m_file != NULL)
+ {
+ if (total == 0)
+ fprintf(stderr, "--- memory leak warning ---\n");
+ total += entry->m_size;
+ fprintf(stderr, "#%06d, nofree %d bytes (%s:%d)\n", (UINT32)entry->m_id, static_cast<UINT32>(entry->m_size), entry->m_file, (int)entry->m_line);
+ }
+
+ release_lock();
+
+ if (total > 0)
+ fprintf(stderr, "a total of %d bytes were not freed\n", total);
+}