DeletedBufferChain

This commit is contained in:
David Rose 2007-07-20 23:59:10 +00:00
parent b2fb75e867
commit 982f614991
12 changed files with 521 additions and 236 deletions

View File

@ -503,11 +503,15 @@ $[cdefine __USE_LARGEFILE64]
#define USE_MEMORY_MALLOC
#define USE_MEMORY_NOWRAPPERS
#if $[ALTERNATIVE_MALLOC]
#if $[and $[HAVE_THREADS], $[not $[SIMPLE_THREADS]]]
// A fast thread-safe alternative implementation.
#if $[and $[WIN32_PLATFORM], $[HAVE_THREADS], $[not $[SIMPLE_THREADS]]]
// A fast thread-safe alternative implementation, but which only
// seems to be a good choice on Windows. (It crashes on Linux and
// isn't thread-safe on OSX).
#set USE_MEMORY_PTMALLOC2 1
#else
// A faster, but non-thread-safe, alternative implementation.
// When threading support is compiled in, we use a global mutex to
// protect it.
#set USE_MEMORY_DLMALLOC 1
#endif
#else

View File

@ -14,6 +14,7 @@
atomicAdjustPosixImpl.h atomicAdjustPosixImpl.I \
atomicAdjustWin32Impl.h atomicAdjustWin32Impl.I \
cmath.I cmath.h \
deletedBufferChain.h deletedBufferChain.I \
deletedChain.h deletedChain.T \
dtoolbase.h dtoolbase_cc.h dtoolsymbols.h \
fakestringstream.h \
@ -46,6 +47,7 @@
atomicAdjustI386Impl.cxx \
atomicAdjustPosixImpl.cxx \
atomicAdjustWin32Impl.cxx \
deletedBufferChain.cxx \
dtoolbase.cxx \
memoryBase.cxx \
memoryHook.cxx \
@ -67,6 +69,7 @@
atomicAdjustPosixImpl.h atomicAdjustPosixImpl.I \
atomicAdjustWin32Impl.h atomicAdjustWin32Impl.I \
cmath.I cmath.h \
deletedBufferChain.h deletedBufferChain.I \
deletedChain.h deletedChain.T \
dtoolbase.h dtoolbase_cc.h dtoolsymbols.h fakestringstream.h \
indent.I indent.h \

View File

@ -0,0 +1,86 @@
// Filename: deletedBufferChain.I
// Created by: drose (20Jul07)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
// Function: DeletedBufferChain::validate
// Access: Public
// Description: Returns true if the pointer is valid, false if it has
// been deleted or if it was never a valid pointer.
//
// This is only meaningful in debug mode, where
// USE_DELETEDCHAINFLAG is defined. If not, this
// trivially returns true.
////////////////////////////////////////////////////////////////////
INLINE bool DeletedBufferChain::
validate(void *ptr) {
TAU_PROFILE("bool DeletedBufferChain::validate(void *)", " ", TAU_USER);
if (ptr == (void *)NULL) {
return false;
}
#ifdef USE_DELETEDCHAINFLAG
const ObjectNode *obj = buffer_to_node(ptr);
return AtomicAdjust::get(obj->_flag) == DCF_alive;
#else
return true;
#endif // USE_DELETEDCHAINFLAG
}
////////////////////////////////////////////////////////////////////
// Function: DeletedBufferChain::get_buffer_size
// Access: Public
// Description: Returns the size of the buffer that is actually
// returned at each request.
////////////////////////////////////////////////////////////////////
INLINE size_t DeletedBufferChain::
get_buffer_size() const {
return _buffer_size;
}
////////////////////////////////////////////////////////////////////
// Function: DeletedBufferChain::node_to_buffer
// Access: Private, Static
// Description: Casts an ObjectNode* to a void* buffer.
////////////////////////////////////////////////////////////////////
INLINE void *DeletedBufferChain::
node_to_buffer(DeletedBufferChain::ObjectNode *node) {
#ifdef USE_DELETEDCHAINFLAG
// In development mode, we increment the pointer so that the
// returned data does not overlap our _flags member.
return (void *)(((PN_int32 *)node) + 1);
#else
return (void *)node;
#endif // NDEBUG
}
////////////////////////////////////////////////////////////////////
// Function: DeletedBufferChain::buffer_to_node
// Access: Private, Static
// Description: Casts a void* buffer to an ObjectNode* .
////////////////////////////////////////////////////////////////////
INLINE DeletedBufferChain::ObjectNode *DeletedBufferChain::
buffer_to_node(void *ptr) {
#ifdef USE_DELETEDCHAINFLAG
// In development mode, we decrement the pointer to undo the
// increment we did above.
return (ObjectNode *)(((PN_int32 *)ptr) - 1);
#else
return (ObjectNode *)ptr;
#endif // NDEBUG
}

