diff options
author | 2014-03-11 15:54:58 +0000 | |
---|---|---|
committer | 2014-03-11 15:54:58 +0000 | |
commit | 4ea9df02a1daf1893246a01e21ffe201781f9266 (patch) | |
tree | 70dcb5feb88dcabe713fc172c30fd159ed1a0a43 /src/lib/util/corealloc.c | |
parent | b05606404a780c5309d39d8ee45411292279f117 (diff) |
Moved core template container classes up from emutempl.h to coretmpl.h:
[Aaron Giles]
* these classes now no longer take a resource_pool; everything is
managed globally -- this means that objects added to lists must be
allocated with global_alloc
* added new auto_pointer<> template which wraps a pointer and auto-frees
it upon destruction; it also defaults to NULL so it doesn't need to
be explicitly initialized
* moved tagged_list template to tagmap.h
Redo of the low-level memory tracking system: [Aaron Giles]
* moved low-level tracking out of emu\emualloc into lib\util\corealloc
so it can be shared among all components and used by core libraries
* global_alloc and friends no longer use a resource pool to track
allocations; turns out this was a wholly redundant system that wasted
a lot of memory
* removed global_resource_pool entirely
* added global_free_array to delete arrays allocated with
global_alloc_array
* added tracking of object versus array allocation; we will now error
if you use global_free on an array, or global_free_array on an object
Added new utility helper const_string_pool which can be used to
efficiently accumulate strings that are not intended to be modified.
Used by updated makelist and software list code. [Aaron Giles]
Updated png2bdc and makelist tools to not leak memory and use more modern
techniques (no more MAX_DRIVERS in makelist, for example). [Aaron Giles]
Deprecated auto_strdup and removed all uses by way of caller-managed
astrings and the software list rewrite. [Aaron Giles]
Rewrote software list management: [Aaron Giles]
* removed the notion of a software_list that is separate from a
software_list_device; they are one and the same now
* moved several functions into device_image_interface since they really
didn't belong in the core software list class
* lots of simplification as a result of the above changes
Additional notes (no whatsnew):
Moved definition of FPTR to osdcomm.h.
Some changes happened in the OSD code to fix issues, especially regarding
freeing arrays. SDL folks may need to fix up some of these.
The following devices still are using tokens and should be modernized
(I found them because they kept their token as void * and tried to
delete it, which you can't):
namco_52xx_device (mame/audio/namco52.c)
namco_54xx_device (mame/audio/namco54.c)
namco_06xx_device (mame/machine/namco06.c)
namco_50xx_device (mame/machine/namco50.c)
namco_51xx_device (mame/machine/namco51.c)
namco_53xx_device (mame/machine/namco53.c)
voodoo_device (emu/video/voodoo.c)
mos6581_device (emu/sound/mos6581.c)
aica_device (emu/sound/aica.c)
scsp_device (emu/sound/scsp.c)
dmadac_sound_device (emu/sound/dmadac.c)
s3c2440_device (emu/machine/s3c2440.c)
wd1770_device (emu/machine/wd17xx.c)
latch8_device (emu/machine/latch8.c)
duart68681_device (emu/machine/68681.c)
s3c2400_device (emu/machine/s3c2400.c)
s3c2410_device (emu/machine/s3c2410.c)
strataflash_device (mess/machine/strata.c)
hd63450_device (mess/machine/hd63450.c)
tap_990_device (mess/machine/ti99/990_tap.c)
omti8621_device (mess/machine/omti8621.c)
vdt911_device (mess/video/911_vdt.c)
apollo_graphics_15i (mess/video/apollo.c)
asr733_device (mess/video/733_asr.c)
Diffstat (limited to 'src/lib/util/corealloc.c')
-rw-r--r-- | src/lib/util/corealloc.c | 375 |
1 files changed, 375 insertions, 0 deletions
diff --git a/src/lib/util/corealloc.c b/src/lib/util/corealloc.c new file mode 100644 index 00000000000..089770b20ed --- /dev/null +++ b/src/lib/util/corealloc.c @@ -0,0 +1,375 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles +/*************************************************************************** + + corealloc.c + + Memory allocation helpers for the helper library. + +***************************************************************************/ + +#include "corealloc.h" +#include "osdcore.h" + + +//************************************************************************** +// DEBUGGING +//************************************************************************** + +#define LOG_ALLOCS (0) + + + +//************************************************************************** +// CONSTANTS +//************************************************************************** + +// 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: + // internal state + 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 + bool m_array; // array? + + // hashing prime number + static const int k_hash_prime = 6151; + + // global state + 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 helpers + static memory_entry *allocate(size_t size, void *base, const char *file, int line, bool array); + static memory_entry *find(void *ptr); + static void release(memory_entry *entry, const char *file, int line); + static void report_unfreed(UINT64 start); + +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; + + + +//************************************************************************** +// 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, bool array, bool throw_on_fail, bool clear) +{ + // allocate the memory and fail if we can't + void *result = array ? osd_malloc_array(size) : osd_malloc(size); + if (result == NULL) + { + fprintf(stderr, "Failed to allocate %d bytes (%s:%d)\n", size, file, line); + osd_break_into_debugger("Failed to allocate RAM"); + if (throw_on_fail) + throw std::bad_alloc(); + return NULL; + } + + // zap the memory if requested + if (clear) + memset(result, 0, size); + else + { +#if !__has_feature(memory_sanitizer) && defined(INITIALIZE_ALLOCATED_MEMORY) + memset(result, 0xdd, size); +#endif + } + + // add a new entry + memory_entry::allocate(size, result, file, line, array); + + return result; +} + + +//------------------------------------------------- +// free_file_line - free memory with file +// and line number information +//------------------------------------------------- + +void free_file_line(void *memory, const char *file, int line, bool array) +{ + // 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 %p in %s(%d)!\n", memory, file, line); + osd_break_into_debugger("Error: attempt to free untracked memory"); + return; + } + + // warn about mismatched arrays + if (!array && entry->m_array) + { + fprintf(stderr, "Error: attempt to free array %p with global_free in %s(%d)!\n", memory, file, line); + osd_break_into_debugger("Error: attempt to free array with global_free"); + } + if (array && !entry->m_array) + { + fprintf(stderr, "Error: attempt to free single object %p with global_free_array in %s(%d)!\n", memory, file, line); + osd_break_into_debugger("Error: attempt to free single object with global_free_array"); + } + +#ifdef OVERWRITE_FREED_MEMORY + // clear memory to a bogus value + memset(memory, 0xfc, entry->m_size); +#endif + + // free the entry and the memory + memory_entry::release(entry, file, line); + 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; +} + + +//------------------------------------------------- +// next_memory_id - return the ID of the next +// allocated block +//------------------------------------------------- + +UINT64 next_memory_id() +{ + return memory_entry::s_curid; +} + + +//------------------------------------------------- +// dump_unfreed_mem - called from the exit path +// of any code that wants to check for unfreed +// memory +//------------------------------------------------- + +void dump_unfreed_mem(UINT64 start) +{ + memory_entry::report_unfreed(start); +} + + + +//************************************************************************** +// 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, bool array) +{ + 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++; + entry->m_array = array; + 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, const char *file, int line) +{ + acquire_lock(); + + if (LOG_ALLOCS) + fprintf(stderr, "#%06d, release %d bytes (%s:%d)\n", (UINT32)entry->m_id, static_cast<UINT32>(entry->m_size), file, line); + + // 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(UINT64 start) +{ + 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 && entry->m_id >= start) + { + 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 %u bytes were not freed\n", total); +} |