diff --git a/dtool/LocalSetup.pp b/dtool/LocalSetup.pp index 2cf8ccb72e..44613912ed 100644 --- a/dtool/LocalSetup.pp +++ b/dtool/LocalSetup.pp @@ -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 diff --git a/dtool/src/dtoolbase/Sources.pp b/dtool/src/dtoolbase/Sources.pp index 132ea22231..06e995856c 100644 --- a/dtool/src/dtoolbase/Sources.pp +++ b/dtool/src/dtoolbase/Sources.pp @@ -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 \ diff --git a/dtool/src/dtoolbase/deletedBufferChain.I b/dtool/src/dtoolbase/deletedBufferChain.I new file mode 100644 index 0000000000..4595b50eaa --- /dev/null +++ b/dtool/src/dtoolbase/deletedBufferChain.I @@ -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 +} diff --git a/dtool/src/dtoolbase/deletedBufferChain.cxx b/dtool/src/dtoolbase/deletedBufferChain.cxx new file mode 100644 index 0000000000..d93677cb26 --- /dev/null +++ b/dtool/src/dtoolbase/deletedBufferChain.cxx @@ -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(); +} diff --git a/dtool/src/dtoolbase/deletedBufferChain.h b/dtool/src/dtoolbase/deletedBufferChain.h new file mode 100644 index 0000000000..fb1b505e66 --- /dev/null +++ b/dtool/src/dtoolbase/deletedBufferChain.h @@ -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 + +// 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 + diff --git a/dtool/src/dtoolbase/deletedChain.T b/dtool/src/dtoolbase/deletedChain.T index f6df5c3198..f881d53589 100644 --- a/dtool/src/dtoolbase/deletedChain.T +++ b/dtool/src/dtoolbase/deletedChain.T @@ -28,58 +28,14 @@ template INLINE Type *DeletedChain:: allocate(size_t size, TypeHandle type_handle) { TAU_PROFILE("Type *DeletedChain::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 INLINE void DeletedChain:: deallocate(Type *ptr, TypeHandle type_handle) { TAU_PROFILE("void DeletedChain::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 INLINE bool DeletedChain:: validate(const Type *ptr) { TAU_PROFILE("bool DeletedChain::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 -INLINE Type *DeletedChain:: -node_to_type(TYPENAME DeletedChain::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 -INLINE TYPENAME DeletedChain::ObjectNode *DeletedChain:: -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 -INLINE void DeletedChain:: -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 void DeletedChain:: -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); } //////////////////////////////////////////////////////////////////// diff --git a/dtool/src/dtoolbase/deletedChain.h b/dtool/src/dtoolbase/deletedChain.h index 82e3e3c220..a9fe760916 100644 --- a/dtool/src/dtoolbase/deletedChain.h +++ b/dtool/src/dtoolbase/deletedChain.h @@ -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 -// 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; }; //////////////////////////////////////////////////////////////////// diff --git a/dtool/src/dtoolbase/dtoolbase_composite1.cxx b/dtool/src/dtoolbase/dtoolbase_composite1.cxx index cd5bdf6f18..2a2ae9c1c9 100644 --- a/dtool/src/dtoolbase/dtoolbase_composite1.cxx +++ b/dtool/src/dtoolbase/dtoolbase_composite1.cxx @@ -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" diff --git a/dtool/src/dtoolbase/memoryHook.cxx b/dtool/src/dtoolbase/memoryHook.cxx index 1bc8b09456..f944c22e89 100644 --- a/dtool/src/dtoolbase/memoryHook.cxx +++ b/dtool/src/dtoolbase/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; +} diff --git a/dtool/src/dtoolbase/memoryHook.h b/dtool/src/dtoolbase/memoryHook.h index ff1d9d8577..199f033666 100644 --- a/dtool/src/dtoolbase/memoryHook.h +++ b/dtool/src/dtoolbase/memoryHook.h @@ -22,6 +22,10 @@ #include "dtoolbase.h" #include "numeric_types.h" #include "atomicAdjust.h" +#include "mutexImpl.h" +#include + +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 DeletedChains; + DeletedChains _deleted_chains; + + MutexImpl _lock; }; #include "memoryHook.I" diff --git a/dtool/src/dtoolbase/stl_compares.I b/dtool/src/dtoolbase/stl_compares.I index da0708bb71..65b907b590 100644 --- a/dtool/src/dtoolbase/stl_compares.I +++ b/dtool/src/dtoolbase/stl_compares.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 +INLINE bool compare_to:: +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 +INLINE bool indirect_compare_to:: +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 +INLINE bool indirect_compare_names:: +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 diff --git a/dtool/src/dtoolbase/stl_compares.h b/dtool/src/dtoolbase/stl_compares.h index 50ac4d9945..843b3c4637 100644 --- a/dtool/src/dtoolbase/stl_compares.h +++ b/dtool/src/dtoolbase/stl_compares.h @@ -29,18 +29,33 @@ #ifdef HAVE_STL_HASH #include // for hash_compare -#define stl_hash_compare stdext::hash_compare +template > +class stl_hash_compare : public stdext::hash_compare { +public: + INLINE bool is_equal(const Key &a, const Key &b) const { + return !operator()(a, b) && !operator()(b, a); + } +}; + #else #include // 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 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 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 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 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; }; ////////////////////////////////////////////////////////////////////