diff --git a/dtool/src/dtoolbase/deletedChain.T b/dtool/src/dtoolbase/deletedChain.T index 60e2e6bf3e..ffa9ecdfda 100644 --- a/dtool/src/dtoolbase/deletedChain.T +++ b/dtool/src/dtoolbase/deletedChain.T @@ -17,16 +17,11 @@ //////////////////////////////////////////////////////////////////// template -TVOLATILE TYPENAME DeletedChain::ObjectNode * TVOLATILE DeletedChain::_deleted_chain; - -#ifndef DELETED_CHAIN_USE_ATOMIC_EXCHANGE -template -MutexImpl *DeletedChain::_lock; -#endif +DeletedChain StaticDeletedChain::_chain; //////////////////////////////////////////////////////////////////// // Function: DeletedChain::allocate -// Access: Public, Static +// Access: Public // Description: Allocates the memory for a new object of Type. //////////////////////////////////////////////////////////////////// template @@ -232,7 +227,7 @@ type_to_node(Type *ptr) { #ifndef DELETED_CHAIN_USE_ATOMIC_EXCHANGE //////////////////////////////////////////////////////////////////// // Function: DeletedChain::init_lock -// Access: Private, Static +// Access: Private // Description: Ensures the lock pointer has been allocated. //////////////////////////////////////////////////////////////////// template @@ -247,7 +242,7 @@ init_lock() { #ifndef DELETED_CHAIN_USE_ATOMIC_EXCHANGE //////////////////////////////////////////////////////////////////// // Function: DeletedChain::do_init_lock -// Access: Private, Static +// Access: Private // Description: Allocates the lock pointer if necessary. Takes some // pains to protect itself from race conditions. // @@ -275,3 +270,41 @@ do_init_lock(MutexImpl *&lock) { assert(lock != (MutexImpl *)NULL); } #endif // DELETED_CHAIN_USE_ATOMIC_EXCHANGE + +//////////////////////////////////////////////////////////////////// +// Function: StaticDeletedChain::allocate +// Access: Public, Static +// Description: Allocates the memory for a new object of Type. +//////////////////////////////////////////////////////////////////// +template +INLINE Type *StaticDeletedChain:: +allocate(size_t size, TypeHandle type_handle) { + return _chain.allocate(size, type_handle); +} + +//////////////////////////////////////////////////////////////////// +// Function: StaticDeletedChain::deallocate +// Access: Public +// Description: Frees the memory for an object of Type. +//////////////////////////////////////////////////////////////////// +template +INLINE void StaticDeletedChain:: +deallocate(Type *ptr, TypeHandle type_handle) { + _chain.deallocate(ptr, type_handle); +} + +//////////////////////////////////////////////////////////////////// +// Function: StaticDeletedChain::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. +//////////////////////////////////////////////////////////////////// +template +INLINE bool StaticDeletedChain:: +validate(const Type *ptr) { + return _chain.validate(ptr); +} diff --git a/dtool/src/dtoolbase/deletedChain.h b/dtool/src/dtoolbase/deletedChain.h index ee3e99af97..635058f8f2 100644 --- a/dtool/src/dtoolbase/deletedChain.h +++ b/dtool/src/dtoolbase/deletedChain.h @@ -82,10 +82,10 @@ enum DeletedChainFlag { template class DeletedChain { public: - INLINE static Type *allocate(size_t size, TypeHandle type_handle); - INLINE static void deallocate(Type *ptr, TypeHandle type_handle); + INLINE Type *allocate(size_t size, TypeHandle type_handle); + INLINE void deallocate(Type *ptr, TypeHandle type_handle); - INLINE static bool validate(const Type *ptr); + INLINE bool validate(const Type *ptr); private: class ObjectNode { @@ -105,44 +105,92 @@ private: TVOLATILE ObjectNode * TVOLATILE _next; }; - INLINE static Type *node_to_type(TVOLATILE ObjectNode *node); - INLINE static ObjectNode *type_to_node(Type *ptr); - - // Ideally, the compiler and linker will unify all references to - // this static pointer for a given type, as per the C++ spec. - // However, if the compiler fails to do this (*cough* Microsoft), it - // won't be a big deal; it just means there will be multiple - // unrelated chains of deleted objects for a particular type. - static TVOLATILE ObjectNode * TVOLATILE _deleted_chain; + static INLINE Type *node_to_type(TVOLATILE ObjectNode *node); + static INLINE ObjectNode *type_to_node(Type *ptr); + TVOLATILE ObjectNode * TVOLATILE _deleted_chain; + #ifndef DELETED_CHAIN_USE_ATOMIC_EXCHANGE // If we don't have atomic compare-and-exchange, we need to use a // Mutex to protect the above linked list. - static INLINE void init_lock(); - static void do_init_lock(MutexImpl *&lock); - static MutexImpl *_lock; + INLINE void init_lock(); + void do_init_lock(MutexImpl *&lock); + MutexImpl *_lock; #endif }; +//////////////////////////////////////////////////////////////////// +// Class : StaticDeletedChain +// Description : This template class is used to conveniently +// declare a single instance of the DeletedChain +// template object, above, for a particular type. +// +// It relies on the fact that the compiler and linker +// should unify all references to this static pointer +// for a given type, as per the C++ spec. However, this +// sometimes fails; and if the compiler fails to do +// this, it mostly won't be a big deal; it just means +// there will be multiple unrelated chains of deleted +// objects for a particular type. This is only a +// problem if the code structure causes objects to be +// allocated from one chain and freed to another, which +// can lead to leaks. +//////////////////////////////////////////////////////////////////// +template +class StaticDeletedChain { +public: + INLINE static Type *allocate(size_t size, TypeHandle type_handle); + INLINE static void deallocate(Type *ptr, TypeHandle type_handle); + + INLINE static bool validate(const Type *ptr); + + static DeletedChain _chain; +}; + // Place this macro within a class definition to define appropriate // operator new and delete methods that take advantage of // DeletedChain. #define ALLOC_DELETED_CHAIN(Type) \ inline void *operator new(size_t size) { \ - return (void *)DeletedChain< Type >::allocate(size, get_type_handle(Type)); \ + return (void *)StaticDeletedChain< Type >::allocate(size, get_type_handle(Type)); \ } \ inline void *operator new(size_t size, void *ptr) { \ return ptr; \ } \ inline void operator delete(void *ptr) { \ - DeletedChain< Type >::deallocate((Type *)ptr, get_type_handle(Type)); \ + StaticDeletedChain< Type >::deallocate((Type *)ptr, get_type_handle(Type)); \ } \ inline void operator delete(void *, void *) { \ } \ inline static bool validate_ptr(const void *ptr) { \ - return DeletedChain< Type >::validate((const Type *)ptr); \ + return StaticDeletedChain< Type >::validate((const Type *)ptr); \ } +// Use this variant of the above macro in cases in which the compiler +// fails to unify the static template pointers properly, to prevent +// leaks. +#define ALLOC_DELETED_CHAIN_DECL(Type) \ + inline void *operator new(size_t size) { \ + return (void *)_deleted_chain.allocate(size, get_type_handle(Type)); \ + } \ + inline void *operator new(size_t size, void *ptr) { \ + return ptr; \ + } \ + inline void operator delete(void *ptr) { \ + _deleted_chain.deallocate((Type *)ptr, get_type_handle(Type)); \ + } \ + inline void operator delete(void *, void *) { \ + } \ + inline static bool validate_ptr(const void *ptr) { \ + return _deleted_chain.validate((const Type *)ptr); \ + } \ + static DeletedChain< Type > _deleted_chain; + +// When you use ALLOC_DELETED_CHAIN_DECL in a class body, you must +// also put this line in the .cxx file defining that class body. +#define ALLOC_DELETED_CHAIN_DEF(Type) \ + DeletedChain< Type > Type::_deleted_chain; + #include "deletedChain.T" #endif diff --git a/dtool/src/dtoolbase/pallocator.T b/dtool/src/dtoolbase/pallocator.T index 56caf41c95..65ea19ae68 100644 --- a/dtool/src/dtoolbase/pallocator.T +++ b/dtool/src/dtoolbase/pallocator.T @@ -29,14 +29,14 @@ allocate(TYPENAME pallocator_single::size_type n, TYPENAME allocator TAU_PROFILE("pallocator_single:allocate()", " ", TAU_USER); // This doesn't support allocating arrays. assert(n == 1); - return DeletedChain::allocate(sizeof(Type), _type_handle); + return StaticDeletedChain::allocate(sizeof(Type), _type_handle); } template INLINE void pallocator_single:: deallocate(TYPENAME pallocator_single::pointer p, TYPENAME pallocator_single::size_type) { TAU_PROFILE("pallocator_single:deallocate()", " ", TAU_USER); - return DeletedChain::deallocate(p, _type_handle); + StaticDeletedChain::deallocate(p, _type_handle); } template diff --git a/panda/src/gobj/geomVertexArrayData.cxx b/panda/src/gobj/geomVertexArrayData.cxx index 1b2aa647a2..1325c4b8e7 100644 --- a/panda/src/gobj/geomVertexArrayData.cxx +++ b/panda/src/gobj/geomVertexArrayData.cxx @@ -53,6 +53,8 @@ TypeHandle GeomVertexArrayData::_type_handle; TypeHandle GeomVertexArrayData::CData::_type_handle; TypeHandle GeomVertexArrayDataHandle::_type_handle; +ALLOC_DELETED_CHAIN_DEF(GeomVertexArrayDataHandle); + //////////////////////////////////////////////////////////////////// // Function: GeomVertexArrayData::Default Constructor // Access: Private diff --git a/panda/src/gobj/geomVertexArrayData.h b/panda/src/gobj/geomVertexArrayData.h index 42fffccbaa..270bfdf673 100644 --- a/panda/src/gobj/geomVertexArrayData.h +++ b/panda/src/gobj/geomVertexArrayData.h @@ -252,7 +252,7 @@ PUBLISHED: INLINE ~GeomVertexArrayDataHandle(); public: - ALLOC_DELETED_CHAIN(GeomVertexArrayDataHandle); + ALLOC_DELETED_CHAIN_DECL(GeomVertexArrayDataHandle); INLINE Thread *get_current_thread() const; INLINE const GeomVertexArrayData *get_object() const;