View File

@ -0,0 +1,134 @@
// Filename: deletedBufferChain.cxx
// Created by: drose (20Jul07)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////
#include "deletedBufferChain.h"
////////////////////////////////////////////////////////////////////
// Function: DeletedBufferChain::Constructor
// Access: Protected
// Description: Use the global MemoryHook to get a new
// DeletedBufferChain of the appropriate size.
////////////////////////////////////////////////////////////////////
DeletedBufferChain::
DeletedBufferChain(size_t buffer_size) {
_deleted_chain = NULL;
_buffer_size = buffer_size;
_alloc_size = _buffer_size;
#ifdef USE_DELETEDCHAINFLAG
// In development mode, we also need to reserve space for _flag.
_alloc_size += sizeof(PN_int32);
#endif // NDEBUG
// We must allocate at least this much space for bookkeeping
// reasons.
_buffer_size = max(_buffer_size, sizeof(ObjectNode));
_alloc_size = max(_alloc_size, sizeof(ObjectNode));
}
////////////////////////////////////////////////////////////////////
// Function: DeletedBufferChain::allocate
// Access: Public
// Description: Allocates the memory for a new buffer of the
// indicated size (which must be no greater than the
// fixed size associated with the DeletedBufferChain).
////////////////////////////////////////////////////////////////////
void *DeletedBufferChain::
allocate(size_t size, TypeHandle type_handle) {
TAU_PROFILE("void *DeletedBufferChain::allocate(size_t, TypeHandle)", " ", TAU_USER);
assert(size <= _buffer_size);
ObjectNode *obj;
_lock.lock();
if (_deleted_chain != (ObjectNode *)NULL) {
obj = _deleted_chain;
_deleted_chain = _deleted_chain->_next;
_lock.release();
#ifdef USE_DELETEDCHAINFLAG
assert(obj->_flag == (PN_int32)DCF_deleted);
obj->_flag = DCF_alive;
#endif // NDEBUG
void *ptr = node_to_buffer(obj);
#ifdef DO_MEMORY_USAGE
// type_handle.dec_memory_usage(TypeHandle::MC_deleted_chain_inactive, _alloc_size);
type_handle.inc_memory_usage(TypeHandle::MC_deleted_chain_active, _alloc_size);
#endif // DO_MEMORY_USAGE
return ptr;
}
_lock.release();
// If we get here, the deleted_chain is empty; we have to allocate a
// new object from the system pool.
obj = (ObjectNode *)NeverFreeMemory::alloc(_alloc_size);
#ifdef USE_DELETEDCHAINFLAG
obj->_flag = DCF_alive;
#endif // USE_DELETEDCHAINFLAG
void *ptr = node_to_buffer(obj);
#ifdef DO_MEMORY_USAGE
type_handle.inc_memory_usage(TypeHandle::MC_deleted_chain_active, _alloc_size);
#endif // DO_MEMORY_USAGE
return ptr;
}
////////////////////////////////////////////////////////////////////
// Function: DeletedBufferChain::deallocate
// Access: Public
// Description: Frees the memory for a buffer previously allocated
// via allocate().
////////////////////////////////////////////////////////////////////
void DeletedBufferChain::
deallocate(void *ptr, TypeHandle type_handle) {
TAU_PROFILE("void DeletedBufferChain::deallocate(void *, TypeHandle)", " ", TAU_USER);
assert(ptr != (void *)NULL);
#ifdef DO_MEMORY_USAGE
type_handle.dec_memory_usage(TypeHandle::MC_deleted_chain_active, _alloc_size);
// type_handle.inc_memory_usage(TypeHandle::MC_deleted_chain_inactive, _alloc_size);
#endif // DO_MEMORY_USAGE
ObjectNode *obj = buffer_to_node(ptr);
#ifdef USE_DELETEDCHAINFLAG
PN_int32 orig_flag = AtomicAdjust::compare_and_exchange(obj->_flag, DCF_alive, DCF_deleted);
// If this assertion is triggered, you double-deleted an object.
assert(orig_flag != (PN_int32)DCF_deleted);
// If this assertion is triggered, you tried to delete an object
// that was never allocated, or you have heap corruption.
assert(orig_flag == (PN_int32)DCF_alive);
#endif // NDEBUG
_lock.lock();
obj->_next = _deleted_chain;
_deleted_chain = obj;
_lock.release();
}

