mirror of
https://github.com/panda3d/panda3d.git
synced 2025-10-04 02:42:49 -04:00
better thread protection in reference counts, esp. TransformState etc.
This commit is contained in:
parent
e2bfe71e05
commit
68cd3322a9
@ -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
|
||||
|
@ -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());
|
||||
|
@ -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 \
|
||||
|
@ -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"
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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:
|
||||
|
@ -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);
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 ©) {
|
||||
////////////////////////////////////////////////////////////////////
|
||||
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) {
|
||||
|
@ -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 ©);
|
||||
~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;
|
||||
|
@ -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 ©) {
|
||||
////////////////////////////////////////////////////////////////////
|
||||
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) {
|
||||
|
@ -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 ©);
|
||||
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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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 \
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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:
|
||||
|
@ -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();
|
||||
|
@ -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");
|
||||
}
|
@ -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
|
@ -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);
|
||||
}
|
||||
|
@ -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:
|
||||
|
@ -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");
|
||||
}
|
@ -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
|
@ -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"
|
||||
|
@ -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"
|
||||
|
@ -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");
|
||||
}
|
@ -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
|
Loading…
x
Reference in New Issue
Block a user