mirror of
https://github.com/panda3d/panda3d.git
synced 2025-10-04 10:54:24 -04:00
DeletedBufferChain
This commit is contained in:
parent
b2fb75e867
commit
982f614991
@ -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
|
||||
|
@ -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 \
|
||||
|
86
dtool/src/dtoolbase/deletedBufferChain.I
Normal file
86
dtool/src/dtoolbase/deletedBufferChain.I
Normal 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
|
||||
}
|
134
dtool/src/dtoolbase/deletedBufferChain.cxx
Normal file
134
dtool/src/dtoolbase/deletedBufferChain.cxx
Normal 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();
|
||||
}
|
113
dtool/src/dtoolbase/deletedBufferChain.h
Normal file
113
dtool/src/dtoolbase/deletedBufferChain.h
Normal 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
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
@ -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"
|
||||
|
@ -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 ©) :
|
||||
_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;
|
||||
}
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
Loading…
x
Reference in New Issue
Block a user