View File

@ -0,0 +1,113 @@
// Filename: deletedBufferChain.h
// Created by: drose (20Jul07)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////
#ifndef DELETEDBUFFERCHAIN_H
#define DELETEDBUFFERCHAIN_H
#include "dtoolbase.h"
#include "neverFreeMemory.h"
#include "mutexImpl.h"
#include "atomicAdjust.h"
#include "numeric_types.h"
#include "typeHandle.h"
#include <assert.h>
// Though it's tempting, it doesn't seem to be possible to implement
// DeletedBufferChain via the atomic exchange operation.
// Specifically, a pointer may be removed from the head of the chain,
// then the same pointer reinserted in the chain, while another thread
// is waiting; and that thread will not detect the change. So
// instead, we always use a mutex.
#ifndef NDEBUG
// In development mode, we define USE_DELETEDCHAINFLAG, which
// triggers the piggyback of an additional word of data on every
// allocated block, so we can ensure that an object is not
// double-deleted and that the deleted chain remains intact.
#define USE_DELETEDCHAINFLAG 1
#endif // NDEBUG
#ifdef USE_DELETEDCHAINFLAG
enum DeletedChainFlag {
DCF_deleted = 0xfeedba0f,
DCF_alive = 0x12487654,
};
#endif
////////////////////////////////////////////////////////////////////
// Class : DeletedBufferChain
// Description : This template class can be used to provide faster
// allocation/deallocation for many Panda objects. It
// works by maintaining a linked list of deleted buffers
// that are all of the same size; when a new object is
// allocated that matches that size, the same space is
// just reused.
//
// This class manages untyped buffers of a fixed size.
// It can be used directly; or it also serves as a
// backbone for DeletedChain, which is a template class
// that manages object allocations.
//
// Use MemoryHook to get a new DeletedBufferChain of a
// particular size.
////////////////////////////////////////////////////////////////////
class DeletedBufferChain {
protected:
DeletedBufferChain(size_t buffer_size);
public:
void *allocate(size_t size, TypeHandle type_handle);
void deallocate(void *ptr, TypeHandle type_handle);
INLINE bool validate(void *ptr);
INLINE size_t get_buffer_size() const;
private:
class ObjectNode {
public:
#ifdef USE_DELETEDCHAINFLAG
// In development mode, we piggyback this extra data. This is
// maintained out-of-band from the actual pointer returned, so we
// can safely use this flag to indicate the difference between
// allocated and freed pointers.
TVOLATILE PN_int32 _flag;
#endif
// This pointer sits within the buffer, in the same space
// referenced by the actual pointer returned (unlike _flag,
// above). It's only used when the buffer is deleted, so there's
// no harm in sharing space with the undeleted buffer.
ObjectNode *_next;
};
static INLINE void *node_to_buffer(ObjectNode *node);
static INLINE ObjectNode *buffer_to_node(void *buffer);
ObjectNode *_deleted_chain;
MutexImpl _lock;
size_t _buffer_size;
size_t _alloc_size;
friend class MemoryHook;
};
#include "deletedBufferChain.I"
#endif

View File

