better thread protection in reference counts, esp. TransformState etc.

This commit is contained in:
David Rose 2007-07-14 21:32:48 +00:00
parent e2bfe71e05
commit 68cd3322a9
41 changed files with 602 additions and 897 deletions

View File

@ -51,6 +51,9 @@
#define USE_DL_PREFIX 1
#define NO_MALLINFO 1
#ifdef _DEBUG
#define DEBUG
#endif
#include "dlmalloc.h"
#include "dlmalloc_src.cxx"
@ -77,6 +80,9 @@
#define USE_DL_PREFIX 1
#define NO_MALLINFO 1
#ifdef _DEBUG
#define MALLOC_DEBUG 2
#endif
#include "ptmalloc2_smp_src.cxx"
#define call_malloc dlmalloc

View File

@ -175,6 +175,15 @@ GraphicsEngine::
////////////////////////////////////////////////////////////////////
void GraphicsEngine::
set_threading_model(const GraphicsThreadingModel &threading_model) {
if (!Thread::is_threading_supported()) {
if (!threading_model.is_single_threaded()) {
display_cat.warning()
<< "Threading model " << threading_model
<< " requested but threading is not available. Ignoring.\n";
return;
}
}
#ifndef THREADED_PIPELINE
if (!threading_model.is_single_threaded()) {
display_cat.warning()
@ -688,14 +697,6 @@ render_frame() {
#endif // THREADED_PIPELINE && DO_PSTATS
// If there is an object deletor, tell it to flush now, while we're
// between frames.
ObjectDeletor *deletor = ObjectDeletor::get_global_ptr();
if (deletor != (ObjectDeletor *)NULL) {
PStatTimer timer(_delete_pcollector, current_thread);
deletor->flush();
}
GeomCacheManager::flush_level();
CullTraverser::flush_level();
RenderState::flush_level();
@ -1960,7 +1961,8 @@ get_window_renderer(const string &name, int pipeline_stage) {
thread->set_min_pipeline_stage(pipeline_stage);
_pipeline->set_min_stages(pipeline_stage + 1);
thread->start(TP_normal, true);
bool started = thread->start(TP_normal, true);
nassertr(started, thread.p());
_threads[name] = thread;
nassertr(thread->get_pipeline_stage() < _pipeline->get_num_stages(), thread.p());

View File

@ -32,7 +32,6 @@
nodePointerToBase.h nodePointerToBase.I \
nodePointerTo.h nodePointerTo.I \
nodeReferenceCount.h nodeReferenceCount.I \
objectDeletor.h objectDeletor.I \
ordered_vector.h ordered_vector.I ordered_vector.T \
password_hash.h \
patchfile.I patchfile.h \
@ -84,7 +83,6 @@
nodePointerToBase.cxx \
nodePointerTo.cxx \
nodeReferenceCount.cxx \
objectDeletor.cxx \
ordered_vector.cxx \
password_hash.cxx \
patchfile.cxx \
@ -142,7 +140,6 @@
nodePointerToBase.h nodePointerToBase.I \
nodePointerTo.h nodePointerTo.I \
nodeReferenceCount.h nodeReferenceCount.I \
objectDeletor.h objectDeletor.I \
ordered_vector.h ordered_vector.I ordered_vector.T \
password_hash.h \
patchfile.I patchfile.h \

View File

@ -19,7 +19,6 @@
#include "nodePointerToBase.cxx"
#include "nodePointerTo.cxx"
#include "nodeReferenceCount.cxx"
#include "objectDeletor.cxx"
#include "ordered_vector.cxx"
#include "patchfile.cxx"
#include "password_hash.cxx"

View File

@ -147,14 +147,8 @@ get_node_ref_count() const {
////////////////////////////////////////////////////////////////////
// Function: NodeReferenceCount::node_ref
// Access: Published
// Description: Explicitly increments the reference count.
//
// This function is const, even though it changes the
// object, because generally fiddling with an object's
// reference count isn't considered part of fiddling
// with the object. An object might be const in other
// ways, but we still need to accurately count the
// number of references to it.
// Description: Explicitly increments the node reference count and
// the normal reference count simultaneously.
////////////////////////////////////////////////////////////////////
INLINE void NodeReferenceCount::
node_ref() const {
@ -169,42 +163,16 @@ node_ref() const {
////////////////////////////////////////////////////////////////////
// Function: NodeReferenceCount::node_unref
// Access: Published
// Description: Explicitly decrements the reference count. Note that
// the object will not be implicitly deleted by unref()
// simply because the reference count drops to zero.
// (Having a member function delete itself is
// problematic; plus, we don't have a virtual destructor
// anyway.) However, see the helper function
// unref_delete().
//
// User code should avoid using ref() and unref()
// directly, which can result in missed reference
// counts. Instead, let a PointerTo object manage the
// reference counting automatically.
//
// This function is const, even though it changes the
// object, because generally fiddling with an object's
// reference count isn't considered part of fiddling
// with the object. An object might be const in other
// ways, but we still need to accurately count the
// number of references to it.
// Description: Explicitly decrements the node reference count and
// the normal reference count simultaneously.
//
// The return value is true if the new reference count
// is nonzero, false if it is zero.
////////////////////////////////////////////////////////////////////
INLINE bool NodeReferenceCount::
node_unref() const {
#ifdef _DEBUG
nassertr(test_ref_count_integrity(), 0);
#endif
// If this assertion fails, you tried to unref an object with a
// zero reference count. Are you using ref() and unref()
// directly? Are you sure you can't use PointerTo's?
nassertr(_node_ref_count > 0, 0);
unref();
return AtomicAdjust::dec(((NodeReferenceCount *)this)->_node_ref_count);
node_unref_only();
return unref();
}
////////////////////////////////////////////////////////////////////
@ -222,6 +190,28 @@ test_ref_count_integrity() const {
#endif
}
////////////////////////////////////////////////////////////////////
// Function: NodeReferenceCount::node_unref_only
// Access: Protected
// Description: Decrements the node reference count without affecting
// the normal reference count. Intended to be called by
// derived classes only, presumably to reimplement
// node_unref().
////////////////////////////////////////////////////////////////////
INLINE void NodeReferenceCount::
node_unref_only() const {
#ifdef _DEBUG
nassertv(test_ref_count_integrity());
#endif
// If this assertion fails, you tried to unref an object with a
// zero reference count. Are you using ref() and unref()
// directly? Are you sure you can't use PointerTo's?
nassertv(_node_ref_count > 0);
AtomicAdjust::dec(((NodeReferenceCount *)this)->_node_ref_count);
}
////////////////////////////////////////////////////////////////////
// Function: node_unref_delete
// Description: This global helper function will unref the given
@ -235,15 +225,7 @@ test_ref_count_integrity() const {
template<class RefCountType>
INLINE void
node_unref_delete(RefCountType *ptr) {
ptr->node_unref();
if (ptr->get_ref_count() == 0) {
ObjectDeletor *deletor = ObjectDeletor::get_global_ptr();
if (deletor != (ObjectDeletor *)NULL) {
ptr->ref();
deletor->delete_object(RefCountDeleteWrapper<RefCountType>::do_delete, (void *)ptr);
return;
}
if (!ptr->node_unref()) {
delete ptr;
}
}

View File

@ -57,6 +57,8 @@ PUBLISHED:
INLINE bool test_ref_count_integrity() const;
protected:
INLINE void node_unref_only() const;
bool do_test_ref_count_integrity() const;
private:

View File

@ -1,62 +0,0 @@
// Filename: objectDeletor.I
// Created by: drose (10Apr06)
//
////////////////////////////////////////////////////////////////////
//
// 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: ObjectDeletor::get_global_ptr
// Access: Public, Static
// Description: Returns the global ObjectDeletor object. This may
// return NULL if there is no such object.
////////////////////////////////////////////////////////////////////
INLINE ObjectDeletor *ObjectDeletor::
get_global_ptr() {
return (ObjectDeletor *)AtomicAdjust::get_ptr(_global_ptr);
}
////////////////////////////////////////////////////////////////////
// Function: ObjectDeletor::set_global_ptr
// Access: Public, Static
// Description: Assigns the global ObjectDeletor object. Returns
// the pointer to the previous object, if any.
////////////////////////////////////////////////////////////////////
INLINE ObjectDeletor *ObjectDeletor::
set_global_ptr(ObjectDeletor *ptr) {
return (ObjectDeletor *)AtomicAdjust::set_ptr(_global_ptr, ptr);
}
////////////////////////////////////////////////////////////////////
// Function: ObjectDeletor::DeleteToken::Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
INLINE ObjectDeletor::DeleteToken::
DeleteToken(DeleteFunc *func, void *ptr) :
_func(func),
_ptr(ptr)
{
}
////////////////////////////////////////////////////////////////////
// Function: ObjectDeletor::DeleteToken::do_delete
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
INLINE void ObjectDeletor::DeleteToken::
do_delete() {
(*_func)(_ptr);
}

View File

@ -1,85 +0,0 @@
// Filename: objectDeletor.h
// Created by: drose (10Apr06)
//
////////////////////////////////////////////////////////////////////
//
// 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 "objectDeletor.h"
#include "configVariableString.h"
#include "config_express.h"
void *ObjectDeletor::_global_ptr = NULL;
////////////////////////////////////////////////////////////////////
// Function: ObjectDeletor::Destructor
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
ObjectDeletor::
~ObjectDeletor() {
}
////////////////////////////////////////////////////////////////////
// Function: ObjectDeletor::delete_object
// Access: Public, Virtual
// Description: Adds the pointer to the object to be deleted, along
// with a pointer to a function that can delete it.
////////////////////////////////////////////////////////////////////
void ObjectDeletor::
delete_object(DeleteFunc *func, void *ptr) {
// The base class functionality simply deletes the pointer
// immediately.
(*func)(ptr);
}
////////////////////////////////////////////////////////////////////
// Function: ObjectDeletor::flush
// Access: Public, Virtual
// Description: Ensures that any objects queued up for deletion have
// been fully deleted by the time flush() returns.
////////////////////////////////////////////////////////////////////
void ObjectDeletor::
flush() {
}
////////////////////////////////////////////////////////////////////
// Function: ObjectDeletor::register_subclass
// Access: Public, Static
// Description: Called at static init time as each subclass is
// defined. The purpose here is to consult the config
// variable object-deletor and install the requested
// deletor. If the indicated deletor is the one that is
// named by the config variable, it is installed.
////////////////////////////////////////////////////////////////////
void ObjectDeletor::
register_subclass(ObjectDeletor *deletor, const string &name) {
ConfigVariableString object_deletor
("object-deletor", "",
PRC_DESC("Specify the type of ObjectDeletor to install. This string "
"must match one of the existing ObjectDeletor types, and if "
"it does not match, no error message is generated. To "
"determine if the ObjectDeletor was properly installed, "
"set notify-level-express debug."));
if (object_deletor == name) {
if (express_cat.is_debug()) {
express_cat.debug()
<< "Installing ObjectDeletor type " << name << "\n";
}
set_global_ptr(deletor);
} else {
delete deletor;
}
}

View File

@ -1,72 +0,0 @@
// Filename: objectDeletor.h
// Created by: drose (10Apr06)
//
////////////////////////////////////////////////////////////////////
//
// 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 OBJECTDELETOR_H
#define OBJECTDELETOR_H
#include "pandabase.h"
#include "atomicAdjust.h"
////////////////////////////////////////////////////////////////////
// Class : ObjectDeletor
// Description : This class is used to collect together pointers to
// objects that are ready to be destructed and freed.
// The actual destruction may be performed immediately,
// or it can be performed at some later time, when it is
// convenient for the application.
//
// This is particularly useful for a multithreaded
// application; the destruction may be performed in a
// sub-thread with a lower priority.
//
// This class body is just an interface; the
// ObjectDeletor class simply deletes its pointers
// immediately. More sophisticated deletors are
// implemented elsewhere.
////////////////////////////////////////////////////////////////////
class EXPCL_PANDAEXPRESS ObjectDeletor {
public:
virtual ~ObjectDeletor();
typedef void DeleteFunc(void *ptr);
virtual void delete_object(DeleteFunc *func, void *ptr);
virtual void flush();
INLINE static ObjectDeletor *get_global_ptr();
INLINE static ObjectDeletor *set_global_ptr(ObjectDeletor *ptr);
static void register_subclass(ObjectDeletor *deletor, const string &name);
protected:
class DeleteToken {
public:
INLINE DeleteToken(DeleteFunc *func, void *ptr);
INLINE void do_delete();
DeleteFunc *_func;
void *_ptr;
};
private:
static void *_global_ptr;
};
#include "objectDeletor.I"
#endif

View File

@ -348,41 +348,6 @@ weak_unref(WeakPointerToVoid *ptv) {
_weak_list->clear_reference(ptv);
}
////////////////////////////////////////////////////////////////////
// Function: DeleteWrapper::do_delete
// Access: Public, Static
// Description: This helper function encapsulates calling the
// destructor and delete operator for the given pointer.
// A pointer to this function is passed to the global
// ObjectDeletor, when there is one.
////////////////////////////////////////////////////////////////////
template<class ObjectType>
void DeleteWrapper<ObjectType>::
do_delete(void *ptr) {
TAU_PROFILE("void DeleteWrapper::do_delete(void *)", " ", TAU_USER);
delete ((ObjectType *)ptr);
}
////////////////////////////////////////////////////////////////////
// Function: RefCountDeleteWrapper::do_delete
// Access: Public, Static
// Description: This is similar to DeleteWrapper::do_delete(), above,
// except it is specialized for an object that inherits
// from ReferenceCount, and it will check that some
// other pointer hasn't incremented the reference count
// in the interim before the pointer is actually
// deleted.
////////////////////////////////////////////////////////////////////
template<class RefCountType>
void RefCountDeleteWrapper<RefCountType>::
do_delete(void *ptr) {
TAU_PROFILE("void RefCountDeleteWrapper::do_delete(void *)", " ", TAU_USER);
if (!((RefCountType *)ptr)->unref()) {
// The reference count has reached 0; time to delete the pointer.
delete ((RefCountType *)ptr);
}
}
////////////////////////////////////////////////////////////////////
// Function: unref_delete
// Description: This global helper function will unref the given
@ -404,59 +369,11 @@ unref_delete(RefCountType *ptr) {
// overloading of the unref() method.
if (!ptr->unref()) {
// The reference count has reached 0; time to delete the pointer.
// Is there a deletor in effect? If so, pass it to that object.
ObjectDeletor *deletor = ObjectDeletor::get_global_ptr();
if (deletor != (ObjectDeletor *)NULL) {
// Re-ref the pointer before adding it to the deletor. The
// deletor will de-ref it again before deleting it.
ptr->ref();
deletor->delete_object(RefCountDeleteWrapper<RefCountType>::do_delete, (void *)ptr);
return;
}
// If there's no deletor, just delete the pointer now.
// If the reference count has gone to zero, delete the object.
delete ptr;
}
}
}
////////////////////////////////////////////////////////////////////
// Function: defer_delete
// Description: This global helper function is designed to be called
// in lieu of the delete operator on any type of object
// (except for an object that inherits from
// ReferenceCount, of course, since you should never
// call delete explicitly on a reference-counted
// object).
//
// This function will ultimately have the same effect as
// calling delete on the object. If a ObjectDeletor
// is currently in effect, the delete will happen at
// some later time; but if a ObjectDeletor is not in
// effect, the delete will happen immediately.
//
// You should not attempt to replace an array-delete
// call, e.g. delete[] array, with a call to
// defer_delete(). This only replaces single-object
// deletes, e.g. delete object.
////////////////////////////////////////////////////////////////////
template<class ObjectType>
INLINE void
defer_delete(ObjectType *ptr) {
TAU_PROFILE("void defer_delete(ObjectType *)", " ", TAU_USER);
ObjectDeletor *deletor = ObjectDeletor::get_global_ptr();
if (deletor != (ObjectDeletor *)NULL) {
deletor->delete_object(DeleteWrapper<ObjectType>::do_delete, (void *)ptr);
return;
}
// If there's no deletor, just delete the pointer now.
delete ptr;
}
////////////////////////////////////////////////////////////////////
// Function: RefCountProxy::Constructor
// Access: Public

View File

@ -105,24 +105,9 @@ private:
static TypeHandle _type_handle;
};
template<class ObjectType>
class DeleteWrapper {
public:
static void do_delete(void *ptr);
};
template<class RefCountType>
class RefCountDeleteWrapper {
public:
static void do_delete(void *ptr);
};
template<class RefCountType>
INLINE void unref_delete(RefCountType *ptr);
template<class ObjectType>
INLINE void defer_delete(ObjectType *ptr);
////////////////////////////////////////////////////////////////////
// Class : RefCountProxy
// Description : A "proxy" to use to make a reference-countable object

View File

@ -24,6 +24,7 @@
#include "bamReader.h"
#include "bamWriter.h"
#include "indirectLess.h"
#include "mutexHolder.h"
GeomVertexArrayFormat::Registry *GeomVertexArrayFormat::_registry = NULL;
TypeHandle GeomVertexArrayFormat::_type_handle;
@ -186,15 +187,38 @@ operator = (const GeomVertexArrayFormat &copy) {
////////////////////////////////////////////////////////////////////
GeomVertexArrayFormat::
~GeomVertexArrayFormat() {
if (is_registered()) {
get_registry()->unregister_format(this);
}
// unref() should have unregistered us.
nassertv(!is_registered());
Columns::iterator ci;
for (ci = _columns.begin(); ci != _columns.end(); ++ci) {
delete (*ci);
}
}
////////////////////////////////////////////////////////////////////
// Function: GeomVertexArrayFormat::unref
// Access: Published
// Description: This method overrides ReferenceCount::unref() to
// unregister the object when its reference count goes
// to zero.
////////////////////////////////////////////////////////////////////
bool GeomVertexArrayFormat::
unref() const {
Registry *registry = get_registry();
MutexHolder holder(registry->_lock);
if (ReferenceCount::unref()) {
return true;
}
if (is_registered()) {
registry->unregister_format((GeomVertexArrayFormat *)this);
}
return false;
}
////////////////////////////////////////////////////////////////////
// Function: GeomVertexArrayFormat::add_column
// Access: Published
@ -759,12 +783,15 @@ register_format(GeomVertexArrayFormat *format) {
// a zero reference count and is not added into the map below, it
// will be automatically deleted when this function returns.
PT(GeomVertexArrayFormat) pt_format = format;
ArrayFormats::iterator fi = _formats.insert(format).first;
GeomVertexArrayFormat *new_format = (*fi);
if (!new_format->is_registered()) {
new_format->do_register();
GeomVertexArrayFormat *new_format;
{
MutexHolder holder(_lock);
ArrayFormats::iterator fi = _formats.insert(format).first;
new_format = (*fi);
if (!new_format->is_registered()) {
new_format->do_register();
}
}
return new_format;
@ -776,6 +803,8 @@ register_format(GeomVertexArrayFormat *format) {
// Description: Removes the indicated format from the registry.
// Normally this should not be done until the format is
// destructing.
//
// The lock should be held prior to calling this method.
////////////////////////////////////////////////////////////////////
void GeomVertexArrayFormat::Registry::
unregister_format(GeomVertexArrayFormat *format) {

View File

@ -26,6 +26,7 @@
#include "indirectCompareTo.h"
#include "pvector.h"
#include "pmap.h"
#include "pmutex.h"
class GeomVertexFormat;
class GeomVertexData;
@ -80,6 +81,8 @@ PUBLISHED:
void operator = (const GeomVertexArrayFormat &copy);
~GeomVertexArrayFormat();
bool unref() const;
INLINE bool is_registered() const;
INLINE static CPT(GeomVertexArrayFormat) register_format(const GeomVertexArrayFormat *format);
@ -147,6 +150,7 @@ private:
void unregister_format(GeomVertexArrayFormat *format);
ArrayFormats _formats;
Mutex _lock;
};
static Registry *_registry;

View File

@ -19,7 +19,7 @@
#include "geomVertexFormat.h"
#include "geomVertexData.h"
#include "geomMunger.h"
#include "mutexHolder.h"
#include "reMutexHolder.h"
#include "indent.h"
#include "bamReader.h"
#include "bamWriter.h"
@ -86,9 +86,31 @@ operator = (const GeomVertexFormat &copy) {
////////////////////////////////////////////////////////////////////
GeomVertexFormat::
~GeomVertexFormat() {
if (is_registered()) {
get_registry()->unregister_format(this);
// unref() should have unregistered us.
nassertv(!is_registered());
}
////////////////////////////////////////////////////////////////////
// Function: GeomVertexFormat::unref
// Access: Published
// Description: This method overrides ReferenceCount::unref() to
// unregister the object when its reference count goes
// to zero.
////////////////////////////////////////////////////////////////////
bool GeomVertexFormat::
unref() const {
Registry *registry = get_registry();
ReMutexHolder holder(registry->_lock);
if (ReferenceCount::unref()) {
return true;
}
if (is_registered()) {
registry->unregister_format((GeomVertexFormat *)this);
}
return false;
}
@ -669,6 +691,7 @@ void GeomVertexFormat::
make_registry() {
if (_registry == (Registry *)NULL) {
_registry = new Registry;
_registry->make_standard_formats();
}
}
@ -897,6 +920,15 @@ fillin(DatagramIterator &scan, BamReader *manager) {
////////////////////////////////////////////////////////////////////
GeomVertexFormat::Registry::
Registry() {
}
////////////////////////////////////////////////////////////////////
// Function: GeomVertexFormat::Registry::make_standard_formats
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
void GeomVertexFormat::Registry::
make_standard_formats() {
_v3 = register_format(new GeomVertexArrayFormat
(InternalName::get_vertex(), 3,
NT_float32, C_point));
@ -1016,11 +1048,14 @@ register_format(GeomVertexFormat *format) {
// will be automatically deleted when this function returns.
PT(GeomVertexFormat) pt_format = format;
Formats::iterator fi = _formats.insert(format).first;
GeomVertexFormat *new_format = (*fi);
if (!new_format->is_registered()) {
new_format->do_register();
GeomVertexFormat *new_format;
{
ReMutexHolder holder(_lock);
Formats::iterator fi = _formats.insert(format).first;
new_format = (*fi);
if (!new_format->is_registered()) {
new_format->do_register();
}
}
return new_format;
@ -1032,6 +1067,8 @@ register_format(GeomVertexFormat *format) {
// Description: Removes the indicated format from the registry.
// Normally this should not be done until the format is
// destructing.
//
// The lock should be held prior to calling this method.
////////////////////////////////////////////////////////////////////
void GeomVertexFormat::Registry::
unregister_format(GeomVertexFormat *format) {

View File

@ -31,7 +31,7 @@
#include "pset.h"
#include "pvector.h"
#include "indirectCompareTo.h"
#include "pmutex.h"
#include "reMutex.h"
class FactoryParams;
class GeomVertexData;
@ -70,6 +70,8 @@ PUBLISHED:
void operator = (const GeomVertexFormat &copy);
virtual ~GeomVertexFormat();
bool unref() const;
INLINE bool is_registered() const;
INLINE static CPT(GeomVertexFormat) register_format(const GeomVertexFormat *format);
INLINE static CPT(GeomVertexFormat) register_format(const GeomVertexArrayFormat *format);
@ -213,11 +215,14 @@ private:
class EXPCL_PANDA Registry {
public:
Registry();
void make_standard_formats();
CPT(GeomVertexFormat) register_format(GeomVertexFormat *format);
INLINE CPT(GeomVertexFormat) register_format(GeomVertexArrayFormat *format);
void unregister_format(GeomVertexFormat *format);
Formats _formats;
ReMutex _lock;
CPT(GeomVertexFormat) _v3;
CPT(GeomVertexFormat) _v3n3;

View File

@ -66,11 +66,44 @@ InternalName(InternalName *parent, const string &basename) :
////////////////////////////////////////////////////////////////////
InternalName::
~InternalName() {
if (_parent != (const InternalName *)NULL) {
#ifndef NDEBUG
{
// unref() should have removed us from our parent's table already.
MutexHolder holder(_parent->_name_table_lock);
NameTable::iterator ni = _parent->_name_table.find(_basename);
nassertv(ni != _parent->_name_table.end());
_parent->_name_table.erase(ni);
nassertv(ni == _parent->_name_table.end());
}
#endif
}
////////////////////////////////////////////////////////////////////
// Function: InternalName::unref
// Access: Published
// Description: This method overrides ReferenceCount::unref() to
// clear the pointer from its parent's table when
// its reference count goes to zero.
////////////////////////////////////////////////////////////////////
bool InternalName::
unref() const {
if (_parent == (const InternalName *)NULL) {
// No parent; no problem. This is the root InternalName.
// Actually, this probably shouldn't be destructing, but I guess
// it might at application shutdown.
return TypedWritableReferenceCount::unref();
}
MutexHolder holder(_parent->_name_table_lock);
if (ReferenceCount::unref()) {
return true;
}
// The reference count has just reached zero.
NameTable::iterator ni = _parent->_name_table.find(_basename);
nassertr(ni != _parent->_name_table.end(), false);
_parent->_name_table.erase(ni);
return false;
}
////////////////////////////////////////////////////////////////////
@ -94,12 +127,14 @@ append(const string &name) {
return append(name.substr(0, dot))->append(name.substr(dot + 1));
}
MutexHolder holder(_name_table_lock);
NameTable::iterator ni = _name_table.find(name);
if (ni != _name_table.end()) {
return (*ni).second;
}
PT(InternalName) internal_name = new InternalName(this, name);
InternalName *internal_name = new InternalName(this, name);
_name_table[name] = internal_name;
return internal_name;
}

View File

@ -24,6 +24,7 @@
#include "typedWritableReferenceCount.h"
#include "pointerTo.h"
#include "pmap.h"
#include "pmutex.h"
class FactoryParams;
@ -48,6 +49,8 @@ private:
PUBLISHED:
virtual ~InternalName();
bool unref() const;
INLINE static PT(InternalName) make(const string &name);
PT(InternalName) append(const string &basename);
@ -93,6 +96,7 @@ private:
typedef phash_map<string, InternalName *, string_hash> NameTable;
NameTable _name_table;
Mutex _name_table_lock;
static PT(InternalName) _root;
static PT(InternalName) _error;

View File

@ -21,7 +21,9 @@
#include "bamReader.h"
#include "indent.h"
#include "config_pgraph.h"
#include "reMutexHolder.h"
ReMutex *RenderAttrib::_attribs_lock = NULL;
RenderAttrib::Attribs *RenderAttrib::_attribs = NULL;
TypeHandle RenderAttrib::_type_handle;
@ -33,12 +35,7 @@ TypeHandle RenderAttrib::_type_handle;
RenderAttrib::
RenderAttrib() {
if (_attribs == (Attribs *)NULL) {
// Make sure the global _attribs map is allocated. This only has
// to be done once. We could make this map static, but then we
// run into problems if anyone creates a RenderState object at
// static init time; it also seems to cause problems when the
// Panda shared library is unloaded at application exit time.
_attribs = new Attribs;
init_attribs();
}
_saved_entry = _attribs->end();
@ -73,24 +70,10 @@ operator = (const RenderAttrib &) {
////////////////////////////////////////////////////////////////////
RenderAttrib::
~RenderAttrib() {
if (_saved_entry != _attribs->end()) {
// We cannot make this assertion, because the RenderAttrib has
// already partially destructed--this means we cannot look up the
// object in the map. In fact, the map is temporarily invalid
// until we finish destructing, since we screwed up the ordering
// when we changed the return value of get_type().
// nassertv(_attribs->find(this) == _saved_entry);
ReMutexHolder holder(*_attribs_lock);
// Note: this isn't thread-safe, because once the derived class
// destructor exits and before this destructor completes, the map
// is invalid, and other threads may inadvertently attempt to read
// the invalid map. To make it thread-safe, we need to move this
// functionality to a separate method, that is to be called from
// *each* derived class's destructor (and then we can put the
// above assert back in).
_attribs->erase(_saved_entry);
_saved_entry = _attribs->end();
}
// unref() should have cleared this.
nassertv(_saved_entry == _attribs->end());
}
////////////////////////////////////////////////////////////////////
@ -157,6 +140,32 @@ cull_callback(CullTraverser *, const CullTraverserData &) const {
return true;
}
////////////////////////////////////////////////////////////////////
// Function: RenderAttrib::unref
// Access: Published
// Description: This method overrides ReferenceCount::unref() to
// clear the pointer from the global object pool when
// its reference count goes to zero.
////////////////////////////////////////////////////////////////////
bool RenderAttrib::
unref() const {
// We always have to grab the lock, since we will definitely need to
// be holding it if we happen to drop the reference count to 0.
ReMutexHolder holder(*_attribs_lock);
if (ReferenceCount::unref()) {
// The reference count is still nonzero.
return true;
}
// The reference count has just reached zero. Make sure the object
// is removed from the global object pool, before anyone else finds
// it and tries to ref it.
((RenderAttrib *)this)->release_new();
return false;
}
////////////////////////////////////////////////////////////////////
// Function: RenderAttrib::output
// Access: Published, Virtual
@ -186,6 +195,8 @@ write(ostream &out, int indent_level) const {
////////////////////////////////////////////////////////////////////
int RenderAttrib::
get_num_attribs() {
ReMutexHolder holder(*_attribs_lock);
if (_attribs == (Attribs *)NULL) {
return 0;
}
@ -201,6 +212,8 @@ get_num_attribs() {
////////////////////////////////////////////////////////////////////
void RenderAttrib::
list_attribs(ostream &out) {
ReMutexHolder holder(*_attribs_lock);
out << _attribs->size() << " attribs:\n";
Attribs::const_iterator si;
for (si = _attribs->begin(); si != _attribs->end(); ++si) {
@ -219,6 +232,8 @@ list_attribs(ostream &out) {
////////////////////////////////////////////////////////////////////
bool RenderAttrib::
validate_attribs() {
ReMutexHolder holder(*_attribs_lock);
if (_attribs->empty()) {
return true;
}
@ -263,6 +278,8 @@ return_new(RenderAttrib *attrib) {
return attrib;
}
ReMutexHolder holder(*_attribs_lock);
// This should be a newly allocated pointer, not one that was used
// for anything else.
nassertr(attrib->_saved_entry == _attribs->end(), attrib);
@ -399,6 +416,26 @@ output_comparefunc(ostream &out, PandaCompareFunc fn) const {
}
}
////////////////////////////////////////////////////////////////////
// Function: RenderAttrib::release_new
// Access: Private
// Description: This inverse of return_new, this releases this object
// from the global RenderAttrib table.
//
// You must already be holding _attribs_lock before you
// call this method.
////////////////////////////////////////////////////////////////////
void RenderAttrib::
release_new() {
nassertv(_attribs_lock->debug_is_locked());
if (_saved_entry != _attribs->end()) {
nassertv(_attribs->find(this) == _saved_entry);
_attribs->erase(_saved_entry);
_saved_entry = _attribs->end();
}
}
////////////////////////////////////////////////////////////////////
// Function: RenderAttrib::make_default_impl
// Access: Protected, Virtual
@ -415,6 +452,29 @@ make_default_impl() const {
return (RenderAttrib *)NULL;
}
////////////////////////////////////////////////////////////////////
// Function: RenderAttrib::init_attribs
// Access: Public, Static
// Description: Make sure the global _attribs map is allocated. This
// only has to be done once. We could make this map
// static, but then we run into problems if anyone
// creates a RenderAttrib object at static init time;
// it also seems to cause problems when the Panda shared
// library is unloaded at application exit time.
////////////////////////////////////////////////////////////////////
void RenderAttrib::
init_attribs() {
_attribs = new Attribs;
// TODO: we should have a global Panda mutex to allow us to safely
// create _attribs_lock without a startup race condition. For the
// meantime, this is OK because we guarantee that this method is
// called at static init time, presumably when there is still only
// one thread in the world.
_attribs_lock = new ReMutex("RenderAttrib::_attribs_lock");
nassertv(Thread::get_current_thread() == Thread::get_main_thread());
}
////////////////////////////////////////////////////////////////////
// Function: RenderAttrib::write_datagram
// Access: Public, Virtual

View File

@ -24,6 +24,7 @@
#include "typedWritableReferenceCount.h"
#include "pointerTo.h"
#include "pset.h"
#include "reMutex.h"
class AttribSlots;
class GraphicsStateGuardianBase;
@ -80,6 +81,9 @@ public:
PUBLISHED:
INLINE int compare_to(const RenderAttrib &other) const;
bool unref() const;
virtual void output(ostream &out) const;
virtual void write(ostream &out, int indent_level) const;
@ -179,10 +183,18 @@ protected:
virtual RenderAttrib *make_default_impl() const=0;
void output_comparefunc(ostream &out, PandaCompareFunc fn) const;
private:
void release_new();
protected:
bool _always_reissue;
public:
static void init_attribs();
private:
// This mutex protects _attribs.
static ReMutex *_attribs_lock;
typedef pset<const RenderAttrib *, indirect_compare_to<const RenderAttrib *> > Attribs;
static Attribs *_attribs;

View File

@ -83,10 +83,9 @@ RenderEffects::
~RenderEffects() {
// Remove the deleted RenderEffects object from the global pool.
ReMutexHolder holder(*_states_lock);
if (_saved_entry != _states->end()) {
_states->erase(_saved_entry);
_saved_entry = _states->end();
}
// unref() should have cleared this.
nassertv(_saved_entry == _states->end());
}
////////////////////////////////////////////////////////////////////
@ -385,6 +384,40 @@ get_effect(TypeHandle type) const {
return NULL;
}
////////////////////////////////////////////////////////////////////
// Function: RenderEffects::unref
// Access: Published
// Description: This method overrides ReferenceCount::unref() to
// check whether the remaining reference count is
// entirely in the cache, and if so, it checks for and
// breaks a cycle in the cache involving this object.
// This is designed to prevent leaks from cyclical
// references within the cache.
//
// Note that this is not a virtual method, and cannot be
// because ReferenceCount itself declares no virtual
// methods (it avoids the overhead of a virtual function
// pointer). But this doesn't matter, because
// PT(TransformState) is a template class, and will call
// the appropriate method even though it is non-virtual.
////////////////////////////////////////////////////////////////////
bool RenderEffects::
unref() const {
ReMutexHolder holder(*_states_lock);
if (ReferenceCount::unref()) {
// The reference count is still nonzero.
return true;
}
// The reference count has just reached zero. Make sure the object
// is removed from the global object pool, before anyone else finds
// it and tries to ref it.
((RenderEffects *)this)->release_new();
return false;
}
////////////////////////////////////////////////////////////////////
// Function: RenderEffects::output
// Access: Published, Virtual
@ -603,6 +636,26 @@ return_new(RenderEffects *state) {
return *(result.first);
}
////////////////////////////////////////////////////////////////////
// Function: RenderEffects::release_new
// Access: Private
// Description: This inverse of return_new, this releases this object
// from the global RenderEffects table.
//
// You must already be holding _states_lock before you
// call this method.
////////////////////////////////////////////////////////////////////
void RenderEffects::
release_new() {
nassertv(_states_lock->debug_is_locked());
if (_saved_entry != _states->end()) {
nassertv(_states->find(this) == _saved_entry);
_states->erase(_saved_entry);
_saved_entry = _states->end();
}
}
////////////////////////////////////////////////////////////////////
// Function: RenderEffects::determine_decal
// Access: Private
@ -759,6 +812,28 @@ complete_pointers(TypedWritable **p_list, BamReader *manager) {
return pi;
}
////////////////////////////////////////////////////////////////////
// Function: RenderEffects::require_fully_complete
// Access: Public, Virtual
// Description: Some objects require all of their nested pointers to
// have been completed before the objects themselves can
// be completed. If this is the case, override this
// method to return true, and be careful with circular
// references (which would make the object unreadable
// from a bam file).
////////////////////////////////////////////////////////////////////
bool RenderEffects::
require_fully_complete() const {
// Since we sort _states based on each RenderEffects' operator <
// method, which in turn compares based on each nested RenderEffect
// object's compare_to() method, some of which depend on the
// RenderEffect's pointers having already been completed
// (e.g. CharacterJointEffect), we therefore require each of out our
// nested RenderEffect objects to have been completed before we can
// be completed.
return true;
}
////////////////////////////////////////////////////////////////////
// Function: RenderEffects::change_this
// Access: Public, Static

View File

@ -89,6 +89,8 @@ PUBLISHED:
const RenderEffect *get_effect(TypeHandle type) const;
bool unref() const;
void output(ostream &out) const;
void write(ostream &out, int indent_level) const;
@ -115,6 +117,8 @@ public:
private:
static CPT(RenderEffects) return_new(RenderEffects *state);
void release_new();
void determine_decal();
void determine_show_bounds();
void determine_cull_callback();
@ -174,6 +178,7 @@ public:
static void register_with_read_factory();
virtual void write_datagram(BamWriter *manager, Datagram &dg);
virtual int complete_pointers(TypedWritable **plist, BamReader *manager);
virtual bool require_fully_complete() const;
static TypedWritable *change_this(TypedWritable *old_ptr, BamReader *manager);
virtual void finalize(BamReader *manager);

View File

@ -103,11 +103,11 @@ INLINE bool RenderState::
cache_unref() const {
#ifdef DO_PSTATS
int old_referenced_bits = get_referenced_bits();
bool result = NodeCachedReferenceCount::cache_unref();
bool result = do_cache_unref();
consider_update_pstats(old_referenced_bits);
return result;
#else // DO_PSTATS
return NodeCachedReferenceCount::cache_unref();
return do_cache_unref();
#endif // DO_PSTATS
}
@ -136,11 +136,11 @@ INLINE bool RenderState::
node_unref() const {
#ifdef DO_PSTATS
int old_referenced_bits = get_referenced_bits();
bool result = NodeCachedReferenceCount::node_unref();
bool result = do_node_unref();
consider_update_pstats(old_referenced_bits);
return result;
#else // DO_PSTATS
return NodeCachedReferenceCount::node_unref();
return do_node_unref();
#endif // DO_PSTATS
}
@ -632,6 +632,31 @@ flush_level() {
_cache_counter.flush_level();
}
////////////////////////////////////////////////////////////////////
// Function: RenderState::do_node_unref
// Access: Private
// Description: Reimplements NodeReferenceCount::node_unref(). We do
// this because we have a non-virtual unref() method.
////////////////////////////////////////////////////////////////////
INLINE bool RenderState::
do_node_unref() const {
node_unref_only();
return unref();
}
////////////////////////////////////////////////////////////////////
// Function: RenderState::do_cache_unref
// Access: Private
// Description: Reimplements
// CachedTypedWritableReferenceCount::cache_unref(). We
// do this because we have a non-virtual unref() method.
////////////////////////////////////////////////////////////////////
INLINE bool RenderState::
do_cache_unref() const {
cache_unref_only();
return unref();
}
////////////////////////////////////////////////////////////////////
// Function: RenderState::CompositionCycleDescEntry::Constructor
// Access: Public

View File

@ -104,13 +104,10 @@ RenderState::
set_destructing();
ReMutexHolder holder(*_states_lock);
if (_saved_entry != _states->end()) {
nassertv(_states->find(this) == _saved_entry);
_states->erase(_saved_entry);
_saved_entry = _states->end();
}
remove_cache_pointers();
// unref() should have cleared these.
nassertv(_saved_entry == _states->end());
nassertv(_composition_cache.empty() && _invert_composition_cache.empty());
// If this was true at the beginning of the destructor, but is no
// longer true now, probably we've been double-deleted.
@ -627,46 +624,44 @@ get_override(TypeHandle type) const {
////////////////////////////////////////////////////////////////////
bool RenderState::
unref() const {
// Most of the time, we won't need to grab the lock. We first check
// whether we think we will need to grab it. Then, after we have
// successfully acquired the lock, we check that the condition is
// still valid.
// We always have to grab the lock, since we will definitely need to
// be holding it if we happen to drop the reference count to 0.
ReMutexHolder holder(*_states_lock);
// It is possible that, due to some race condition, this condition
// is never seen as true on any one thread. In that case, the cycle
// will not automatically be detected and broken. But since (a)
// that will be a relatively rare situation, (b) it will be
// expensive to protect against it, and (c) the damage is minimal,
// the race condition is allowed to remain.
if (get_cache_ref_count() > 0 &&
get_ref_count() == get_cache_ref_count() + 1) {
if (auto_break_cycles) {
ReMutexHolder holder(*_states_lock);
if (auto_break_cycles) {
if (get_cache_ref_count() > 0 &&
get_ref_count() == get_cache_ref_count() + 1) {
// If we are about to remove the one reference that is not in the
// cache, leaving only references in the cache, then we need to
// check for a cycle involving this RenderState and break it if
// it exists.
if (get_cache_ref_count() > 0 &&
get_ref_count() == get_cache_ref_count() + 1) {
// If we are about to remove the one reference that is not in the
// cache, leaving only references in the cache, then we need to
// check for a cycle involving this RenderState and break it if
// it exists.
++_last_cycle_detect;
if (r_detect_cycles(this, this, 1, _last_cycle_detect, NULL)) {
// Ok, we have a cycle. This will be a leak unless we break the
// cycle by freeing the cache on this object.
if (pgraph_cat.is_debug()) {
pgraph_cat.debug()
<< "Breaking cycle involving " << (*this) << "\n";
}
((RenderState *)this)->remove_cache_pointers();
++_last_cycle_detect;
if (r_detect_cycles(this, this, 1, _last_cycle_detect, NULL)) {
// Ok, we have a cycle. This will be a leak unless we break the
// cycle by freeing the cache on this object.
if (pgraph_cat.is_debug()) {
pgraph_cat.debug()
<< "Breaking cycle involving " << (*this) << "\n";
}
((RenderState *)this)->remove_cache_pointers();
}
}
}
return ReferenceCount::unref();
if (ReferenceCount::unref()) {
// The reference count is still nonzero.
return true;
}
// The reference count has just reached zero. Make sure the object
// is removed from the global object pool, before anyone else finds
// it and tries to ref it.
((RenderState *)this)->release_new();
((RenderState *)this)->remove_cache_pointers();
return false;
}
////////////////////////////////////////////////////////////////////
@ -1371,6 +1366,26 @@ r_detect_cycles(const RenderState *start_state,
return false;
}
////////////////////////////////////////////////////////////////////
// Function: RenderState::release_new
// Access: Private
// Description: This inverse of return_new, this releases this object
// from the global RenderState table.
//
// You must already be holding _states_lock before you
// call this method.
////////////////////////////////////////////////////////////////////
void RenderState::
release_new() {
nassertv(_states_lock->debug_is_locked());
if (_saved_entry != _states->end()) {
nassertv(_states->find(this) == _saved_entry);
_states->erase(_saved_entry);
_saved_entry = _states->end();
}
}
////////////////////////////////////////////////////////////////////
// Function: RenderState::remove_cache_pointers
// Access: Private

View File

@ -163,6 +163,9 @@ public:
INLINE static void flush_level();
private:
INLINE bool do_cache_unref() const;
INLINE bool do_node_unref() const;
class CompositionCycleDescEntry {
public:
INLINE CompositionCycleDescEntry(const RenderState *obj,
@ -183,6 +186,7 @@ private:
int length, UpdateSeq this_seq,
CompositionCycleDesc *cycle_desc);
void release_new();
void remove_cache_pointers();
void determine_bin_index();

View File

@ -733,11 +733,11 @@ INLINE bool TransformState::
cache_unref() const {
#ifdef DO_PSTATS
int old_referenced_bits = get_referenced_bits();
bool result = NodeCachedReferenceCount::cache_unref();
bool result = do_cache_unref();
consider_update_pstats(old_referenced_bits);
return result;
#else // DO_PSTATS
return NodeCachedReferenceCount::cache_unref();
return do_cache_unref();
#endif // DO_PSTATS
}
@ -766,11 +766,11 @@ INLINE bool TransformState::
node_unref() const {
#ifdef DO_PSTATS
int old_referenced_bits = get_referenced_bits();
bool result = NodeCachedReferenceCount::node_unref();
bool result = do_node_unref();
consider_update_pstats(old_referenced_bits);
return result;
#else // DO_PSTATS
return NodeCachedReferenceCount::node_unref();
return do_node_unref();
#endif // DO_PSTATS
}
@ -785,6 +785,31 @@ flush_level() {
_cache_counter.flush_level();
}
////////////////////////////////////////////////////////////////////
// Function: TransformState::do_node_unref
// Access: Private
// Description: Reimplements NodeReferenceCount::node_unref(). We do
// this because we have a non-virtual unref() method.
////////////////////////////////////////////////////////////////////
INLINE bool TransformState::
do_node_unref() const {
node_unref_only();
return unref();
}
////////////////////////////////////////////////////////////////////
// Function: TransformState::do_cache_unref
// Access: Private
// Description: Reimplements
// CachedTypedWritableReferenceCount::cache_unref(). We
// do this because we have a non-virtual unref() method.
////////////////////////////////////////////////////////////////////
INLINE bool TransformState::
do_cache_unref() const {
cache_unref_only();
return unref();
}
////////////////////////////////////////////////////////////////////
// Function: TransformState::check_hash
// Access: Private

View File

@ -102,13 +102,10 @@ TransformState::
}
ReMutexHolder holder(*_states_lock);
if (_saved_entry != _states->end()) {
nassertv(_states->find(this) == _saved_entry);
_states->erase(_saved_entry);
_saved_entry = _states->end();
}
remove_cache_pointers();
// unref() should have cleared these.
nassertv(_saved_entry == _states->end());
nassertv(_composition_cache.empty() && _invert_composition_cache.empty());
// If this was true at the beginning of the destructor, but is no
// longer true now, probably we've been double-deleted.
@ -765,48 +762,46 @@ invert_compose(const TransformState *other) const {
////////////////////////////////////////////////////////////////////
bool TransformState::
unref() const {
// Most of the time, we won't need to grab the lock. We first check
// whether we think we will need to grab it. Then, after we have
// successfully acquired the lock, we check that the condition is
// still valid.
// We always have to grab the lock, since we will definitely need to
// be holding it if we happen to drop the reference count to 0.
ReMutexHolder holder(*_states_lock);
// It is possible that, due to some race condition, this condition
// is never seen as true on any one thread. In that case, the cycle
// will not automatically be detected and broken. But since (a)
// that will be a relatively rare situation, (b) it will be
// expensive to protect against it, and (c) the damage is minimal,
// the race condition is allowed to remain.
if (get_cache_ref_count() > 0 &&
get_ref_count() == get_cache_ref_count() + 1) {
if (auto_break_cycles) {
ReMutexHolder holder(*_states_lock);
if (auto_break_cycles) {
if (get_cache_ref_count() > 0 &&
get_ref_count() == get_cache_ref_count() + 1) {
// If we are about to remove the one reference that is not in the
// cache, leaving only references in the cache, then we need to
// check for a cycle involving this TransformState and break it if
// it exists.
if (get_cache_ref_count() > 0 &&
get_ref_count() == get_cache_ref_count() + 1) {
// If we are about to remove the one reference that is not in the
// cache, leaving only references in the cache, then we need to
// check for a cycle involving this TransformState and break it if
// it exists.
PStatTimer timer(_transform_break_cycles_pcollector);
PStatTimer timer(_transform_break_cycles_pcollector);
++_last_cycle_detect;
if (r_detect_cycles(this, this, 1, _last_cycle_detect, NULL)) {
// Ok, we have a cycle. This will be a leak unless we break the
// cycle by freeing the cache on this object.
if (pgraph_cat.is_debug()) {
pgraph_cat.debug()
<< "Breaking cycle involving " << (*this) << "\n";
}
((TransformState *)this)->remove_cache_pointers();
++_last_cycle_detect;
if (r_detect_cycles(this, this, 1, _last_cycle_detect, NULL)) {
// Ok, we have a cycle. This will be a leak unless we break the
// cycle by freeing the cache on this object.
if (pgraph_cat.is_debug()) {
pgraph_cat.debug()
<< "Breaking cycle involving " << (*this) << "\n";
}
((TransformState *)this)->remove_cache_pointers();
}
}
}
return ReferenceCount::unref();
if (ReferenceCount::unref()) {
// The reference count is still nonzero.
return true;
}
// The reference count has just reached zero. Make sure the object
// is removed from the global object pool, before anyone else finds
// it and tries to ref it.
((TransformState *)this)->release_new();
((TransformState *)this)->remove_cache_pointers();
return false;
}
////////////////////////////////////////////////////////////////////
@ -1592,6 +1587,27 @@ r_detect_cycles(const TransformState *start_state,
return false;
}
////////////////////////////////////////////////////////////////////
// Function: TransformState::release_new
// Access: Private
// Description: This inverse of return_new, this releases this object
// from the global TransformState table.
//
// You must already be holding _states_lock before you
// call this method.
////////////////////////////////////////////////////////////////////
void TransformState::
release_new() {
nassertv(_states_lock->debug_is_locked());
if (_saved_entry != _states->end()) {
nassertv(_states->find(this) == _saved_entry);
_states->erase(_saved_entry);
_saved_entry = _states->end();
}
}
////////////////////////////////////////////////////////////////////
// Function: TransformState::remove_cache_pointers
// Access: Private
@ -1606,7 +1622,7 @@ r_detect_cycles(const TransformState *start_state,
void TransformState::
remove_cache_pointers() {
nassertv(_states_lock->debug_is_locked());
// Fortunately, since we added CompositionCache records in pairs, we
// know exactly the set of TransformState objects that have us in their
// cache: it's the same set of TransformState objects that we have in

View File

@ -195,6 +195,9 @@ public:
INLINE static void flush_level();
private:
INLINE bool do_cache_unref() const;
INLINE bool do_node_unref() const;
class CompositionCycleDescEntry {
public:
INLINE CompositionCycleDescEntry(const TransformState *obj,
@ -215,6 +218,7 @@ private:
int length, UpdateSeq this_seq,
CompositionCycleDesc *cycle_desc);
void release_new();
void remove_cache_pointers();
private:

View File

@ -32,7 +32,6 @@
config_util.N config_util.h configurable.h \
datagramInputFile.I datagramInputFile.h \
datagramOutputFile.I datagramOutputFile.h \
deferredDeletor.h \
doubleBitMask.I doubleBitMask.h \
drawMask.h \
factoryBase.I factoryBase.h \
@ -52,11 +51,9 @@
modifierButtons.I modifierButtons.h mouseButton.h \
mouseData.I mouseData.h nameUniquifier.I nameUniquifier.h \
nodeCachedReferenceCount.h nodeCachedReferenceCount.I \
nonDeletor.h \
portalMask.h \
pta_double.h \
pta_float.h pta_int.h \
spamDeletor.h \
sparseArray.I sparseArray.h \
string_utils.I string_utils.N string_utils.h \
stringStreamBuf.I stringStreamBuf.h \
@ -88,7 +85,6 @@
copyOnWritePointer.cxx \
config_util.cxx configurable.cxx \
datagramInputFile.cxx datagramOutputFile.cxx \
deferredDeletor.cxx \
doubleBitMask.cxx \
factoryBase.cxx \
factoryParam.cxx factoryParams.cxx \
@ -101,10 +97,8 @@
modifierButtons.cxx mouseButton.cxx mouseData.cxx \
nameUniquifier.cxx \
nodeCachedReferenceCount.cxx \
nonDeletor.cxx \
pta_double.cxx pta_float.cxx \
pta_int.cxx pta_ushort.cxx \
spamDeletor.cxx \
sparseArray.cxx \
string_utils.cxx \
stringStreamBuf.cxx \
@ -140,7 +134,6 @@
config_util.h configurable.h factory.I factory.h \
datagramInputFile.I datagramInputFile.h \
datagramOutputFile.I datagramOutputFile.h \
deferredDeletor.h \
doubleBitMask.I doubleBitMask.h \
drawMask.h \
factoryBase.I factoryBase.h factoryParam.I factoryParam.h \
@ -160,11 +153,9 @@
modifierButtons.h mouseButton.h mouseData.I mouseData.h \
nameUniquifier.I nameUniquifier.h \
nodeCachedReferenceCount.h nodeCachedReferenceCount.I \
nonDeletor.h \
portalMask.h \
pta_double.h \
pta_float.h pta_int.h pta_ushort.h \
spamDeletor.h \
sparseArray.I sparseArray.h \
string_utils.I string_utils.h \
stringStreamBuf.I stringStreamBuf.h \

View File

@ -144,14 +144,8 @@ get_cache_ref_count() const {
////////////////////////////////////////////////////////////////////
// Function: CachedTypedWritableReferenceCount::cache_ref
// Access: Published
// Description: Explicitly increments the reference count.
//
// This function is const, even though it changes the
// object, because generally fiddling with an object's
// reference count isn't considered part of fiddling
// with the object. An object might be const in other
// ways, but we still need to accurately count the
// number of references to it.
// Description: Explicitly increments the cache reference count and
// the normal reference count simultaneously.
////////////////////////////////////////////////////////////////////
INLINE void CachedTypedWritableReferenceCount::
cache_ref() const {
@ -166,25 +160,8 @@ cache_ref() const {
////////////////////////////////////////////////////////////////////
// Function: CachedTypedWritableReferenceCount::cache_unref
// Access: Published
// Description: Explicitly decrements the reference count. Note that
// the object will not be implicitly deleted by unref()
// simply because the reference count drops to zero.
// (Having a member function delete itself is
// problematic; plus, we don't have a virtual destructor
// anyway.) However, see the helper function
// unref_delete().
//
// User code should avoid using ref() and unref()
// directly, which can result in missed reference
// counts. Instead, let a PointerTo object manage the
// reference counting automatically.
//
// This function is const, even though it changes the
// object, because generally fiddling with an object's
// reference count isn't considered part of fiddling
// with the object. An object might be const in other
// ways, but we still need to accurately count the
// number of references to it.
// Description: Explicitly decrements the cache reference count and
// the normal reference count simultaneously.
//
// The return value is true if the new reference count
// is nonzero, false if it is zero.
@ -199,9 +176,9 @@ cache_unref() const {
// zero reference count. Are you using ref() and unref()
// directly? Are you sure you can't use PointerTo's?
nassertr(_cache_ref_count > 0, 0);
unref();
return AtomicAdjust::dec(((CachedTypedWritableReferenceCount *)this)->_cache_ref_count);
AtomicAdjust::dec(((CachedTypedWritableReferenceCount *)this)->_cache_ref_count);
return unref();
}
////////////////////////////////////////////////////////////////////
@ -219,6 +196,28 @@ test_ref_count_integrity() const {
#endif
}
////////////////////////////////////////////////////////////////////
// Function: CachedTypedWritableReferenceCount::cache_unref_only
// Access: Protected
// Description: Decrements the cache reference count without affecting
// the normal reference count. Intended to be called by
// derived classes only, presumably to reimplement
// cache_unref().
////////////////////////////////////////////////////////////////////
INLINE void CachedTypedWritableReferenceCount::
cache_unref_only() const {
#ifdef _DEBUG
nassertv(test_ref_count_integrity());
#endif
// If this assertion fails, you tried to unref an object with a
// zero reference count. Are you using ref() and unref()
// directly? Are you sure you can't use PointerTo's?
nassertv(_cache_ref_count > 0);
AtomicAdjust::dec(((CachedTypedWritableReferenceCount *)this)->_cache_ref_count);
}
////////////////////////////////////////////////////////////////////
// Function: cache_unref_delete
// Description: This global helper function will unref the given
@ -232,14 +231,7 @@ test_ref_count_integrity() const {
template<class RefCountType>
INLINE void
cache_unref_delete(RefCountType *ptr) {
ptr->cache_unref();
if (ptr->get_ref_count() == 0) {
ObjectDeletor *deletor = ObjectDeletor::get_global_ptr();
if (deletor != (ObjectDeletor *)NULL) {
ptr->ref();
deletor->delete_object(RefCountDeleteWrapper<RefCountType>::do_delete, (void *)ptr);
return;
}
if (!ptr->cache_unref()) {
delete ptr;
}
}

View File

@ -56,6 +56,7 @@ PUBLISHED:
INLINE bool test_ref_count_integrity() const;
protected:
INLINE void cache_unref_only() const;
bool do_test_ref_count_integrity() const;
private:

View File

@ -44,9 +44,6 @@
#include "writableParam.h"
#include "keyboardButton.h"
#include "mouseButton.h"
#include "deferredDeletor.h"
#include "nonDeletor.h"
#include "spamDeletor.h"
#include "dconfig.h"
@ -179,10 +176,6 @@ init_libputil() {
KeyboardButton::init_keyboard_buttons();
MouseButton::init_mouse_buttons();
DeferredDeletor::register_deletor();
NonDeletor::register_deletor();
SpamDeletor::register_deletor();
register_type(BamReader::_remove_flag, "remove");
BamCacheIndex::register_with_read_factory();

View File

@ -1,76 +0,0 @@
// Filename: deferredDeletor.cxx
// Created by: drose (10Apr06)
//
////////////////////////////////////////////////////////////////////
//
// 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 "deferredDeletor.h"
#include "config_util.h"
#include "mutexHolder.h"
////////////////////////////////////////////////////////////////////
// Function: DeferredDeletor::Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
DeferredDeletor::
DeferredDeletor() : _lock("DeferredDeletor") {
}
////////////////////////////////////////////////////////////////////
// Function: DeferredDeletor::delete_object
// Access: Public, Virtual
// Description: Adds the pointer to the object to be deleted, along
// with a pointer to a function that can delete it.
////////////////////////////////////////////////////////////////////
void DeferredDeletor::
delete_object(DeleteFunc *func, void *ptr) {
MutexHolder holder(_lock);
if (util_cat.is_spam()) {
util_cat.spam()
<< "Deferring deleting pointer " << ptr << "\n";
}
_tokens.push_back(DeleteToken(func, ptr));
}
////////////////////////////////////////////////////////////////////
// Function: DeferredDeletor::flush
// Access: Public, Virtual
// Description: Ensures that any objects queued up for deletion have
// been fully deleted by the time flush() returns.
////////////////////////////////////////////////////////////////////
void DeferredDeletor::
flush() {
Tokens new_tokens;
{
MutexHolder holder(_lock);
_tokens.swap(new_tokens);
}
Tokens::iterator ti;
for (ti = new_tokens.begin(); ti != new_tokens.end(); ++ti) {
(*ti).do_delete();
}
}
////////////////////////////////////////////////////////////////////
// Function: DeferredDeletor::register_deletor
// Access: Public, Static
// Description: Registers this deletor with the global pool.
////////////////////////////////////////////////////////////////////
void DeferredDeletor::
register_deletor() {
register_subclass(new DeferredDeletor, "deferred");
}

View File

@ -1,48 +0,0 @@
// Filename: deferredDeletor.h
// Created by: drose (10Apr06)
//
////////////////////////////////////////////////////////////////////
//
// 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 DEFERREDDELETOR_H
#define DEFERREDDELETOR_H
#include "pandabase.h"
#include "objectDeletor.h"
#include "pvector.h"
#include "pmutex.h"
////////////////////////////////////////////////////////////////////
// Class : DeferredDeletor
// Description : The DeferredDeletor does all its deleting between
// frames, where it can be observed by PStats and where
// it won't interfere with rendering.
////////////////////////////////////////////////////////////////////
class EXPCL_PANDA DeferredDeletor : public ObjectDeletor {
public:
DeferredDeletor();
virtual void delete_object(DeleteFunc *func, void *ptr);
virtual void flush();
static void register_deletor();
private:
typedef pvector<DeleteToken> Tokens;
Tokens _tokens;
Mutex _lock;
};
#endif

View File

@ -166,42 +166,16 @@ node_ref() const {
////////////////////////////////////////////////////////////////////
// Function: NodeCachedReferenceCount::node_unref
// Access: Published
// Description: Explicitly decrements the reference count. Note that
// the object will not be implicitly deleted by unref()
// simply because the reference count drops to zero.
// (Having a member function delete itself is
// problematic; plus, we don't have a virtual destructor
// anyway.) However, see the helper function
// unref_delete().
//
// User code should avoid using ref() and unref()
// directly, which can result in missed reference
// counts. Instead, let a PointerTo object manage the
// reference counting automatically.
//
// This function is const, even though it changes the
// object, because generally fiddling with an object's
// reference count isn't considered part of fiddling
// with the object. An object might be const in other
// ways, but we still need to accurately count the
// number of references to it.
// Description: Explicitly decrements the node reference count and
// the normal reference count simultaneously.
//
// The return value is true if the new reference count
// is nonzero, false if it is zero.
////////////////////////////////////////////////////////////////////
INLINE bool NodeCachedReferenceCount::
node_unref() const {
#ifdef _DEBUG
nassertr(test_ref_count_integrity(), 0);
#endif
// If this assertion fails, you tried to unref an object with a
// zero reference count. Are you using ref() and unref()
// directly? Are you sure you can't use PointerTo's?
nassertr(_node_ref_count > 0, 0);
unref();
return AtomicAdjust::dec(((NodeCachedReferenceCount *)this)->_node_ref_count);
node_unref_only();
return unref();
}
////////////////////////////////////////////////////////////////////
@ -242,3 +216,25 @@ get_referenced_bits() const {
return result;
}
////////////////////////////////////////////////////////////////////
// Function: NodeCachedReferenceCount::node_unref_only
// Access: Protected
// Description: Decrements the node reference count without affecting
// the normal reference count. Intended to be called by
// derived classes only, presumably to reimplement
// node_unref().
////////////////////////////////////////////////////////////////////
INLINE void NodeCachedReferenceCount::
node_unref_only() const {
#ifdef _DEBUG
nassertv(test_ref_count_integrity());
#endif
// If this assertion fails, you tried to unref an object with a
// zero reference count. Are you using ref() and unref()
// directly? Are you sure you can't use PointerTo's?
nassertv(_node_ref_count > 0);
AtomicAdjust::dec(((NodeCachedReferenceCount *)this)->_node_ref_count);
}

View File

@ -84,6 +84,7 @@ PUBLISHED:
INLINE int get_referenced_bits() const;
protected:
INLINE void node_unref_only() const;
bool do_test_ref_count_integrity() const;
private:

View File

@ -1,44 +0,0 @@
// Filename: nonDeletor.cxx
// Created by: drose (10Apr06)
//
////////////////////////////////////////////////////////////////////
//
// 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 "nonDeletor.h"
#include "config_util.h"
////////////////////////////////////////////////////////////////////
// Function: NonDeletor::delete_object
// Access: Public, Virtual
// Description: Adds the pointer to the object to be deleted, along
// with a pointer to a function that can delete it.
////////////////////////////////////////////////////////////////////
void NonDeletor::
delete_object(DeleteFunc *, void *ptr) {
if (util_cat.is_spam()) {
util_cat.spam()
<< "Not deleting pointer " << ptr << "\n";
}
}
////////////////////////////////////////////////////////////////////
// Function: NonDeletor::register_deletor
// Access: Public, Static
// Description: Registers this deletor with the global pool.
////////////////////////////////////////////////////////////////////
void NonDeletor::
register_deletor() {
register_subclass(new NonDeletor, "none");
}

View File

@ -1,40 +0,0 @@
// Filename: nonDeletor.h
// Created by: drose (10Apr06)
//
////////////////////////////////////////////////////////////////////
//
// 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 NONDELETOR_H
#define NONDELETOR_H
#include "pandabase.h"
#include "objectDeletor.h"
////////////////////////////////////////////////////////////////////
// Class : NonDeletor
// Description : This specialization of ObjectDeletor serves a very
// specific function: it *doesn't* delete pointers it is
// given. This is useful mainly for testing, for
// instance to determine if there is a problem with a
// destructor somewhere.
////////////////////////////////////////////////////////////////////
class EXPCL_PANDA NonDeletor : public ObjectDeletor {
public:
virtual void delete_object(DeleteFunc *func, void *ptr);
static void register_deletor();
};
#endif

View File

@ -19,7 +19,6 @@
#include "copyOnWritePointer.cxx"
#include "datagramInputFile.cxx"
#include "datagramOutputFile.cxx"
#include "deferredDeletor.cxx"
#include "doubleBitMask.cxx"
#include "factoryBase.cxx"
#include "factoryParam.cxx"

View File

@ -8,12 +8,10 @@
#include "mouseData.cxx"
#include "nameUniquifier.cxx"
#include "nodeCachedReferenceCount.cxx"
#include "nonDeletor.cxx"
#include "pta_double.cxx"
#include "pta_float.cxx"
#include "pta_int.cxx"
#include "pta_ushort.cxx"
#include "spamDeletor.cxx"
#include "sparseArray.cxx"
#include "string_utils.cxx"
#include "stringStreamBuf.cxx"

View File

@ -1,45 +0,0 @@
// Filename: spamDeletor.cxx
// Created by: drose (11Apr07)
//
////////////////////////////////////////////////////////////////////
//
// 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 "spamDeletor.h"
#include "config_util.h"
////////////////////////////////////////////////////////////////////
// Function: SpamDeletor::delete_object
// Access: Public, Virtual
// Description: Adds the pointer to the object to be deleted, along
// with a pointer to a function that can delete it.
////////////////////////////////////////////////////////////////////
void SpamDeletor::
delete_object(DeleteFunc *func, void *ptr) {
if (util_cat.is_spam()) {
util_cat.spam()
<< "Deleting pointer " << ptr << "\n";
}
(*func)(ptr);
}
////////////////////////////////////////////////////////////////////
// Function: SpamDeletor::register_deletor
// Access: Public, Static
// Description: Registers this deletor with the global pool.
////////////////////////////////////////////////////////////////////
void SpamDeletor::
register_deletor() {
register_subclass(new SpamDeletor, "spam");
}

View File

@ -1,39 +0,0 @@
// Filename: spamDeletor.h
// Created by: drose (11Apr07)
//
////////////////////////////////////////////////////////////////////
//
// 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 SPAMDELETOR_H
#define SPAMDELETOR_H
#include "pandabase.h"
#include "objectDeletor.h"
////////////////////////////////////////////////////////////////////
// Class : SpamDeletor
// Description : The only purpose of the SpamDeletor is to issue a
// spam Notify message as each object is deleted. Maybe
// it will be useful to track down accidental deletions
// due to PT mismanagement.
////////////////////////////////////////////////////////////////////
class EXPCL_PANDA SpamDeletor : public ObjectDeletor {
public:
virtual void delete_object(DeleteFunc *func, void *ptr);
static void register_deletor();
};
#endif