@ -28,58 +28,14 @@ template<class Type>
INLINE Type *DeletedChain<Type>::
allocate(size_t size, TypeHandle type_handle) {
TAU_PROFILE("Type *DeletedChain<Type>::allocate(size_t, TypeHandle)", " ", TAU_USER);
assert(size <= sizeof(Type));
size_t alloc_size = sizeof(Type);
#ifdef USE_DELETEDCHAINFLAG
// In development mode, we also need to reserve space for _flag.
alloc_size += sizeof(PN_int32);
#endif // NDEBUG
ObjectNode *obj;
init_lock();
_lock->lock();
if (_deleted_chain != (ObjectNode *)NULL) {
obj = _deleted_chain;
_deleted_chain = _deleted_chain->_next;
_lock->release();
#ifdef USE_DELETEDCHAINFLAG
assert(obj->_flag == (PN_int32)DCF_deleted);
obj->_flag = DCF_alive;
#endif // NDEBUG
Type *ptr = node_to_type(obj);
init_deleted_chain();
void *ptr = _chain->allocate(size, type_handle);
#ifdef DO_MEMORY_USAGE
type_handle.dec_memory_usage(TypeHandle::MC_deleted_chain_inactive, alloc_size);
type_handle.inc_memory_usage(TypeHandle::MC_deleted_chain_active, alloc_size);
memory_hook->mark_pointer(ptr, max(size, sizeof(ObjectNode)), make_ref_ptr(ptr));
memory_hook->mark_pointer(ptr, _chain->get_buffer_size(), make_ref_ptr(ptr));
#endif // DO_MEMORY_USAGE
return ptr;
}
_lock->release();
// If we get here, the deleted_chain is empty; we have to allocate a
// new object from the system pool.
alloc_size = max(alloc_size, sizeof(ObjectNode));
obj = (ObjectNode *)NeverFreeMemory::alloc(alloc_size);
#ifdef USE_DELETEDCHAINFLAG
obj->_flag = DCF_alive;
#endif // USE_DELETEDCHAINFLAG
Type *ptr = node_to_type(obj);
#ifdef DO_MEMORY_USAGE
type_handle.inc_memory_usage(TypeHandle::MC_deleted_chain_active, alloc_size);
memory_hook->mark_pointer(ptr, max(size, sizeof(ObjectNode)), make_ref_ptr(ptr));
#endif // DO_MEMORY_USAGE
return ptr;
return (Type *)ptr;
}
////////////////////////////////////////////////////////////////////
@ -91,47 +47,18 @@ template<class Type>
INLINE void DeletedChain<Type>::
deallocate(Type *ptr, TypeHandle type_handle) {
TAU_PROFILE("void DeletedChain<Type>::deallocate(Type *, TypeHandle)", " ", TAU_USER);
assert(ptr != (Type *)NULL);
#ifdef DO_MEMORY_USAGE
size_t alloc_size = sizeof(Type);
memory_hook->mark_pointer(ptr, 0, make_ref_ptr(ptr));
#endif
#ifdef USE_DELETEDCHAINFLAG
// In development mode, we also need to reserve space for _flag.
alloc_size += sizeof(PN_int32);
#endif // NDEBUG
type_handle.dec_memory_usage(TypeHandle::MC_deleted_chain_active, alloc_size);
type_handle.inc_memory_usage(TypeHandle::MC_deleted_chain_inactive, alloc_size);
// We have to call this again, even though it must have been called
// in allocate(), because with template resolution across dll's it
// is possible that this DeletedChain object is not the same one
// that allocated the object.
init_deleted_chain();
#endif // DO_MEMORY_USAGE
ObjectNode *obj = type_to_node(ptr);
#ifdef USE_DELETEDCHAINFLAG
PN_int32 orig_flag = AtomicAdjust::compare_and_exchange(obj->_flag, DCF_alive, DCF_deleted);
// If this assertion is triggered, you double-deleted an object.
assert(orig_flag != (PN_int32)DCF_deleted);
// If this assertion is triggered, you tried to delete an object
// that was never allocated, or you have heap corruption.
assert(orig_flag == (PN_int32)DCF_alive);
#endif // NDEBUG
// We have to test the lock again, even though we must have
// allocated this object at some point, because Windows might make
// multiple different DeletedChain instances for a particular Type,
// and there's no guarantee this one is the same one we used to
// allocate this object.
init_lock();
_lock->lock();
obj->_next = _deleted_chain;
_deleted_chain = obj;
_lock->release();
_chain->deallocate(ptr, type_handle);
}
////////////////////////////////////////////////////////////////////
@ -148,16 +75,13 @@ template<class Type>
INLINE bool DeletedChain<Type>::
validate(const Type *ptr) {
TAU_PROFILE("bool DeletedChain<Type>::validate(Type *)", " ", TAU_USER);
if (ptr == (Type *)NULL) {
return false;
}
#ifdef USE_DELETEDCHAINFLAG
const ObjectNode *obj = type_to_node((Type *)ptr);
return AtomicAdjust::get(obj->_flag) == DCF_alive;
init_deleted_chain();
return _chain->validate((void *)ptr);
#else
return true;
#endif // NDEBUG
#endif // USE_DELETEDCHAINFLAG
}
////////////////////////////////////////////////////////////////////
@ -195,76 +119,20 @@ make_ref_ptr(ReferenceCount *ptr) {
}
////////////////////////////////////////////////////////////////////
// Function: DeletedChain::node_to_type
// Access: Private, Static
// Description: Casts an ObjectNode* to a Type*.
////////////////////////////////////////////////////////////////////
template<class Type>
INLINE Type *DeletedChain<Type>::
node_to_type(TYPENAME DeletedChain<Type>::ObjectNode *node) {
#ifdef USE_DELETEDCHAINFLAG
// In development mode, we increment the pointer so that the
// returned data does not overlap our _flags member.
return (Type *)(((PN_int32 *)node) + 1);
#else
return (Type *)node;
#endif // NDEBUG
}
////////////////////////////////////////////////////////////////////
// Function: DeletedChain::type_to_node
// Access: Private, Static
// Description: Casts a Type* to an ObjectNode* .
////////////////////////////////////////////////////////////////////
template<class Type>
INLINE TYPENAME DeletedChain<Type>::ObjectNode *DeletedChain<Type>::
type_to_node(Type *ptr) {
#ifdef USE_DELETEDCHAINFLAG
// In development mode, we decrement the pointer to undo the
// increment we did above.
return (ObjectNode *)(((PN_int32 *)ptr) - 1);
#else
return (ObjectNode *)ptr;
#endif // NDEBUG
}
////////////////////////////////////////////////////////////////////
// Function: DeletedChain::init_lock
// Function: DeletedChain::init_deleted_chain
// Access: Private
// Description: Ensures the lock pointer has been allocated.
////////////////////////////////////////////////////////////////////
template<class Type>
INLINE void DeletedChain<Type>::
init_lock() {
if (_lock == (MutexImpl *)NULL) {
do_init_lock(_lock);
}
}
////////////////////////////////////////////////////////////////////
// Function: DeletedChain::do_init_lock
// Access: Private
// Description: Allocates the lock pointer if necessary. Takes some
// pains to protect itself from race conditions.
//
// We have to receive the MutexImpl object as a
// parameter, because this is a non-inline function, and
// the template pointer might get evaluated differently
// for inline vs. non-inline functions.
// Description: Assigns the _chain pointer if it is not already
// assigned. This can't be done by a constructor, since
// often the DeletedChain instance is used before its
// static construct has had a chance to be called.
////////////////////////////////////////////////////////////////////
template<class Type>
void DeletedChain<Type>::
do_init_lock(MutexImpl *&lock) {
MutexImpl *new_lock = new MutexImpl;
MutexImpl *result;
result = (MutexImpl *)AtomicAdjust::compare_and_exchange_ptr((void * TVOLATILE &)lock, (void *)NULL, (void *)new_lock);
if (result != NULL) {
delete new_lock;
init_deleted_chain() {
if (_chain == (DeletedBufferChain *)NULL) {
init_memory_hook();
_chain = memory_hook->get_deleted_chain(sizeof(Type));
}
assert(lock != (MutexImpl *)NULL);
}
////////////////////////////////////////////////////////////////////

View File

@ -20,36 +20,9 @@
#define DELETEDCHAIN_H
#include "dtoolbase.h"
#include "neverFreeMemory.h"
#include "mutexImpl.h"
#include "atomicAdjust.h"
#include "numeric_types.h"
#include "register_type.h"
#include "typeHandle.h"
#include "deletedBufferChain.h"
#include <assert.h>
// Though it's tempting, it doesn't seem to be possible to implement
// DeletedChain via the atomic exchange operation. Specifically, a
// pointer may be removed from the head of the chain, then the same
// pointer reinserted in the chain, while another thread is waiting;
// and that thread will not detect the change. So instead, we always
// use a mutex.
#ifndef NDEBUG
// In development mode, we define USE_DELETEDCHAINFLAG, which
// triggers the piggyback of an additional word of data on every
// allocated block, so we can ensure that an object is not
// double-deleted and that the deleted chain remains intact.
#define USE_DELETEDCHAINFLAG 1
#endif // NDEBUG
#ifdef USE_DELETEDCHAINFLAG
enum DeletedChainFlag {
DCF_deleted = 0xfeedba0f,
DCF_alive = 0x12487654,
};
#endif
////////////////////////////////////////////////////////////////////
// Class : DeletedChain
// Description : This template class can be used to provide faster
@ -59,13 +32,10 @@ enum DeletedChainFlag {
// allocated that matches that type, the same space is
// just reused.
//
// This is particularly important to do in the case of a
// multithreaded pipeline, so we minimize contention
// between the parallel threads. If we didn't do this,
// the threads might compete overmuch on the system
// mutex protecting the malloc() call. This way, there
// can be a different mutex for each type of object; or on
// some systems (e.g. i386), no mutex at all.
// This class is actually a layer on top of
// DeletedBufferChain, which handles the actual
// allocation. This class just provides the
// typecasting.
//
// Of course, this trick of maintaining the deleted
// object chain won't work in the presence of
@ -88,31 +58,9 @@ public:
static INLINE ReferenceCount *make_ref_ptr(ReferenceCount *ptr);
private:
class ObjectNode {
public:
#ifdef USE_DELETEDCHAINFLAG
// In development mode, we piggyback this extra data. This is
// maintained out-of-band from the actual pointer returned, so we
// can safely use this flag to indicate the difference between
// allocated and freed pointers.
TVOLATILE PN_int32 _flag;
#endif
INLINE void init_deleted_chain();
// This pointer sits in the same space referenced by the actual
// pointer returned (unlike _flag, above). It's only used when
// the object is deleted, so there's no harm in sharing space with
// the undeleted object.
ObjectNode *_next;
};
static INLINE Type *node_to_type(ObjectNode *node);
static INLINE ObjectNode *type_to_node(Type *ptr);
ObjectNode *_deleted_chain;
INLINE void init_lock();
void do_init_lock(MutexImpl *&lock);
MutexImpl *_lock;
DeletedBufferChain *_chain;
};
////////////////////////////////////////////////////////////////////

View File

@ -3,6 +3,7 @@
#include "atomicAdjustI386Impl.cxx"
#include "atomicAdjustPosixImpl.cxx"
#include "atomicAdjustWin32Impl.cxx"
#include "deletedBufferChain.cxx"
#include "dtoolbase.cxx"
#include "memoryBase.cxx"
#include "memoryHook.cxx"

View File

@ -17,6 +17,7 @@
////////////////////////////////////////////////////////////////////
#include "memoryHook.h"
#include "deletedBufferChain.h"
#ifdef WIN32
@ -40,19 +41,15 @@
//
// Memory manager: DLMALLOC
//
// This is Doug Lea's memory manager. It is very fast,
// but it is not thread-safe.
// This is Doug Lea's memory manager. It is very fast, but it is not
// thread-safe. However, we provide thread locking within MemoryHook.
//
/////////////////////////////////////////////////////////////////////
#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
#error Cannot use dlmalloc library with threading enabled!
#endif
#define USE_DL_PREFIX 1
#define NO_MALLINFO 1
#ifdef _DEBUG
#define DEBUG
#define DEBUG 1
#endif
#include "dlmalloc.h"
#include "dlmalloc_src.cxx"
@ -60,17 +57,18 @@
#define call_malloc dlmalloc
#define call_realloc dlrealloc
#define call_free dlfree
#define MEMORY_HOOK_MALLOC_LOCK 1
#elif defined(USE_MEMORY_PTMALLOC2) && !defined(linux)
#elif defined(USE_MEMORY_PTMALLOC2)
// This doesn't appear to work in Linux; perhaps it is clashing with
// the system library. On Linux, fall through to the next case
// instead.
// the system library. It also doesn't appear to be thread-safe on
// OSX.
/////////////////////////////////////////////////////////////////////
//
// Memory manager: PTMALLOC2
//
// Ptmalloc2 is a derivative of Doug Lea's memory manager that was
// Ptmalloc2 is a derivative of Doug Lea's memory manager that was
// made thread-safe by Wolfram Gloger, then was ported to windows by
// Niall Douglas. It is not quite as fast as dlmalloc (because the
// thread-safety constructs take a certain amount of CPU time), but
@ -88,6 +86,7 @@
#define call_malloc dlmalloc
#define call_realloc dlrealloc
#define call_free dlfree
#undef MEMORY_HOOK_MALLOC_LOCK
#else
@ -103,6 +102,7 @@
#define call_malloc malloc
#define call_realloc realloc
#define call_free free
#undef MEMORY_HOOK_MALLOC_LOCK
#endif // USE_MEMORY_*
@ -152,6 +152,10 @@ MemoryHook(const MemoryHook &copy) :
_requested_heap_size = copy._requested_heap_size;
_total_mmap_size = copy._total_mmap_size;
#endif
((MutexImpl &)copy._lock).lock();
_deleted_chains = copy._deleted_chains;
((MutexImpl &)copy._lock).release();
}
////////////////////////////////////////////////////////////////////
@ -184,7 +188,14 @@ heap_alloc_single(size_t size) {
AtomicAdjust::add(_total_heap_single_size, (PN_int32)size);
#endif // DO_MEMORY_USAGE
#ifdef MEMORY_HOOK_MALLOC_LOCK
_lock.lock();
void *alloc = call_malloc(inflate_size(size));
_lock.release();
#else
void *alloc = call_malloc(inflate_size(size));
#endif
if (alloc == (void *)NULL) {
cerr << "Out of memory!\n";
abort();
@ -209,7 +220,13 @@ heap_free_single(void *ptr) {
AtomicAdjust::add(_total_heap_single_size, -(PN_int32)size);
#endif // DO_MEMORY_USAGE
#ifdef MEMORY_HOOK_MALLOC_LOCK
_lock.lock();
call_free(alloc);
_lock.release();
#else
call_free(alloc);
#endif
}
////////////////////////////////////////////////////////////////////
@ -232,7 +249,14 @@ heap_alloc_array(size_t size) {
AtomicAdjust::add(_total_heap_array_size, (PN_int32)size);
#endif // DO_MEMORY_USAGE
#ifdef MEMORY_HOOK_MALLOC_LOCK
_lock.lock();
void *alloc = call_malloc(inflate_size(size));
_lock.release();
#else
void *alloc = call_malloc(inflate_size(size));
#endif
if (alloc == (void *)NULL) {
cerr << "Out of memory!\n";
abort();
@ -257,7 +281,14 @@ heap_realloc_array(void *ptr, size_t size) {
AtomicAdjust::add(_total_heap_array_size, (PN_int32)size-(PN_int32)orig_size);
#endif // DO_MEMORY_USAGE
#ifdef MEMORY_HOOK_MALLOC_LOCK
_lock.lock();
alloc = call_realloc(alloc, inflate_size(size));
_lock.release();
#else
alloc = call_realloc(alloc, inflate_size(size));
#endif
if (alloc == (void *)NULL) {
cerr << "Out of memory!\n";
abort();
@ -282,7 +313,13 @@ heap_free_array(void *ptr) {
AtomicAdjust::add(_total_heap_array_size, -(PN_int32)size);
#endif // DO_MEMORY_USAGE
#ifdef MEMORY_HOOK_MALLOC_LOCK
_lock.lock();
call_free(alloc);
_lock.release();
#else
call_free(alloc);
#endif
}
////////////////////////////////////////////////////////////////////
@ -303,13 +340,15 @@ bool MemoryHook::
heap_trim(size_t pad) {
bool trimmed = false;
#if defined(USE_MEMORY_DLMALLOC) || (defined(USE_MEMORY_PTMALLOC2) && !defined(linux))
#if defined(USE_MEMORY_DLMALLOC) || defined(USE_MEMORY_PTMALLOC2)
// Since malloc_trim() isn't standard C, we can't be sure it exists
// on a given platform. But if we're using ALTERNATIVE_MALLOC, we
// know we have dlmalloc_trim.
// on a given platform. But if we're using dlmalloc, we know we
// have dlmalloc_trim.
_lock.lock();
if (dlmalloc_trim(pad)) {
trimmed = true;
}
_lock.release();
#endif
#ifdef WIN32
@ -424,3 +463,29 @@ mmap_free(void *ptr, size_t size) {
void MemoryHook::
mark_pointer(void *, size_t, ReferenceCount *) {
}
////////////////////////////////////////////////////////////////////
// Function: MemoryHook::get_deleted_chain
// Access: Public
// Description: Returns a pointer to a global DeletedBufferChain
// object suitable for allocating arrays of the
// indicated size. There is one unique
// DeletedBufferChain object for every different size.
////////////////////////////////////////////////////////////////////
DeletedBufferChain *MemoryHook::
get_deleted_chain(size_t buffer_size) {
DeletedBufferChain *chain;
_lock.lock();
DeletedChains::iterator dci = _deleted_chains.find(buffer_size);
if (dci != _deleted_chains.end()) {
chain = (*dci).second;
} else {
// Once allocated, this DeletedBufferChain object is never deleted.
chain = new DeletedBufferChain(buffer_size);
_deleted_chains.insert(DeletedChains::value_type(buffer_size, chain));
}
_lock.release();
return chain;
}

View File

@ -22,6 +22,10 @@
#include "dtoolbase.h"
#include "numeric_types.h"
#include "atomicAdjust.h"
#include "mutexImpl.h"
#include <map>
class DeletedBufferChain;
////////////////////////////////////////////////////////////////////
// Class : MemoryHook
@ -65,9 +69,9 @@ public:
virtual void mark_pointer(void *ptr, size_t orig_size, ReferenceCount *ref_ptr);
private:
size_t _page_size;
DeletedBufferChain *get_deleted_chain(size_t buffer_size);
private:
INLINE static size_t inflate_size(size_t size);
INLINE static void *alloc_to_ptr(void *alloc, size_t size);
INLINE static void *ptr_to_alloc(void *ptr, size_t &size);
@ -79,6 +83,14 @@ protected:
TVOLATILE PN_int32 _requested_heap_size;
TVOLATILE PN_int32 _total_mmap_size;
#endif
private:
size_t _page_size;
typedef map<size_t, DeletedBufferChain *> DeletedChains;
DeletedChains _deleted_chains;
MutexImpl _lock;
};
#include "memoryHook.I"

View File

@ -50,6 +50,17 @@ operator () (const Key &a, const Key &b) const {
return (a.compare_to(b) < 0);
}
////////////////////////////////////////////////////////////////////
// Function: compare_to::is_equal
// Access: Public
// Description: Returns true if a is equivalent to b, false otherwise.
////////////////////////////////////////////////////////////////////
template<class Key>
INLINE bool compare_to<Key>::
is_equal(const Key &a, const Key &b) const {
return (a.compare_to(b) == 0);
}
////////////////////////////////////////////////////////////////////
// Function: indirect_less::operator ()
// Access: Public
@ -72,6 +83,17 @@ operator () (const Key &a, const Key &b) const {
return (a != b && (*a).compare_to(*b) < 0);
}
////////////////////////////////////////////////////////////////////
// Function: indirect_compare_to::is_equal
// Access: Public
// Description: Returns true if a is equivalent to b, false otherwise.
////////////////////////////////////////////////////////////////////
template<class Key>
INLINE bool indirect_compare_to<Key>::
is_equal(const Key &a, const Key &b) const {
return (a == b || (*a).compare_to(*b) == 0);
}
////////////////////////////////////////////////////////////////////
// Function: indirect_compare_names::operator ()
// Access: Public
@ -83,6 +105,17 @@ operator () (const Key &a, const Key &b) const {
return (a != b && (*a).get_name() < (*b).get_name());
}
////////////////////////////////////////////////////////////////////
// Function: indirect_compare_names::is_equal
// Access: Public
// Description: Returns true if a is equivalent to b, false otherwise.
////////////////////////////////////////////////////////////////////
template<class Key>
INLINE bool indirect_compare_names<Key>::
is_equal(const Key &a, const Key &b) const {
return (a == b || (*a).get_name() == (*b).get_name());
}
////////////////////////////////////////////////////////////////////
// Function: integer_hash::add_hash
// Access: Public, Static

View File

@ -29,18 +29,33 @@
#ifdef HAVE_STL_HASH
#include <hash_map> // for hash_compare
#define stl_hash_compare stdext::hash_compare
template<class Key, class Compare = less<Key> >
class stl_hash_compare : public stdext::hash_compare<Key, Compare> {
public:
INLINE bool is_equal(const Key &a, const Key &b) const {
return !operator()(a, b) && !operator()(b, a);
}
};
#else
#include <map> // for less
// This is declared for the cases in which we don't have STL_HASH
// available--it's just a name to inherit from, but there's no need to
// provide much functionality in the base class (since it won't
// actually be used for hashing, just for comparing).
// available.
template<class Key, class Compare = less<Key> >
class stl_hash_compare : public Compare {
public:
INLINE size_t operator () (const Key &key) const {
return (size_t)key;
}
INLINE bool operator () (const Key &a, const Key &b) const {
return Compare::operator ()(a, b);
}
INLINE bool is_equal(const Key &a, const Key &b) const {
return !operator()(a, b) && !operator()(b, a);
}
};
#endif // HAVE_STL_HASH
@ -69,6 +84,7 @@ template<class Key>
class compare_to {
public:
INLINE bool operator () (const Key &a, const Key &b) const;
INLINE bool is_equal(const Key &a, const Key &b) const;
};
////////////////////////////////////////////////////////////////////
@ -95,6 +111,7 @@ template<class Key>
class indirect_compare_to {
public:
INLINE bool operator () (const Key &a, const Key &b) const;
INLINE bool is_equal(const Key &a, const Key &b) const;
};
////////////////////////////////////////////////////////////////////
@ -110,6 +127,7 @@ template<class Key>
class indirect_compare_names {
public:
INLINE bool operator () (const Key &a, const Key &b) const;
INLINE bool is_equal(const Key &a, const Key &b) const;
};
////////////////////////////////////////////////////////////////////