pipelining getting close; uprev bam version to 6.0

This commit is contained in:
David Rose 2006-02-14 19:07:47 +00:00
parent 7023429790
commit 1d2282a879
223 changed files with 27512 additions and 25773 deletions

View File

@ -320,15 +320,14 @@
// Do you want to compile in support for pipelining? This enables
// setting and accessing multiple different copies of frame-specific
// data stored in nodes, etc. At the moment, Panda cannot actually
// take advantage of this support to do anything useful, but
// eventually this will enable multi-stage pipelining of the render
// process, as well as potentially remote rendering using a
// distributed scene graph. For now, we enable this when building
// optimize 1 only, since turning this on does perform some additional
// sanity checks, but doesn't do anything else useful other than
// increase run-time overhead.
#defer DO_PIPELINING $[<= $[OPTIMIZE], 1]
// data stored in nodes, etc. This is necessary, in conjunction with
// HAVE_THREADS, to implement threaded multistage rendering in Panda.
// However, compiling this option in does add some additional runtime
// overhead even if it is not used. By default, we enable pipelining
// whenever threads are enabled, assuming that if you have threads,
// you also want to use piplining. We also enable it at OPTIMIZE
// level 1, since that enables additional runtime checks.
#defer DO_PIPELINING $[or $[<= $[OPTIMIZE], 1],$[HAVE_THREADS]]
// Do you want to use one of the alternative malloc implementations?
// This is almost always a good idea on Windows, where the standard
@ -578,9 +577,13 @@
// that Panda can use.
#define HAVE_THREADS
// Even if threading is not defined, you might want to double-check
// that ordinary mutexes are not locked reentrantly:
#define CHECK_REENTRANT_MUTEX
// Whether threading is defined or not, you might want to validate the
// thread and synchronization operations. With threading enabled,
// defining this will also enable deadlock detection and logging.
// Without threading enabled, defining this will simply verify that a
// mutex is not recursively locked. There is, of course, additional
// run-time overhead for these tests.
#defer DEBUG_THREADS $[<= $[OPTIMIZE], 2]
// Do you want to build the network interface? What additional libraries
// are required? Currently, this requires NSPR.

View File

@ -232,8 +232,8 @@ $[cdefine HAVE_CHROMIUM]
/* Define if we want to compile the threading code. */
$[cdefine HAVE_THREADS]
/* Define to check that ordinary (non-reentrant) Mutexes are not reentrantly locked. */
$[cdefine CHECK_REENTRANT_MUTEX]
/* Define to enable deadlock detection, mutex recursion checks, etc. */
$[cdefine DEBUG_THREADS]
/* Define if we want to compile the net code. */
$[cdefine HAVE_NET]

View File

@ -213,7 +213,7 @@
#set HAVE_OPENCV $[HAVE_OPENCV]
#set HAVE_THREADS $[HAVE_THREADS]
#set CHECK_REENTRANT_MUTEX $[CHECK_REENTRANT_MUTEX]
#set DEBUG_THREADS $[DEBUG_THREADS]
#set NET_IPATH $[unixfilename $[NET_IPATH]]
#set NET_LPATH $[unixfilename $[NET_LPATH]]

View File

@ -68,16 +68,15 @@ output(ostream &out) const {
}
////////////////////////////////////////////////////////////////////
// Function: CollisionInvSphere::recompute_bound
// Function: CollisionInvSphere::compute_internal_bounds
// Access: Protected, Virtual
// Description:
////////////////////////////////////////////////////////////////////
BoundingVolume *CollisionInvSphere::
recompute_bound(int pipeline_stage) {
BoundedObject::recompute_bound(pipeline_stage);
PT(BoundingVolume) CollisionInvSphere::
compute_internal_bounds() const {
// An inverse sphere always has an infinite bounding volume, since
// everything outside the sphere is solid matter.
return set_bound_ptr(new OmniBoundingVolume());
return new OmniBoundingVolume();
}
////////////////////////////////////////////////////////////////////

View File

@ -50,7 +50,7 @@ public:
virtual void output(ostream &out) const;
protected:
virtual BoundingVolume *recompute_bound(int pipeline_stage);
virtual PT(BoundingVolume) compute_internal_bounds() const;
virtual PT(CollisionEntry)
test_intersection_from_sphere(const CollisionEntry &entry) const;

View File

@ -59,7 +59,7 @@ prepare_collider(const ColliderDef &def) {
_colliders.push_back(def);
CollisionSolid *collider = def._collider;
const BoundingVolume *bv = collider->get_bound();
CPT(BoundingVolume) bv = collider->get_bounds();
if (!bv->is_of_type(GeometricBoundingVolume::get_class_type())) {
_local_bounds.push_back((GeometricBoundingVolume *)NULL);
} else {
@ -118,7 +118,7 @@ any_in_bounds() {
}
#endif // NDEBUG
const BoundingVolume *node_bv = node()->get_bound();
CPT(BoundingVolume) node_bv = node()->get_bounds();
if (node_bv->is_of_type(GeometricBoundingVolume::get_class_type())) {
const GeometricBoundingVolume *node_gbv;
DCAST_INTO_R(node_gbv, node_bv, false);

View File

@ -81,7 +81,7 @@ get_into_collide_mask() const {
INLINE void CollisionNode::
clear_solids() {
_solids.clear();
mark_bound_stale();
mark_internal_bounds_stale();
}
////////////////////////////////////////////////////////////////////
@ -114,7 +114,7 @@ INLINE void CollisionNode::
set_solid(int n, CollisionSolid *solid) {
nassertv(n >= 0 && n < get_num_solids());
_solids[n] = solid;
mark_bound_stale();
mark_internal_bounds_stale();
}
////////////////////////////////////////////////////////////////////
@ -127,7 +127,7 @@ INLINE void CollisionNode::
remove_solid(int n) {
nassertv(n >= 0 && n < get_num_solids());
_solids.erase(_solids.begin() + n);
mark_bound_stale();
mark_internal_bounds_stale();
}
////////////////////////////////////////////////////////////////////
@ -140,7 +140,7 @@ remove_solid(int n) {
INLINE int CollisionNode::
add_solid(CollisionSolid *solid) {
_solids.push_back(solid);
mark_bound_stale();
mark_internal_bounds_stale();
return _solids.size() - 1;
}

View File

@ -128,7 +128,7 @@ xform(const LMatrix4f &mat) {
solid->xform(mat);
}
mark_bound_stale();
mark_internal_bounds_stale();
}
////////////////////////////////////////////////////////////////////
@ -157,7 +157,7 @@ combine_with(PandaNode *other) {
const PT(CollisionSolid) *solids_begin = &cother->_solids[0];
const PT(CollisionSolid) *solids_end = solids_begin + cother->_solids.size();
_solids.insert(_solids.end(), solids_begin, solids_end);
mark_bound_stale();
mark_internal_bounds_stale();
return this;
}
@ -348,17 +348,17 @@ get_collide_geom() const {
}
////////////////////////////////////////////////////////////////////
// Function: CollisionNode::recompute_internal_bound
// Function: CollisionNode::compute_internal_bounds
// Access: Protected, Virtual
// Description: Called when needed to recompute the node's
// _internal_bound object. Nodes that contain anything
// of substance should redefine this to do the right
// thing.
////////////////////////////////////////////////////////////////////
BoundingVolume *CollisionNode::
recompute_internal_bound(int pipeline_stage) {
PT(BoundingVolume) CollisionNode::
compute_internal_bounds(int pipeline_stage) const {
// First, get ourselves a fresh, empty bounding volume.
BoundingVolume *bound = PandaNode::recompute_internal_bound(pipeline_stage);
PT(BoundingVolume) bound = PandaNode::compute_internal_bounds(pipeline_stage);
nassertr(bound != (BoundingVolume *)NULL, bound);
// Now actually compute the bounding volume by putting it around all
@ -367,7 +367,7 @@ recompute_internal_bound(int pipeline_stage) {
Solids::const_iterator gi;
for (gi = _solids.begin(); gi != _solids.end(); ++gi) {
child_volumes.push_back((*gi)->get_bound());
child_volumes.push_back((*gi)->get_bounds());
}
const BoundingVolume **child_begin = &child_volumes[0];

View File

@ -74,7 +74,7 @@ PUBLISHED:
INLINE static CollideMask get_default_collide_mask();
protected:
virtual BoundingVolume *recompute_internal_bound(int pipeline_stage);
virtual PT(BoundingVolume) compute_internal_bounds(int pipeline_stage) const;
private:
CPT(RenderState) get_last_pos_state();

View File

@ -16,6 +16,7 @@
//
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
// Function: CollisionPlane::Default Constructor
// Access: Protected
@ -78,7 +79,7 @@ dist_to_plane(const LPoint3f &point) const {
INLINE void CollisionPlane::
set_plane(const Planef &plane) {
_plane = plane;
mark_bound_stale();
mark_internal_bounds_stale();
mark_viz_stale();
}

View File

@ -88,16 +88,13 @@ output(ostream &out) const {
}
////////////////////////////////////////////////////////////////////
// Function: CollisionPlane::recompute_bound
// Function: CollisionPlane::compute_internal_bounds
// Access: Protected, Virtual
// Description:
////////////////////////////////////////////////////////////////////
BoundingVolume *CollisionPlane::
recompute_bound(int pipeline_stage) {
BoundedObject::recompute_bound(pipeline_stage);
// Less than ideal: we throw away whatever we just allocated in
// BoundedObject.
return set_bound_ptr(new BoundingPlane(_plane));
PT(BoundingVolume) CollisionPlane::
compute_internal_bounds() const {
return new BoundingPlane(_plane);
}
////////////////////////////////////////////////////////////////////

View File

@ -54,7 +54,7 @@ PUBLISHED:
INLINE const Planef &get_plane() const;
protected:
virtual BoundingVolume *recompute_bound(int pipeline_stage);
virtual PT(BoundingVolume) compute_internal_bounds() const;
protected:
virtual PT(CollisionEntry)

View File

@ -16,6 +16,7 @@
//
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
// Function: CollisionPolygon::Constructor
// Access: Public

View File

@ -356,14 +356,14 @@ write(ostream &out, int indent_level) const {
}
////////////////////////////////////////////////////////////////////
// Function: CollisionPolygon::recompute_bound
// Function: CollisionPolygon::compute_internal_bounds
// Access: Protected, Virtual
// Description:
////////////////////////////////////////////////////////////////////
BoundingVolume *CollisionPolygon::
recompute_bound(int pipeline_stage) {
PT(BoundingVolume) CollisionPolygon::
compute_internal_bounds() const {
// First, get ourselves a fresh, empty bounding volume.
BoundingVolume *bound = BoundedObject::recompute_bound(pipeline_stage);
PT(BoundingVolume) bound = CollisionSolid::compute_internal_bounds();
nassertr(bound != (BoundingVolume*)0L, bound);
GeometricBoundingVolume *gbv = DCAST(GeometricBoundingVolume, bound);

View File

@ -69,7 +69,7 @@ public:
virtual void write(ostream &out, int indent_level = 0) const;
protected:
virtual BoundingVolume *recompute_bound(int pipeline_stage);
virtual PT(BoundingVolume) compute_internal_bounds() const;
virtual PT(CollisionEntry)
test_intersection_from_sphere(const CollisionEntry &entry) const;

View File

@ -78,7 +78,7 @@ CollisionRay(const CollisionRay &copy) :
INLINE void CollisionRay::
set_origin(const LPoint3f &origin) {
_origin = origin;
mark_bound_stale();
mark_internal_bounds_stale();
mark_viz_stale();
}
@ -110,7 +110,7 @@ get_origin() const {
INLINE void CollisionRay::
set_direction(const LVector3f &direction) {
_direction = direction;
mark_bound_stale();
mark_internal_bounds_stale();
mark_viz_stale();
nassertv(_direction != LPoint3f::zero());
}

View File

@ -119,23 +119,20 @@ set_from_lens(LensNode *camera, const LPoint2f &point) {
_direction = far_point - near_point;
}
mark_bound_stale();
mark_internal_bounds_stale();
mark_viz_stale();
return success;
}
////////////////////////////////////////////////////////////////////
// Function: CollisionRay::recompute_bound
// Function: CollisionRay::compute_internal_bounds
// Access: Protected, Virtual
// Description:
////////////////////////////////////////////////////////////////////
BoundingVolume *CollisionRay::
recompute_bound(int pipeline_stage) {
BoundedObject::recompute_bound(pipeline_stage);
// Less than ideal: we throw away whatever we just allocated in
// BoundedObject.
return set_bound_ptr(new BoundingLine(_origin, _origin + _direction));
PT(BoundingVolume) CollisionRay::
compute_internal_bounds() const {
return new BoundingLine(_origin, _origin + _direction);
}
////////////////////////////////////////////////////////////////////

View File

@ -65,7 +65,7 @@ PUBLISHED:
INLINE bool set_from_lens(LensNode *camera, float px, float py);
protected:
virtual BoundingVolume *recompute_bound(int pipeline_stage);
virtual PT(BoundingVolume) compute_internal_bounds() const;
protected:
virtual void fill_viz_geom();

View File

@ -16,6 +16,7 @@
//
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
// Function: CollisionSegment::Default Constructor
// Access: Public
@ -77,7 +78,7 @@ CollisionSegment(const CollisionSegment &copy) :
INLINE void CollisionSegment::
set_point_a(const LPoint3f &a) {
_a = a;
mark_bound_stale();
mark_internal_bounds_stale();
mark_viz_stale();
// We don't assert here that a != b, on the assumption that you
// might be about to change both at once, and you'll probably start
@ -112,7 +113,7 @@ get_point_a() const {
INLINE void CollisionSegment::
set_point_b(const LPoint3f &b) {
_b = b;
mark_bound_stale();
mark_internal_bounds_stale();
mark_viz_stale();
nassertv(_a != _b);
}

View File

@ -116,20 +116,20 @@ set_from_lens(LensNode *camera, const LPoint2f &point) {
success = false;
}
mark_bound_stale();
mark_internal_bounds_stale();
mark_viz_stale();
return success;
}
////////////////////////////////////////////////////////////////////
// Function: CollisionSegment::recompute_bound
// Function: CollisionSegment::compute_internal_bounds
// Access: Protected, Virtual
// Description:
////////////////////////////////////////////////////////////////////
BoundingVolume *CollisionSegment::
recompute_bound(int pipeline_stage) {
BoundingVolume *bound = BoundedObject::recompute_bound(pipeline_stage);
PT(BoundingVolume) CollisionSegment::
compute_internal_bounds() const {
PT(BoundingVolume) bound = CollisionSolid::compute_internal_bounds();
if (bound->is_of_type(GeometricBoundingVolume::get_class_type())) {
GeometricBoundingVolume *gbound;

View File

@ -68,7 +68,7 @@ PUBLISHED:
INLINE bool set_from_lens(LensNode *camera, float px, float py);
protected:
virtual BoundingVolume *recompute_bound(int pipeline_stage);
virtual PT(BoundingVolume) compute_internal_bounds() const;
protected:
virtual void fill_viz_geom();

View File

@ -135,6 +135,19 @@ get_respect_effective_normal() const {
return (_flags & F_ignore_effective_normal) == 0;
}
////////////////////////////////////////////////////////////////////
// Function: CollisionSolid::mark_internal_bounds_stale
// Access: Protected
// Description: Should be called by a derived class to mark the
// internal bounding volume stale, so that
// recompute_internal_bounds() will be called when the
// bounding volume is next requested.
////////////////////////////////////////////////////////////////////
INLINE void CollisionSolid::
mark_internal_bounds_stale() {
_flags |= F_internal_bounds_stale;
}
////////////////////////////////////////////////////////////////////
// Function: CollisionSolid::mark_viz_stale
// Access: Protected

View File

@ -22,7 +22,7 @@
#include "collisionLine.h"
#include "collisionRay.h"
#include "collisionSegment.h"
#include "boundingSphere.h"
#include "datagram.h"
#include "datagramIterator.h"
#include "bamReader.h"
@ -43,7 +43,7 @@ TypeHandle CollisionSolid::_type_handle;
////////////////////////////////////////////////////////////////////
CollisionSolid::
CollisionSolid() {
_flags = F_viz_geom_stale | F_tangible;
_flags = F_viz_geom_stale | F_tangible | F_internal_bounds_stale;
}
////////////////////////////////////////////////////////////////////
@ -54,6 +54,7 @@ CollisionSolid() {
CollisionSolid::
CollisionSolid(const CollisionSolid &copy) :
_effective_normal(copy._effective_normal),
_internal_bounds(copy._internal_bounds),
_flags(copy._flags)
{
_flags |= F_viz_geom_stale;
@ -68,6 +69,20 @@ CollisionSolid::
~CollisionSolid() {
}
////////////////////////////////////////////////////////////////////
// Function: CollisionSolid::get_bounds
// Access: Protected
// Description: Returns the solid's bounding volume.
////////////////////////////////////////////////////////////////////
CPT(BoundingVolume) CollisionSolid::
get_bounds() const {
if (_flags & F_internal_bounds_stale) {
((CollisionSolid *)this)->_internal_bounds = compute_internal_bounds();
((CollisionSolid *)this)->_flags &= ~F_internal_bounds_stale;
}
return _internal_bounds;
}
////////////////////////////////////////////////////////////////////
// Function: CollisionSolid::test_intersection
// Access: Public, Virtual
@ -96,7 +111,7 @@ xform(const LMatrix4f &mat) {
}
mark_viz_stale();
mark_bound_stale();
mark_internal_bounds_stale();
}
////////////////////////////////////////////////////////////////////
@ -148,6 +163,16 @@ write(ostream &out, int indent_level) const {
indent(out, indent_level) << (*this) << "\n";
}
////////////////////////////////////////////////////////////////////
// Function: CollisionSolid::compute_internal_bounds
// Access: Protected, Virtual
// Description:
////////////////////////////////////////////////////////////////////
PT(BoundingVolume) CollisionSolid::
compute_internal_bounds() const {
return new BoundingSphere;
}
////////////////////////////////////////////////////////////////////
// Function: CollisionSolid::test_intersection_from_sphere
// Access: Protected, Virtual

View File

@ -23,7 +23,6 @@
#include "config_collide.h"
#include "typedWritableReferenceCount.h"
#include "boundedObject.h"
#include "luse.h"
#include "pointerTo.h"
#include "renderState.h"
@ -49,8 +48,7 @@ class CullTraverserData;
// function calls handle the subset of the N*N
// intersection tests that we care about.
////////////////////////////////////////////////////////////////////
class EXPCL_PANDA CollisionSolid :
public TypedWritableReferenceCount, public BoundedObject {
class EXPCL_PANDA CollisionSolid : public TypedWritableReferenceCount {
public:
CollisionSolid();
CollisionSolid(const CollisionSolid &copy);
@ -71,6 +69,8 @@ PUBLISHED:
INLINE void set_respect_effective_normal(bool respect_effective_normal);
INLINE bool get_respect_effective_normal() const;
CPT(BoundingVolume) get_bounds() const;
public:
virtual PT(CollisionEntry)
test_intersection(const CollisionEntry &entry) const;
@ -86,6 +86,9 @@ PUBLISHED:
virtual void write(ostream &out, int indent_level = 0) const;
protected:
INLINE void mark_internal_bounds_stale();
virtual PT(BoundingVolume) compute_internal_bounds() const;
virtual PT(CollisionEntry)
test_intersection_from_sphere(const CollisionEntry &entry) const;
virtual PT(CollisionEntry)
@ -114,6 +117,7 @@ protected:
private:
LVector3f _effective_normal;
PT(BoundingVolume) _internal_bounds;
// Be careful reordering these bits, since they are written to a bam
// file.
@ -122,6 +126,7 @@ private:
F_effective_normal = 0x02,
F_viz_geom_stale = 0x04,
F_ignore_effective_normal = 0x08,
F_internal_bounds_stale = 0x10,
};
int _flags;
@ -137,10 +142,8 @@ public:
}
static void init_type() {
TypedWritableReferenceCount::init_type();
BoundedObject::init_type();
register_type(_type_handle, "CollisionSolid",
TypedWritableReferenceCount::get_class_type(),
BoundedObject::get_class_type());
TypedWritableReferenceCount::get_class_type());
}
virtual TypeHandle get_type() const {
return get_class_type();

View File

@ -70,7 +70,7 @@ CollisionSphere(const CollisionSphere &copy) :
INLINE void CollisionSphere::
set_center(const LPoint3f &center) {
_center = center;
mark_bound_stale();
mark_internal_bounds_stale();
mark_viz_stale();
}
@ -102,7 +102,7 @@ get_center() const {
INLINE void CollisionSphere::
set_radius(float radius) {
_radius = radius;
mark_bound_stale();
mark_internal_bounds_stale();
mark_viz_stale();
}

View File

@ -72,7 +72,7 @@ xform(const LMatrix4f &mat) {
LVector3f radius_v = LVector3f(_radius, 0.0f, 0.0f) * mat;
_radius = length(radius_v);
mark_viz_stale();
mark_bound_stale();
mark_internal_bounds_stale();
}
////////////////////////////////////////////////////////////////////
@ -99,19 +99,13 @@ output(ostream &out) const {
}
////////////////////////////////////////////////////////////////////
// Function: CollisionSphere::recompute_bound
// Function: CollisionSphere::compute_internal_bounds
// Access: Protected, Virtual
// Description:
////////////////////////////////////////////////////////////////////
BoundingVolume *CollisionSphere::
recompute_bound(int pipeline_stage) {
BoundingVolume *bound = BoundedObject::recompute_bound(pipeline_stage);
nassertr(bound != (BoundingVolume*)0L, bound);
nassertr(!_center.is_nan() && !cnan(_radius), bound);
BoundingSphere sphere(_center, _radius);
bound->extend_by(&sphere);
return bound;
PT(BoundingVolume) CollisionSphere::
compute_internal_bounds() const {
return new BoundingSphere(_center, _radius);
}
////////////////////////////////////////////////////////////////////

View File

@ -56,7 +56,7 @@ PUBLISHED:
INLINE float get_radius() const;
protected:
virtual BoundingVolume *recompute_bound(int pipeline_stage);
virtual PT(BoundingVolume) compute_internal_bounds() const;
virtual PT(CollisionEntry)
test_intersection_from_sphere(const CollisionEntry &entry) const;

View File

@ -519,7 +519,7 @@ r_traverse(CollisionLevelState &level_state) {
if (node->is_exact_type(CollisionNode::get_class_type())) {
CollisionNode *cnode;
DCAST_INTO_V(cnode, node);
const BoundingVolume *node_bv = cnode->get_bound();
CPT(BoundingVolume) node_bv = cnode->get_bounds();
const GeometricBoundingVolume *node_gbv = NULL;
if (node_bv->is_of_type(GeometricBoundingVolume::get_class_type())) {
DCAST_INTO_V(node_gbv, node_bv);
@ -560,7 +560,7 @@ r_traverse(CollisionLevelState &level_state) {
GeomNode *gnode;
DCAST_INTO_V(gnode, node);
const BoundingVolume *node_bv = gnode->get_bound();
CPT(BoundingVolume) node_bv = gnode->get_bounds();
const GeometricBoundingVolume *node_gbv = NULL;
if (node_bv->is_of_type(GeometricBoundingVolume::get_class_type())) {
DCAST_INTO_V(node_gbv, node_bv);
@ -642,7 +642,7 @@ compare_collider_to_node(CollisionEntry &entry,
for (int s = 0; s < num_solids; ++s) {
entry._into = cnode->get_solid(s);
if (entry._from != entry._into) {
const BoundingVolume *solid_bv = entry._into->get_bound();
CPT(BoundingVolume) solid_bv = entry._into->get_bounds();
const GeometricBoundingVolume *solid_gbv = NULL;
if (num_solids > 1 &&
solid_bv->is_of_type(GeometricBoundingVolume::get_class_type())) {
@ -684,7 +684,7 @@ compare_collider_to_geom_node(CollisionEntry &entry,
entry._into = (CollisionSolid *)NULL;
const Geom *geom = DCAST(Geom, gnode->get_geom(s));
if (geom != (Geom *)NULL) {
const BoundingVolume *geom_bv = geom->get_bound();
CPT(BoundingVolume) geom_bv = geom->get_bounds();
const GeometricBoundingVolume *geom_gbv = NULL;
if (num_geoms > 1 &&
geom_bv->is_of_type(GeometricBoundingVolume::get_class_type())) {

View File

@ -141,7 +141,7 @@ set_radius(float radius) {
// We don't need to call recalc_internals(), since the radius
// doesn't change either of those properties.
mark_bound_stale();
mark_internal_bounds_stale();
mark_viz_stale();
}

View File

@ -94,13 +94,13 @@ output(ostream &out) const {
}
////////////////////////////////////////////////////////////////////
// Function: CollisionTube::recompute_bound
// Function: CollisionTube::compute_internal_bounds
// Access: Protected, Virtual
// Description:
////////////////////////////////////////////////////////////////////
BoundingVolume *CollisionTube::
recompute_bound(int pipeline_stage) {
BoundingVolume *bound = BoundedObject::recompute_bound(pipeline_stage);
PT(BoundingVolume) CollisionTube::
compute_internal_bounds() const {
PT(BoundingVolume) bound = CollisionSolid::compute_internal_bounds();
if (bound->is_of_type(GeometricBoundingVolume::get_class_type())) {
GeometricBoundingVolume *gbound;
@ -458,7 +458,7 @@ recalc_internals() {
_inv_mat.invert_from(_mat);
mark_viz_stale();
mark_bound_stale();
mark_internal_bounds_stale();
}
////////////////////////////////////////////////////////////////////

View File

@ -64,7 +64,7 @@ PUBLISHED:
INLINE float get_radius() const;
protected:
virtual BoundingVolume *recompute_bound(int pipeline_stage);
virtual PT(BoundingVolume) compute_internal_bounds() const;
protected:
virtual PT(CollisionEntry)

View File

@ -50,7 +50,7 @@ CollisionVisualizer::
CollisionVisualizer(const string &name) : PandaNode(name) {
// We always want to render the CollisionVisualizer node itself
// (even if it doesn't appear to have any geometry within it).
set_bound(OmniBoundingVolume());
set_internal_bounds(new OmniBoundingVolume());
_viz_scale = 1.0f;
}

View File

@ -254,8 +254,8 @@ define_output(const string &name, TypeHandle data_type) {
// node has.
////////////////////////////////////////////////////////////////////
void DataNode::
parents_changed(int pipeline_stage) {
PandaNode::parents_changed(pipeline_stage);
parents_changed() {
PandaNode::parents_changed();
reconnect();
}

View File

@ -90,7 +90,7 @@ protected:
protected:
// Inherited from PandaNode
virtual void parents_changed(int pipeline_stage);
virtual void parents_changed();
// Local to DataNode
virtual void do_transmit_data(const DataNodeTransmit &input,

View File

@ -18,6 +18,7 @@
#include "config_display.h"
#include "displayRegion.h"
#include "standardMunger.h"
#include "graphicsStateGuardian.h"
#include "graphicsPipe.h"
@ -26,6 +27,7 @@
#include "graphicsWindow.h"
#include "graphicsDevice.h"
#include "parasiteBuffer.h"
#include "pandaSystem.h"
ConfigureDef(config_display);
NotifyCategoryDef(display, "");
@ -260,13 +262,19 @@ init_libdisplay() {
return;
}
initialized = true;
StandardMunger::init_type();
GraphicsStateGuardian::init_type();
GraphicsPipe::init_type();
GraphicsOutput::init_type();
GraphicsWindow::init_type();
DisplayRegion::init_type();
GraphicsBuffer::init_type();
GraphicsDevice::init_type();
GraphicsOutput::init_type();
GraphicsPipe::init_type();
GraphicsStateGuardian::init_type();
GraphicsWindow::init_type();
ParasiteBuffer::init_type();
StandardMunger::init_type();
#if defined(HAVE_THREADS) && defined(DO_PIPELINING)
PandaSystem *ps = PandaSystem::get_global_ptr();
ps->add_system("pipelining");
#endif
}

View File

@ -25,7 +25,7 @@
////////////////////////////////////////////////////////////////////
INLINE bool DisplayRegion::
operator < (const DisplayRegion &other) const {
return _sort < other._sort;
return get_sort() < other.get_sort();
}
////////////////////////////////////////////////////////////////////
@ -36,7 +36,8 @@ operator < (const DisplayRegion &other) const {
////////////////////////////////////////////////////////////////////
INLINE bool DisplayRegion::
is_active() const {
return _active;
CDReader cdata(_cycler);
return cdata->_active;
}
////////////////////////////////////////////////////////////////////
@ -47,7 +48,8 @@ is_active() const {
////////////////////////////////////////////////////////////////////
INLINE int DisplayRegion::
get_sort() const {
return _sort;
CDReader cdata(_cycler);
return cdata->_sort;
}
////////////////////////////////////////////////////////////////////
@ -63,7 +65,10 @@ get_sort() const {
////////////////////////////////////////////////////////////////////
INLINE void DisplayRegion::
set_cube_map_index(int cube_map_index) {
_cube_map_index = cube_map_index;
int pipeline_stage = Thread::get_current_pipeline_stage();
nassertv(pipeline_stage == 0);
CDWriter cdata(_cycler);
cdata->_cube_map_index = cube_map_index;
}
////////////////////////////////////////////////////////////////////
@ -76,7 +81,77 @@ set_cube_map_index(int cube_map_index) {
////////////////////////////////////////////////////////////////////
INLINE int DisplayRegion::
get_cube_map_index() const {
return _cube_map_index;
CDReader cdata(_cycler);
return cdata->_cube_map_index;
}
////////////////////////////////////////////////////////////////////
// Function: DisplayRegion::set_cull_result
// Access: Public
// Description: Stores the result of performing a cull operation on
// this DisplayRegion. Normally, this will only be
// called by the GraphicsEngine; you should not call
// this directly.
//
// The stored result will automatically be applied back
// to all upstream pipeline stages.
////////////////////////////////////////////////////////////////////
INLINE void DisplayRegion::
set_cull_result(CullResult *cull_result, SceneSetup *scene_setup) {
CDCullWriter cdata(_cycler_cull, true);
cdata->_cull_result = cull_result;
cdata->_scene_setup = scene_setup;
}
////////////////////////////////////////////////////////////////////
// Function: DisplayRegion::get_cull_result
// Access: Public
// Description: Returns the CullResult value that was stored on this
// DisplayRegion, presumably by the last successful cull
// operation. This method is for the benefit of the
// GraphicsEngine; normally you shouldn't call this
// directly.
////////////////////////////////////////////////////////////////////
INLINE CullResult *DisplayRegion::
get_cull_result() const {
CDCullReader cdata(_cycler_cull);
return cdata->_cull_result;
}
////////////////////////////////////////////////////////////////////
// Function: DisplayRegion::get_scene_setup
// Access: Public
// Description: Returns the SceneSetup value that was stored on this
// DisplayRegion, presumably by the last successful cull
// operation. This method is for the benefit of the
// GraphicsEngine; normally you shouldn't call this
// directly.
////////////////////////////////////////////////////////////////////
INLINE SceneSetup *DisplayRegion::
get_scene_setup() const {
CDCullReader cdata(_cycler_cull);
return cdata->_scene_setup;
}
////////////////////////////////////////////////////////////////////
// Function: DisplayRegion::CDataCull::Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
INLINE DisplayRegion::CDataCull::
CDataCull() {
}
////////////////////////////////////////////////////////////////////
// Function: DisplayRegion::CDataCull::Copy Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
INLINE DisplayRegion::CDataCull::
CDataCull(const DisplayRegion::CDataCull &copy) :
_cull_result(copy._cull_result),
_scene_setup(copy._scene_setup)
{
}
INLINE ostream &operator << (ostream &out, const DisplayRegion &dr) {

View File

@ -22,9 +22,9 @@
#include "texture.h"
#include "camera.h"
#include "dcast.h"
#include "mutexHolder.h"
#include "pnmImage.h"
TypeHandle DisplayRegion::_type_handle;
////////////////////////////////////////////////////////////////////
// Function: DisplayRegion::Constructor
@ -33,12 +33,7 @@
////////////////////////////////////////////////////////////////////
DisplayRegion::
DisplayRegion(GraphicsOutput *window) :
_l(0.), _r(1.), _b(0.), _t(1.),
_window(window),
_camera_node((Camera *)NULL),
_active(true),
_sort(0),
_cube_map_index(-1)
_window(window)
{
_draw_buffer_type = window->get_draw_buffer_type();
compute_pixels();
@ -50,17 +45,11 @@ DisplayRegion(GraphicsOutput *window) :
// Description:
////////////////////////////////////////////////////////////////////
DisplayRegion::
DisplayRegion(GraphicsOutput *window, const float l,
const float r, const float b, const float t) :
_l(l), _r(r), _b(b), _t(t),
_window(window),
_camera_node((Camera *)NULL),
_active(true),
_sort(0),
_cube_map_index(-1)
DisplayRegion(GraphicsOutput *window, float l, float r, float b, float t) :
_window(window)
{
_draw_buffer_type = window->get_draw_buffer_type();
compute_pixels();
set_dimensions(l, r, b, t);
}
////////////////////////////////////////////////////////////////////
@ -109,7 +98,8 @@ void DisplayRegion::
cleanup() {
set_camera(NodePath());
_cull_result = NULL;
CDCullWriter cdata(_cycler_cull, true);
cdata->_cull_result = NULL;
}
////////////////////////////////////////////////////////////////////
@ -121,11 +111,11 @@ cleanup() {
////////////////////////////////////////////////////////////////////
void DisplayRegion::
get_dimensions(float &l, float &r, float &b, float &t) const {
MutexHolder holder(_lock);
l = _l;
r = _r;
b = _b;
t = _t;
CDReader cdata(_cycler);
l = cdata->_l;
r = cdata->_r;
b = cdata->_b;
t = cdata->_t;
}
////////////////////////////////////////////////////////////////////
@ -137,8 +127,8 @@ get_dimensions(float &l, float &r, float &b, float &t) const {
////////////////////////////////////////////////////////////////////
float DisplayRegion::
get_left() const {
MutexHolder holder(_lock);
return _l;
CDReader cdata(_cycler);
return cdata->_l;
}
////////////////////////////////////////////////////////////////////
@ -150,8 +140,8 @@ get_left() const {
////////////////////////////////////////////////////////////////////
float DisplayRegion::
get_right() const {
MutexHolder holder(_lock);
return _r;
CDReader cdata(_cycler);
return cdata->_r;
}
////////////////////////////////////////////////////////////////////
@ -163,8 +153,8 @@ get_right() const {
////////////////////////////////////////////////////////////////////
float DisplayRegion::
get_bottom() const {
MutexHolder holder(_lock);
return _b;
CDReader cdata(_cycler);
return cdata->_b;
}
////////////////////////////////////////////////////////////////////
@ -176,8 +166,8 @@ get_bottom() const {
////////////////////////////////////////////////////////////////////
float DisplayRegion::
get_top() const {
MutexHolder holder(_lock);
return _t;
CDReader cdata(_cycler);
return cdata->_t;
}
////////////////////////////////////////////////////////////////////
@ -191,14 +181,17 @@ get_top() const {
////////////////////////////////////////////////////////////////////
void DisplayRegion::
set_dimensions(float l, float r, float b, float t) {
MutexHolder holder(_lock);
_l = l;
_r = r;
_b = b;
_t = t;
int pipeline_stage = Thread::get_current_pipeline_stage();
nassertv(pipeline_stage == 0);
CDWriter cdata(_cycler);
cdata->_l = l;
cdata->_r = r;
cdata->_b = b;
cdata->_t = t;
if (_window != (GraphicsOutput *)NULL && _window->has_size()) {
do_compute_pixels(_window->get_x_size(), _window->get_y_size());
do_compute_pixels(_window->get_x_size(), _window->get_y_size(), cdata);
}
}
@ -211,7 +204,6 @@ set_dimensions(float l, float r, float b, float t) {
////////////////////////////////////////////////////////////////////
GraphicsOutput *DisplayRegion::
get_window() const {
MutexHolder holder(_lock);
return _window;
}
@ -224,7 +216,6 @@ get_window() const {
////////////////////////////////////////////////////////////////////
GraphicsPipe *DisplayRegion::
get_pipe() const {
MutexHolder holder(_lock);
return (_window != (GraphicsOutput *)NULL) ? _window->get_pipe() : NULL;
}
@ -242,25 +233,32 @@ get_pipe() const {
////////////////////////////////////////////////////////////////////
void DisplayRegion::
set_camera(const NodePath &camera) {
MutexHolder holder(_lock);
int pipeline_stage = Thread::get_current_pipeline_stage();
nassertv(pipeline_stage == 0);
CDWriter cdata(_cycler);
Camera *camera_node = (Camera *)NULL;
if (!camera.is_empty()) {
DCAST_INTO_V(camera_node, camera.node());
}
if (camera_node != _camera_node) {
if (_camera_node != (Camera *)NULL) {
if (camera_node != cdata->_camera_node) {
// Note that these operations on the DisplayRegion are not
// pipelined: they operate across all pipeline stages. Since we
// have already asserted we are running in pipeline stage 0, no
// problem.
if (cdata->_camera_node != (Camera *)NULL) {
// We need to tell the old camera we're not using him anymore.
_camera_node->remove_display_region(this);
cdata->_camera_node->remove_display_region(this);
}
_camera_node = camera_node;
if (_camera_node != (Camera *)NULL) {
cdata->_camera_node = camera_node;
if (cdata->_camera_node != (Camera *)NULL) {
// Now tell the new camera we are using him.
_camera_node->add_display_region(this);
cdata->_camera_node->add_display_region(this);
}
}
_camera = camera;
cdata->_camera = camera;
}
////////////////////////////////////////////////////////////////////
@ -272,8 +270,8 @@ set_camera(const NodePath &camera) {
////////////////////////////////////////////////////////////////////
NodePath DisplayRegion::
get_camera() const {
MutexHolder holder(_lock);
return _camera;
CDReader cdata(_cycler);
return cdata->_camera;
}
////////////////////////////////////////////////////////////////////
@ -285,9 +283,13 @@ get_camera() const {
////////////////////////////////////////////////////////////////////
void DisplayRegion::
set_active(bool active) {
MutexHolder holder(_lock);
if (active != _active) {
_active = active;
int pipeline_stage = Thread::get_current_pipeline_stage();
nassertv(pipeline_stage == 0);
CDReader cdata(_cycler);
if (active != cdata->_active) {
CDWriter cdataw(_cycler, cdata);
cdataw->_active = active;
win_display_regions_changed();
}
}
@ -302,9 +304,13 @@ set_active(bool active) {
////////////////////////////////////////////////////////////////////
void DisplayRegion::
set_sort(int sort) {
MutexHolder holder(_lock);
if (sort != _sort) {
_sort = sort;
int pipeline_stage = Thread::get_current_pipeline_stage();
nassertv(pipeline_stage == 0);
CDReader cdata(_cycler);
if (sort != cdata->_sort) {
CDWriter cdataw(_cycler, cdata);
cdataw->_sort = sort;
win_display_regions_changed();
}
}
@ -318,9 +324,13 @@ set_sort(int sort) {
////////////////////////////////////////////////////////////////////
void DisplayRegion::
compute_pixels() {
MutexHolder holder(_lock);
int pipeline_stage = Thread::get_current_pipeline_stage();
nassertv(pipeline_stage == 0);
if (_window != (GraphicsOutput *)NULL) {
do_compute_pixels(_window->get_x_size(), _window->get_y_size());
CDWriter cdata(_cycler);
do_compute_pixels(_window->get_x_size(), _window->get_y_size(),
cdata);
}
}
@ -333,8 +343,10 @@ compute_pixels() {
////////////////////////////////////////////////////////////////////
void DisplayRegion::
compute_pixels(int x_size, int y_size) {
MutexHolder holder(_lock);
do_compute_pixels(x_size, y_size);
int pipeline_stage = Thread::get_current_pipeline_stage();
nassertv(pipeline_stage == 0);
CDWriter cdata(_cycler);
do_compute_pixels(x_size, y_size, cdata);
}
////////////////////////////////////////////////////////////////////
@ -345,11 +357,11 @@ compute_pixels(int x_size, int y_size) {
////////////////////////////////////////////////////////////////////
void DisplayRegion::
get_pixels(int &pl, int &pr, int &pb, int &pt) const {
MutexHolder holder(_lock);
pl = _pl;
pr = _pr;
pb = _pb;
pt = _pt;
CDReader cdata(_cycler);
pl = cdata->_pl;
pr = cdata->_pr;
pb = cdata->_pb;
pt = cdata->_pt;
}
////////////////////////////////////////////////////////////////////
@ -361,11 +373,11 @@ get_pixels(int &pl, int &pr, int &pb, int &pt) const {
////////////////////////////////////////////////////////////////////
void DisplayRegion::
get_region_pixels(int &xo, int &yo, int &w, int &h) const {
MutexHolder holder(_lock);
xo = _pl;
yo = _pb;
w = _pr - _pl;
h = _pt - _pb;
CDReader cdata(_cycler);
xo = cdata->_pl;
yo = cdata->_pb;
w = cdata->_pr - cdata->_pl;
h = cdata->_pt - cdata->_pb;
}
////////////////////////////////////////////////////////////////////
@ -378,11 +390,11 @@ get_region_pixels(int &xo, int &yo, int &w, int &h) const {
////////////////////////////////////////////////////////////////////
void DisplayRegion::
get_region_pixels_i(int &xo, int &yo, int &w, int &h) const {
MutexHolder holder(_lock);
xo = _pl;
yo = _pti;
w = _pr - _pl;
h = _pbi - _pti;
CDReader cdata(_cycler);
xo = cdata->_pl;
yo = cdata->_pti;
w = cdata->_pr - cdata->_pl;
h = cdata->_pbi - cdata->_pti;
}
////////////////////////////////////////////////////////////////////
@ -392,8 +404,8 @@ get_region_pixels_i(int &xo, int &yo, int &w, int &h) const {
////////////////////////////////////////////////////////////////////
int DisplayRegion::
get_pixel_width() const {
MutexHolder holder(_lock);
return _pr - _pl;
CDReader cdata(_cycler);
return cdata->_pr - cdata->_pl;
}
////////////////////////////////////////////////////////////////////
@ -403,8 +415,8 @@ get_pixel_width() const {
////////////////////////////////////////////////////////////////////
int DisplayRegion::
get_pixel_height() const {
MutexHolder holder(_lock);
return _pt - _pb;
CDReader cdata(_cycler);
return cdata->_pt - cdata->_pb;
}
////////////////////////////////////////////////////////////////////
@ -414,9 +426,10 @@ get_pixel_height() const {
////////////////////////////////////////////////////////////////////
void DisplayRegion::
output(ostream &out) const {
MutexHolder holder(_lock);
out << "DisplayRegion(" << _l << " " << _r << " " << _b << " " << _t
<< ")=pixels(" << _pl << " " << _pr << " " << _pb << " " << _pt
CDReader cdata(_cycler);
out << "DisplayRegion(" << cdata->_l << " " << cdata->_r << " "
<< cdata->_b << " " << cdata->_t << ")=pixels(" << cdata->_pl
<< " " << cdata->_pr << " " << cdata->_pb << " " << cdata->_pt
<< ")";
}
@ -593,28 +606,88 @@ win_display_regions_changed() {
// assumes that we already have the lock.
////////////////////////////////////////////////////////////////////
void DisplayRegion::
do_compute_pixels(int x_size, int y_size) {
do_compute_pixels(int x_size, int y_size, CData *cdata) {
if (display_cat.is_debug()) {
display_cat.debug()
<< "DisplayRegion::do_compute_pixels(" << x_size << ", " << y_size << ")\n";
}
_pl = int((_l * x_size) + 0.5);
_pr = int((_r * x_size) + 0.5);
cdata->_pl = int((cdata->_l * x_size) + 0.5);
cdata->_pr = int((cdata->_r * x_size) + 0.5);
nassertv(_window != (GraphicsOutput *)NULL);
if (_window->get_inverted()) {
// The window is inverted; compute the DisplayRegion accordingly.
_pb = int(((1.0f - _t) * y_size) + 0.5);
_pt = int(((1.0f - _b) * y_size) + 0.5);
_pbi = int((_t * y_size) + 0.5);
_pti = int((_b * y_size) + 0.5);
cdata->_pb = int(((1.0f - cdata->_t) * y_size) + 0.5);
cdata->_pt = int(((1.0f - cdata->_b) * y_size) + 0.5);
cdata->_pbi = int((cdata->_t * y_size) + 0.5);
cdata->_pti = int((cdata->_b * y_size) + 0.5);
} else {
// The window is normal.
_pb = int((_b * y_size) + 0.5);
_pt = int((_t * y_size) + 0.5);
_pbi = int(((1.0f - _b) * y_size) + 0.5);
_pti = int(((1.0f - _t) * y_size) + 0.5);
cdata->_pb = int((cdata->_b * y_size) + 0.5);
cdata->_pt = int((cdata->_t * y_size) + 0.5);
cdata->_pbi = int(((1.0f - cdata->_b) * y_size) + 0.5);
cdata->_pti = int(((1.0f - cdata->_t) * y_size) + 0.5);
}
}
////////////////////////////////////////////////////////////////////
// Function: DisplayRegion::CData::Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
DisplayRegion::CData::
CData() :
_l(0.), _r(1.), _b(0.), _t(1.),
_camera_node((Camera *)NULL),
_active(true),
_sort(0),
_cube_map_index(-1)
{
}
////////////////////////////////////////////////////////////////////
// Function: DisplayRegion::CData::Copy Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
DisplayRegion::CData::
CData(const DisplayRegion::CData &copy) :
_l(copy._l),
_r(copy._r),
_b(copy._b),
_t(copy._t),
_pl(copy._pl),
_pr(copy._pr),
_pb(copy._pb),
_pt(copy._pt),
_pbi(copy._pbi),
_pti(copy._pti),
_camera(copy._camera),
_camera_node(copy._camera_node),
_active(copy._active),
_sort(copy._sort),
_cube_map_index(copy._cube_map_index)
{
}
////////////////////////////////////////////////////////////////////
// Function: DisplayRegion::CData::make_copy
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
CycleData *DisplayRegion::CData::
make_copy() const {
return new CData(*this);
}
////////////////////////////////////////////////////////////////////
// Function: DisplayRegion::CDataCull::make_copy
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
CycleData *DisplayRegion::CDataCull::
make_copy() const {
return new CDataCull(*this);
}

View File

@ -15,6 +15,7 @@
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////
#ifndef DISPLAYREGION_H
#define DISPLAYREGION_H
@ -24,8 +25,12 @@
#include "referenceCount.h"
#include "nodePath.h"
#include "cullResult.h"
#include "sceneSetup.h"
#include "pointerTo.h"
#include "pmutex.h"
#include "cycleData.h"
#include "cycleDataReader.h"
#include "cycleDataWriter.h"
#include "pipelineCycler.h"
#include "config_display.h"
#include "plist.h"
@ -51,8 +56,7 @@ class EXPCL_PANDA DisplayRegion : public ReferenceCount, public DrawableRegion {
protected:
DisplayRegion(GraphicsOutput *window);
DisplayRegion(GraphicsOutput *window,
const float l, const float r,
const float b, const float t);
float l, float r, float b, float t);
private:
DisplayRegion(const DisplayRegion &copy);
void operator = (const DisplayRegion &copy);
@ -102,40 +106,95 @@ PUBLISHED:
bool save_screenshot(const Filename &filename);
bool get_screenshot(PNMImage &image);
public:
INLINE void set_cull_result(CullResult *cull_result, SceneSetup *scene_setup);
INLINE CullResult *get_cull_result() const;
INLINE SceneSetup *get_scene_setup() const;
private:
class CData;
void win_display_regions_changed();
void do_compute_pixels(int x_size, int y_size);
Mutex _lock;
float _l;
float _r;
float _b;
float _t;
int _pl;
int _pr;
int _pb;
int _pt;
int _pbi;
int _pti;
void do_compute_pixels(int x_size, int y_size, CData *cdata);
// The associated window is a permanent property of the
// DisplayRegion. It doesn't need to be cycled.
GraphicsOutput *_window;
NodePath _camera;
// This needs to be a PT(Camera) so we prevent the Camera node from
// destructing while we hold its pointer.
PT(Camera) _camera_node;
// This is the data that is associated with the DisplayRegion that
// needs to be cycled every frame, but represents the parameters as
// specified by the user, and which probably will not change that
// often.
class EXPCL_PANDA CData : public CycleData {
public:
CData();
CData(const CData &copy);
bool _active;
int _sort;
int _cube_map_index;
virtual CycleData *make_copy() const;
virtual TypeHandle get_parent_type() const {
return DisplayRegion::get_class_type();
}
// This is used to cache the culling result from last frame's
// drawing into this display region. It should only be accessed or
// modified by the GraphicsEngine, during the cull traversal.
PT(CullResult) _cull_result;
float _l;
float _r;
float _b;
float _t;
int _pl;
int _pr;
int _pb;
int _pt;
int _pbi;
int _pti;
NodePath _camera;
Camera *_camera_node;
bool _active;
int _sort;
int _cube_map_index;
};
PipelineCycler<CData> _cycler;
typedef CycleDataReader<CData> CDReader;
typedef CycleDataWriter<CData> CDWriter;
// This is a special cycler created to hold the results from the
// cull traversal, for (a) the draw traversal, and (b) the next
// frame's cull traversal. It needs to be cycled, but it gets its
// own cycler because it will certainly change every frame, so we
// don't need to lump all the heavy data above in with this
// lightweight cycler.
class EXPCL_PANDA CDataCull : public CycleData {
public:
CDataCull();
CDataCull(const CDataCull &copy);
virtual CycleData *make_copy() const;
virtual TypeHandle get_parent_type() const {
return DisplayRegion::get_class_type();
}
PT(CullResult) _cull_result;
PT(SceneSetup) _scene_setup;
};
PipelineCycler<CDataCull> _cycler_cull;
typedef CycleDataReader<CDataCull> CDCullReader;
typedef CycleDataWriter<CDataCull> CDCullWriter;
public:
static TypeHandle get_class_type() {
return _type_handle;
}
static void init_type() {
ReferenceCount::init_type();
register_type(_type_handle, "DisplayRegion",
ReferenceCount::get_class_type());
}
private:
static TypeHandle _type_handle;
friend class GraphicsEngine;
friend class GraphicsOutput;
};

View File

@ -149,13 +149,14 @@ get_frame_buffer_properties() const {
////////////////////////////////////////////////////////////////////
void GraphicsEngine::
set_threading_model(const GraphicsThreadingModel &threading_model) {
if (!threading_model.is_single_threaded() &&
!Thread::is_threading_supported()) {
#ifndef THREADED_PIPELINE
if (!threading_model.is_single_threaded()) {
display_cat.warning()
<< "Threading model " << threading_model
<< " requested but threading not supported.\n";
<< " requested but multithreaded render pipelines not enabled in build.\n";
return;
}
#endif // THREADED_PIPELINE
MutexHolder holder(_lock);
_threading_model = threading_model;
}
@ -712,7 +713,7 @@ void GraphicsEngine::
render_subframe(GraphicsOutput *win, DisplayRegion *dr,
bool cull_sorting) {
if (cull_sorting) {
cull_bin_draw(win, dr);
cull_to_bins(win, dr);
} else {
cull_and_draw_together(win, dr);
}
@ -740,7 +741,7 @@ add_callback(const string &thread_name,
GraphicsEngine::CallbackTime callback_time,
GraphicsEngine::CallbackFunction *func, void *data) {
MutexHolder holder(_lock);
WindowRenderer *wr = get_window_renderer(thread_name);
WindowRenderer *wr = get_window_renderer(thread_name, 0);
return wr->add_callback(callback_time, Callback(func, data));
}
@ -757,11 +758,11 @@ add_callback(const string &thread_name,
// removed).
////////////////////////////////////////////////////////////////////
bool GraphicsEngine::
remove_callback(const string &thread_name,
remove_callback(const string &thread_name,
GraphicsEngine::CallbackTime callback_time,
GraphicsEngine::CallbackFunction *func, void *data) {
MutexHolder holder(_lock);
WindowRenderer *wr = get_window_renderer(thread_name);
WindowRenderer *wr = get_window_renderer(thread_name, 0);
return wr->remove_callback(callback_time, Callback(func, data));
}
@ -857,7 +858,7 @@ cull_and_draw_together(GraphicsOutput *win, DisplayRegion *dr) {
}
////////////////////////////////////////////////////////////////////
// Function: GraphicsEngine::cull_bin_draw
// Function: GraphicsEngine::cull_to_bins
// Access: Private
// Description: This is called in the cull thread by individual
// RenderThread objects during the frame rendering. It
@ -865,12 +866,68 @@ cull_and_draw_together(GraphicsOutput *win, DisplayRegion *dr) {
// drawing.
////////////////////////////////////////////////////////////////////
void GraphicsEngine::
cull_bin_draw(const GraphicsEngine::Windows &wlist) {
cull_to_bins(const GraphicsEngine::Windows &wlist) {
Windows::const_iterator wi;
for (wi = wlist.begin(); wi != wlist.end(); ++wi) {
GraphicsOutput *win = (*wi);
if (win->is_active() && win->get_gsg()->is_active()) {
int num_display_regions = win->get_num_active_display_regions();
for (int i = 0; i < num_display_regions; ++i) {
DisplayRegion *dr = win->get_active_display_region(i);
if (dr != (DisplayRegion *)NULL) {
cull_to_bins(win, dr);
}
}
}
}
}
////////////////////////////////////////////////////////////////////
// Function: GraphicsEngine::cull_to_bins
// Access: Private
// Description: This variant of cull_to_bins() is called
// by render_subframe(), as well as within the
// implementation of cull_to_bins(), above.
////////////////////////////////////////////////////////////////////
void GraphicsEngine::
cull_to_bins(GraphicsOutput *win, DisplayRegion *dr) {
GraphicsStateGuardian *gsg = win->get_gsg();
nassertv(gsg != (GraphicsStateGuardian *)NULL);
PT(CullResult) cull_result = dr->get_cull_result();
if (cull_result != (CullResult *)NULL) {
cull_result = cull_result->make_next();
} else {
cull_result = new CullResult(gsg);
}
PT(SceneSetup) scene_setup = setup_scene(gsg, dr);
if (scene_setup != (SceneSetup *)NULL) {
BinCullHandler cull_handler(cull_result);
do_cull(&cull_handler, scene_setup, gsg);
cull_result->finish_cull();
// Save the results for next frame.
dr->set_cull_result(cull_result, scene_setup);
}
}
////////////////////////////////////////////////////////////////////
// Function: GraphicsEngine::draw_bins
// Access: Private
// Description: This is called in the draw thread by individual
// RenderThread objects during the frame rendering. It
// issues the graphics commands to draw the objects that
// have been collected into bins by a previous call to
// cull_to_bins().
////////////////////////////////////////////////////////////////////
void GraphicsEngine::
draw_bins(const GraphicsEngine::Windows &wlist) {
Windows::const_iterator wi;
for (wi = wlist.begin(); wi != wlist.end(); ++wi) {
GraphicsOutput *win = (*wi);
if (win->is_active() && win->get_gsg()->is_active()) {
// This should be done in the draw thread, not here.
if (win->begin_frame(GraphicsOutput::FM_render)) {
win->clear();
@ -878,7 +935,7 @@ cull_bin_draw(const GraphicsEngine::Windows &wlist) {
for (int i = 0; i < num_display_regions; ++i) {
DisplayRegion *dr = win->get_active_display_region(i);
if (dr != (DisplayRegion *)NULL) {
cull_bin_draw(win, dr);
draw_bins(win, dr);
}
}
win->end_frame(GraphicsOutput::FM_render);
@ -901,36 +958,20 @@ cull_bin_draw(const GraphicsEngine::Windows &wlist) {
}
////////////////////////////////////////////////////////////////////
// Function: GraphicsEngine::cull_bin_draw
// Function: GraphicsEngine::draw_bins
// Access: Private
// Description: This variant of cull_bin_draw() is called
// by render_subframe(), as well as within the
// implementation of cull_bin_draw(), above.
// Description: This variant on draw_bins() is only called from
// draw_bins(), above. It draws the cull result for a
// particular DisplayRegion.
////////////////////////////////////////////////////////////////////
void GraphicsEngine::
cull_bin_draw(GraphicsOutput *win, DisplayRegion *dr) {
draw_bins(GraphicsOutput *win, DisplayRegion *dr) {
GraphicsStateGuardian *gsg = win->get_gsg();
nassertv(gsg != (GraphicsStateGuardian *)NULL);
PT(CullResult) cull_result = dr->_cull_result;
if (cull_result != (CullResult *)NULL) {
cull_result = cull_result->make_next();
} else {
cull_result = new CullResult(gsg);
}
PT(SceneSetup) scene_setup = setup_scene(gsg, dr);
if (scene_setup != (SceneSetup *)NULL) {
BinCullHandler cull_handler(cull_result);
do_cull(&cull_handler, scene_setup, gsg);
cull_result->finish_cull();
// Save the results for next frame.
dr->_cull_result = cull_result;
// Now draw.
// This should get deferred into the next pipeline stage.
PT(CullResult) cull_result = dr->get_cull_result();
PT(SceneSetup) scene_setup = dr->get_scene_setup();
if (cull_result != (CullResult *)NULL && scene_setup != (SceneSetup *)NULL) {
do_draw(cull_result, scene_setup, win, dr);
}
}
@ -1046,7 +1087,7 @@ do_flip_frame() {
for (ti = _threads.begin(); ti != _threads.end(); ++ti) {
RenderThread *thread = (*ti).second;
thread->_cv_mutex.lock();
while (thread->_thread_state != TS_wait) {
thread->_cv_done.wait();
}
@ -1269,8 +1310,12 @@ do_add_window(GraphicsOutput *window, GraphicsStateGuardian *gsg,
_windows_sorted = false;
_windows.push_back(window);
WindowRenderer *cull = get_window_renderer(threading_model.get_cull_name());
WindowRenderer *draw = get_window_renderer(threading_model.get_draw_name());
WindowRenderer *cull =
get_window_renderer(threading_model.get_cull_name(),
threading_model.get_cull_stage());
WindowRenderer *draw =
get_window_renderer(threading_model.get_draw_name(),
threading_model.get_draw_stage());
draw->add_gsg(gsg);
if (threading_model.get_cull_sorting()) {
@ -1438,11 +1483,15 @@ get_invert_polygon_state() {
// Access: Private
// Description: Returns the WindowRenderer with the given name.
// Creates a new RenderThread if there is no such thread
// already. You must already be holding the lock before
// calling this method.
// already. The pipeline_stage number specifies the
// pipeline stage that will be assigned to the thread
// (unless was previously given a higher stage).
//
// You must already be holding the lock before calling
// this method.
////////////////////////////////////////////////////////////////////
GraphicsEngine::WindowRenderer *GraphicsEngine::
get_window_renderer(const string &name) {
get_window_renderer(const string &name, int pipeline_stage) {
nassertr(_lock.debug_is_locked(), NULL);
if (name.empty()) {
@ -1455,8 +1504,8 @@ get_window_renderer(const string &name) {
}
PT(RenderThread) thread = new RenderThread(name, this);
thread->set_pipeline_stage(1);
_pipeline->set_min_stages(2);
thread->set_min_pipeline_stage(pipeline_stage);
_pipeline->set_min_stages(pipeline_stage + 1);
thread->start(TP_normal, true, true);
_threads[name] = thread;
@ -1592,8 +1641,9 @@ do_frame(GraphicsEngine *engine) {
do_callbacks(CB_pre_frame);
engine->cull_bin_draw(_cull);
engine->cull_to_bins(_cull);
engine->cull_and_draw_together(_cdraw);
engine->draw_bins(_draw);
engine->process_events(_window);
// If any GSG's on the list have no more outstanding pointers, clean

View File

@ -145,8 +145,10 @@ private:
void cull_and_draw_together(const Windows &wlist);
void cull_and_draw_together(GraphicsOutput *win, DisplayRegion *dr);
void cull_bin_draw(const Windows &wlist);
void cull_bin_draw(GraphicsOutput *win, DisplayRegion *dr);
void cull_to_bins(const Windows &wlist);
void cull_to_bins(GraphicsOutput *win, DisplayRegion *dr);
void draw_bins(const Windows &wlist);
void draw_bins(GraphicsOutput *win, DisplayRegion *dr);
void make_contexts(const Windows &wlist);
void process_events(const Windows &wlist);
@ -222,7 +224,7 @@ private:
ThreadState _thread_state;
};
WindowRenderer *get_window_renderer(const string &name);
WindowRenderer *get_window_renderer(const string &name, int pipeline_stage);
Pipeline *_pipeline;
Windows _windows;

View File

@ -814,7 +814,7 @@ static ShowBuffersCubeMapRegions cube_map_regions[6] = {
////////////////////////////////////////////////////////////////////
GraphicsOutput *GraphicsOutput::
make_cube_map(const string &name, int size, NodePath &camera_rig,
DrawMask camera_mask, bool to_ram) {
DrawMask camera_mask, bool to_ram) {
if (!to_ram) {
// Check the limits imposed by the GSG. (However, if we're
// rendering the texture to RAM only, these limits may be

View File

@ -872,7 +872,6 @@ begin_draw_primitives(const Geom *geom, const GeomMunger *munger,
const GeomVertexData *data) {
_munger = munger;
_vertex_data = data;
nassertr(geom->check_valid(data), false);
return _vertex_data->has_vertex();
}

View File

@ -25,7 +25,9 @@
INLINE GraphicsThreadingModel::
GraphicsThreadingModel(const GraphicsThreadingModel &copy) :
_cull_name(copy._cull_name),
_cull_stage(copy._cull_stage),
_draw_name(copy._draw_name),
_draw_stage(copy._draw_stage),
_cull_sorting(copy._cull_sorting)
{
}
@ -38,7 +40,9 @@ GraphicsThreadingModel(const GraphicsThreadingModel &copy) :
INLINE void GraphicsThreadingModel::
operator = (const GraphicsThreadingModel &copy) {
_cull_name = copy._cull_name;
_cull_stage = copy._cull_stage;
_draw_name = copy._draw_name;
_draw_stage = copy._draw_stage;
_cull_sorting = copy._cull_sorting;
}
@ -53,6 +57,19 @@ get_cull_name() const {
return _cull_name;
}
////////////////////////////////////////////////////////////////////
// Function: GraphicsThreadingModel::get_cull_stage
// Access: Published
// Description: Returns the pipeline stage from which the cull thread
// should access data. This will be 0 if the cull is
// run in the same thread as app, or 1 if it is its own
// thread.
////////////////////////////////////////////////////////////////////
INLINE int GraphicsThreadingModel::
get_cull_stage() const {
return _cull_stage;
}
////////////////////////////////////////////////////////////////////
// Function: GraphicsThreadingModel::get_draw_name
// Access: Published
@ -65,6 +82,20 @@ get_draw_name() const {
return _draw_name;
}
////////////////////////////////////////////////////////////////////
// Function: GraphicsThreadingModel::get_draw_stage
// Access: Published
// Description: Returns the pipeline stage from which the draw thread
// should access data. This will be the same value as
// get_cull_stage() if cull and draw are run in the same
// thread, or one more than that value if draw should be
// in its own thread.
////////////////////////////////////////////////////////////////////
INLINE int GraphicsThreadingModel::
get_draw_stage() const {
return _draw_stage;
}
////////////////////////////////////////////////////////////////////
// Function: GraphicsThreadingModel::get_cull_sorting
// Access: Published

View File

@ -64,9 +64,20 @@ GraphicsThreadingModel(const string &model) {
_cull_name = model.substr(start, slash - start);
_draw_name = model.substr(slash + 1);
}
if (_cull_name.empty()) {
_cull_stage = 0;
} else {
_cull_stage = 1;
}
if (!_cull_sorting || _draw_name.empty()) {
_draw_name = _cull_name;
}
if (_draw_name == _cull_name) {
_draw_stage = _cull_stage;
} else {
_draw_stage = _cull_stage + 1;
}
}

View File

@ -34,7 +34,9 @@ PUBLISHED:
string get_model() const;
INLINE const string &get_cull_name() const;
INLINE int get_cull_stage() const;
INLINE const string &get_draw_name() const;
INLINE int get_draw_stage() const;
INLINE bool get_cull_sorting() const;
INLINE bool is_single_threaded() const;
@ -43,7 +45,9 @@ PUBLISHED:
private:
string _cull_name;
int _cull_stage;
string _draw_name;
int _draw_stage;
bool _cull_sorting;
};

View File

@ -81,7 +81,6 @@
#include "collisionPolygon.h"
#include "parametricCurve.h"
#include "nurbsCurve.h"
#include "classicNurbsCurve.h"
#include "nurbsCurveInterface.h"
#include "nurbsCurveEvaluator.h"
#include "nurbsSurfaceEvaluator.h"
@ -665,10 +664,10 @@ make_nurbs_curve(EggNurbsCurve *egg_curve, PandaNode *parent,
////////////////////////////////////////////////////////////////////
// Function: EggLoader::make_old_nurbs_curve
// Access: Private
// Description: This deprecated interface creates a NurbsCurve (or a
// ClassicNurbsCurve) object for the EggNurbsCurve
// entry. It will eventually be removed in favor of the
// above, which creates a RopeNode.
// Description: This deprecated interface creates a NurbsCurve object
// for the EggNurbsCurve entry. It will eventually be
// removed in favor of the above, which creates a
// RopeNode.
////////////////////////////////////////////////////////////////////
void EggLoader::
make_old_nurbs_curve(EggNurbsCurve *egg_curve, PandaNode *parent,
@ -677,12 +676,7 @@ make_old_nurbs_curve(EggNurbsCurve *egg_curve, PandaNode *parent,
assert(!parent->is_geom_node());
PT(ParametricCurve) curve;
if (egg_load_classic_nurbs_curves) {
curve = new ClassicNurbsCurve;
} else {
curve = new NurbsCurve;
}
curve = new NurbsCurve;
NurbsCurveInterface *nurbs = curve->get_nurbs_interface();
nassertv(nurbs != (NurbsCurveInterface *)NULL);

View File

@ -17,8 +17,11 @@
bigEndian.h buffer.I buffer.h \
checksumHashGenerator.I checksumHashGenerator.h circBuffer.I \
circBuffer.h clockObject.I clockObject.h \
conditionVarDummyImpl.h conditionVarDummyImpl.I conditionVar.h \
conditionVar.I conditionVarImpl.h \
conditionVar.h conditionVar.I \
conditionVarDebug.h conditionVarDebug.I \
conditionVarDirect.h conditionVarDirect.I \
conditionVarDummyImpl.h conditionVarDummyImpl.I \
conditionVarImpl.h \
conditionVarNsprImpl.h conditionVarNsprImpl.I \
conditionVarPosixImpl.h conditionVarPosixImpl.I \
conditionVarWin32Impl.h conditionVarWin32Impl.I \
@ -40,8 +43,11 @@
memoryUsagePointerCounts.I memoryUsagePointerCounts.h \
memoryUsagePointers.I memoryUsagePointers.h \
multifile.I multifile.h \
mutexDummyImpl.h mutexDummyImpl.I pmutex.h mutexHolder.h \
mutexHolder.I pmutex.I mutexImpl.h \
mutexDebug.h mutexDebug.I \
mutexDirect.h mutexDirect.I \
mutexDummyImpl.h mutexDummyImpl.I \
mutexHolder.h mutexHolder.I \
mutexImpl.h \
mutexNsprImpl.h mutexNsprImpl.I \
mutexPosixImpl.h mutexPosixImpl.I \
mutexWin32Impl.h mutexWin32Impl.I \
@ -51,6 +57,7 @@
ordered_vector.h ordered_vector.I ordered_vector.T \
password_hash.h \
patchfile.I patchfile.h \
pmutex.h pmutex.I \
pointerTo.I pointerTo.h \
pointerToArray.I pointerToArray.h \
pointerToBase.I pointerToBase.h \
@ -60,6 +67,7 @@
ramfile.I ramfile.h \
referenceCount.I referenceCount.h \
reMutex.I reMutex.h \
reMutexDirect.h reMutexDirect.I \
reMutexHolder.I reMutexHolder.h \
reversedNumericData.I reversedNumericData.h \
selectThreadImpl.h \
@ -96,7 +104,10 @@
atomicAdjustPosixImpl.cxx \
atomicAdjustWin32Impl.cxx \
buffer.cxx checksumHashGenerator.cxx clockObject.cxx \
conditionVar.cxx conditionVarDummyImpl.cxx \
conditionVar.cxx \
conditionVarDebug.cxx \
conditionVarDirect.cxx \
conditionVarDummyImpl.cxx \
conditionVarNsprImpl.cxx \
conditionVarPosixImpl.cxx \
conditionVarWin32Impl.cxx \
@ -110,10 +121,14 @@
mainThread.cxx \
memoryInfo.cxx memoryUsage.cxx memoryUsagePointerCounts.cxx \
memoryUsagePointers.cxx multifile.cxx \
pmutex.cxx mutexHolder.cxx mutexDummyImpl.cxx \
mutexDebug.cxx \
mutexDirect.cxx \
mutexHolder.cxx \
mutexDummyImpl.cxx \
mutexNsprImpl.cxx \
mutexPosixImpl.cxx \
mutexWin32Impl.cxx \
pmutex.cxx \
namable.cxx \
nativeNumericData.cxx \
ordered_vector.cxx \
@ -128,6 +143,7 @@
ramfile.cxx \
referenceCount.cxx \
reMutex.cxx \
reMutexDirect.cxx \
reMutexHolder.cxx \
reversedNumericData.cxx \
streamReader.cxx streamWriter.cxx \
@ -162,8 +178,11 @@
bigEndian.h buffer.I buffer.h checksumHashGenerator.I \
checksumHashGenerator.h circBuffer.I circBuffer.h clockObject.I \
clockObject.h \
conditionVarDummyImpl.h conditionVarDummyImpl.I conditionVar.h \
conditionVar.I conditionVarImpl.h \
conditionVar.h conditionVar.I \
conditionVarDebug.h conditionVarDebug.I \
conditionVarDirect.h conditionVarDirect.I \
conditionVarDummyImpl.h conditionVarDummyImpl.I \
conditionVarImpl.h \
conditionVarNsprImpl.h conditionVarNsprImpl.I \
conditionVarPosixImpl.h conditionVarPosixImpl.I \
conditionVarWin32Impl.h conditionVarWin32Impl.I \
@ -183,8 +202,11 @@
memoryUsage.h memoryUsagePointerCounts.I \
memoryUsagePointerCounts.h memoryUsagePointers.I \
memoryUsagePointers.h multifile.I multifile.h \
mutexDummyImpl.h mutexDummyImpl.I pmutex.h mutexHolder.h \
mutexHolder.I pmutex.I mutexImpl.h \
mutexDebug.h mutexDebug.I \
mutexDirect.h mutexDirect.I \
mutexDummyImpl.h mutexDummyImpl.I \
mutexHolder.h mutexHolder.I \
mutexImpl.h \
mutexNsprImpl.h mutexNsprImpl.I \
mutexPosixImpl.h mutexPosixImpl.I \
mutexWin32Impl.h mutexWin32Impl.I \
@ -193,6 +215,7 @@
ordered_vector.h ordered_vector.I ordered_vector.T \
password_hash.h \
patchfile.I patchfile.h \
pmutex.h pmutex.I \
pointerTo.I pointerTo.h \
pointerToArray.I pointerToArray.h \
pointerToBase.I pointerToBase.h \
@ -202,6 +225,7 @@
ramfile.I ramfile.h \
referenceCount.I referenceCount.h \
reMutex.I reMutex.h \
reMutexDirect.h reMutexDirect.I \
reMutexHolder.I reMutexHolder.h \
reversedNumericData.I reversedNumericData.h \
selectThreadImpl.h \

View File

@ -49,3 +49,17 @@ INLINE PN_int32 AtomicAdjust::
set(PN_int32 &var, PN_int32 new_value) {
return AtomicAdjustImpl::set(var, new_value);
}
////////////////////////////////////////////////////////////////////
// Function: AtomicAdjust::get
// Access: Public, Static
// Description: Atomically retrieves the snapshot value of the
// indicated variable. This is the only guaranteed safe
// way to retrieve the value that other threads might be
// asynchronously setting, incrementing, or decrementing
// (via other AtomicAjust methods).
////////////////////////////////////////////////////////////////////
INLINE PN_int32 AtomicAdjust::
get(const PN_int32 &var) {
return AtomicAdjustImpl::get(var);
}

View File

@ -35,6 +35,7 @@ public:
INLINE static PN_int32 inc(PN_int32 &var);
INLINE static PN_int32 dec(PN_int32 &var);
INLINE static PN_int32 set(PN_int32 &var, PN_int32 new_value);
INLINE static PN_int32 get(const PN_int32 &var);
};
#include "atomicAdjust.I"

View File

@ -51,3 +51,17 @@ set(PN_int32 &var, PN_int32 new_value) {
var = new_value;
return orig_value;
}
////////////////////////////////////////////////////////////////////
// Function: AtomicAdjustDummyImpl::get
// Access: Public, Static
// Description: Atomically retrieves the snapshot value of the
// indicated variable. This is the only guaranteed safe
// way to retrieve the value that other threads might be
// asynchronously setting, incrementing, or decrementing
// (via other AtomicAjust methods).
////////////////////////////////////////////////////////////////////
INLINE PN_int32 AtomicAdjustDummyImpl::
get(const PN_int32 &var) {
return var;
}

View File

@ -38,6 +38,7 @@ public:
INLINE static PN_int32 inc(PN_int32 &var);
INLINE static PN_int32 dec(PN_int32 &var);
INLINE static PN_int32 set(PN_int32 &var, PN_int32 new_value);
INLINE static PN_int32 get(const PN_int32 &var);
};
#include "atomicAdjustDummyImpl.I"

View File

@ -49,3 +49,17 @@ INLINE PN_int32 AtomicAdjustNsprImpl::
set(PN_int32 &var, PN_int32 new_value) {
return PR_AtomicSet(&var, new_value);
}
////////////////////////////////////////////////////////////////////
// Function: AtomicAdjustNsprImpl::get
// Access: Public, Static
// Description: Atomically retrieves the snapshot value of the
// indicated variable. This is the only guaranteed safe
// way to retrieve the value that other threads might be
// asynchronously setting, incrementing, or decrementing
// (via other AtomicAjust methods).
////////////////////////////////////////////////////////////////////
INLINE PN_int32 AtomicAdjustNsprImpl::
get(const PN_int32 &var) {
return var;
}

View File

@ -38,6 +38,7 @@ public:
INLINE static PN_int32 inc(PN_int32 &var);
INLINE static PN_int32 dec(PN_int32 &var);
INLINE static PN_int32 set(PN_int32 &var, PN_int32 new_value);
INLINE static PN_int32 get(const PN_int32 &var);
};
#include "atomicAdjustNsprImpl.I"

View File

@ -59,3 +59,20 @@ set(PN_int32 &var, PN_int32 new_value) {
pthread_mutex_unlock(&_mutex);
return orig_value;
}
////////////////////////////////////////////////////////////////////
// Function: AtomicAdjustPosixImpl::get
// Access: Public, Static
// Description: Atomically retrieves the snapshot value of the
// indicated variable. This is the only guaranteed safe
// way to retrieve the value that other threads might be
// asynchronously setting, incrementing, or decrementing
// (via other AtomicAjust methods).
////////////////////////////////////////////////////////////////////
INLINE PN_int32 AtomicAdjustPosixImpl::
get(const PN_int32 &var) {
pthread_mutex_lock(&_mutex);
PN_int32 orig_value = var;
pthread_mutex_unlock(&_mutex);
return orig_value;
}

View File

@ -38,6 +38,7 @@ public:
INLINE static PN_int32 inc(PN_int32 &var);
INLINE static PN_int32 dec(PN_int32 &var);
INLINE static PN_int32 set(PN_int32 &var, PN_int32 new_value);
INLINE static PN_int32 get(const PN_int32 &var);
private:
static pthread_mutex_t _mutex;

View File

@ -49,3 +49,17 @@ INLINE PN_int32 AtomicAdjustWin32Impl::
set(PN_int32 &var, PN_int32 new_value) {
return InterlockedExchange((LONG *)&var, new_value);
}
////////////////////////////////////////////////////////////////////
// Function: AtomicAdjustWin32Impl::get
// Access: Public, Static
// Description: Atomically retrieves the snapshot value of the
// indicated variable. This is the only guaranteed safe
// way to retrieve the value that other threads might be
// asynchronously setting, incrementing, or decrementing
// (via other AtomicAjust methods).
////////////////////////////////////////////////////////////////////
INLINE PN_int32 AtomicAdjustWin32Impl::
get(const PN_int32 &var) {
return var;
}

View File

@ -39,6 +39,7 @@ public:
INLINE static PN_int32 inc(PN_int32 &var);
INLINE static PN_int32 dec(PN_int32 &var);
INLINE static PN_int32 set(PN_int32 &var, PN_int32 new_value);
INLINE static PN_int32 get(const PN_int32 &var);
};
#include "atomicAdjustWin32Impl.I"

View File

@ -29,8 +29,11 @@
////////////////////////////////////////////////////////////////////
INLINE ConditionVar::
ConditionVar(Mutex &mutex) :
_mutex(mutex),
_impl(mutex._impl)
#ifdef DEBUG_THREADS
ConditionVarDebug(mutex)
#else
ConditionVarDirect(mutex)
#endif // DEBUG_THREADS
{
}
@ -50,8 +53,11 @@ INLINE ConditionVar::
////////////////////////////////////////////////////////////////////
INLINE ConditionVar::
ConditionVar(const ConditionVar &copy) :
_mutex(copy._mutex),
_impl(_mutex._impl)
#ifdef DEBUG_THREADS
ConditionVarDebug(copy.get_mutex())
#else
ConditionVarDirect(copy.get_mutex())
#endif // DEBUG_THREADS
{
nassertv(false);
}
@ -73,67 +79,10 @@ operator = (const ConditionVar &copy) {
// variable.
////////////////////////////////////////////////////////////////////
INLINE Mutex &ConditionVar::
get_mutex() {
return _mutex;
}
#ifndef CHECK_REENTRANT_MUTEX
////////////////////////////////////////////////////////////////////
// Function: ConditionVar::wait
// Access: Public
// Description: Waits on the condition. The caller must already be
// holding the lock associated with the condition
// variable before calling this function.
//
// wait() will release the lock, then go to sleep until
// some other thread calls signal() on this condition
// variable. At that time at least one thread waiting
// on the same ConditionVar will grab the lock again,
// and then return from wait().
//
// It is possible that wait() will return even if no one
// has called signal(). It is the responsibility of the
// calling process to verify the condition on return
// from wait, and possibly loop back to wait again if
// necessary.
//
// Note the semantics of a condition variable: the mutex
// must be held before wait() is called, and it will
// still be held when wait() returns. However, it will
// be temporarily released during the wait() call
// itself.
//
// This is defined as an inline method only when
// CHECK_REENTRANT_MUTEX is not defined. If
// CHECK_REENTRANT_MUTEX is defined, it is an
// out-of-line method, just to avoid circular
// dependencies on #include files.
////////////////////////////////////////////////////////////////////
INLINE void ConditionVar::
wait() {
nassertv(_mutex.debug_is_locked());
_impl.wait();
}
#endif // CHECK_REENTRANT_MUTEX
////////////////////////////////////////////////////////////////////
// Function: ConditionVar::signal
// Access: Public
// Description: Informs one of the other threads who are currently
// blocked on wait() that the relevant condition has
// changed. If multiple threads are currently waiting,
// at least one of them will be woken up, although there
// is no way to predict which one.
//
// The caller must be holding the mutex associated with
// the condition variable before making this call, which
// will not release the mutex.
//
// If no threads are waiting, this is a no-op: the
// signal is lost.
////////////////////////////////////////////////////////////////////
INLINE void ConditionVar::
signal() {
nassertv(_mutex.debug_is_locked());
_impl.signal();
get_mutex() const {
#ifdef DEBUG_THREADS
return (Mutex &)ConditionVarDebug::get_mutex();
#else
return (Mutex &)ConditionVarDirect::get_mutex();
#endif // DEBUG_THREADS
}

View File

@ -17,45 +17,3 @@
////////////////////////////////////////////////////////////////////
#include "conditionVar.h"
#include "thread.h"
#ifdef CHECK_REENTRANT_MUTEX
////////////////////////////////////////////////////////////////////
// Function: ConditionVar::wait
// Access: Public
// Description: Waits on the condition. The caller must already be
// holding the lock associated with the condition
// variable before calling this function.
//
// wait() will release the lock, then go to sleep until
// some other thread calls signal() on this condition
// variable. At that time at least one thread waiting
// on the same ConditionVar will grab the lock again,
// and then return from wait().
//
// It is possible that wait() will return even if no one
// has called signal(). It is the responsibility of the
// calling process to verify the condition on return
// from wait, and possibly loop back to wait again if
// necessary.
//
// Note the semantics of a condition variable: the mutex
// must be held before wait() is called, and it will
// still be held when wait() returns. However, it will
// be temporarily released during the wait() call
// itself.
//
// This is defined as an inline method only when
// CHECK_REENTRANT_MUTEX is not defined. If
// CHECK_REENTRANT_MUTEX is defined, it is an
// out-of-line method, just to avoid circular
// dependencies on #include files.
////////////////////////////////////////////////////////////////////
void ConditionVar::
wait() {
nassertv(_mutex.debug_is_locked());
_impl.wait();
_mutex._locking_thread = Thread::get_current_thread();
}
#endif // CHECK_REENTRANT_MUTEX

View File

@ -20,9 +20,8 @@
#define CONDITIONVAR_H
#include "pandabase.h"
#include "pmutex.h"
#include "conditionVarImpl.h"
#include "notify.h"
#include "conditionVarDebug.h"
#include "conditionVarDirect.h"
////////////////////////////////////////////////////////////////////
// Class : ConditionVar
@ -35,8 +34,17 @@
// A condition variable is associated with a single
// mutex, and several condition variables may share the
// same mutex.
//
// This class inherits its implementation either from
// ConditionVarDebug or ConditionVarDirect, depending on
// the definition of DEBUG_THREADS.
////////////////////////////////////////////////////////////////////
class EXPCL_PANDAEXPRESS ConditionVar {
#ifdef DEBUG_THREADS
class EXPCL_PANDAEXPRESS ConditionVar : public ConditionVarDebug
#else
class EXPCL_PANDAEXPRESS ConditionVar : public ConditionVarDirect
#endif // DEBUG_THREADS
{
public:
INLINE ConditionVar(Mutex &mutex);
INLINE ~ConditionVar();
@ -45,18 +53,7 @@ private:
INLINE void operator = (const ConditionVar &copy);
public:
INLINE Mutex &get_mutex();
#ifdef CHECK_REENTRANT_MUTEX
void wait();
#else // CHECK_REENTRANT_MUTEX
INLINE void wait();
#endif // CHECK_REENTRANT_MUTEX
INLINE void signal();
private:
Mutex &_mutex;
ConditionVarImpl _impl;
INLINE Mutex &get_mutex() const;
};
#include "conditionVar.I"

View File

@ -0,0 +1,52 @@
// Filename: conditionVarDebug.I
// Created by: drose (13Feb06)
//
////////////////////////////////////////////////////////////////////
//
// 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: ConditionVarDebug::Copy Constructor
// Access: Private
// Description: Do not attempt to copy condition variables.
////////////////////////////////////////////////////////////////////
INLINE ConditionVarDebug::
ConditionVarDebug(const ConditionVarDebug &copy) :
_mutex(copy._mutex),
_impl(_mutex._global_mutex)
{
nassertv(false);
}
////////////////////////////////////////////////////////////////////
// Function: ConditionVarDebug::Copy Assignment Operator
// Access: Private
// Description: Do not attempt to copy condition variables.
////////////////////////////////////////////////////////////////////
INLINE void ConditionVarDebug::
operator = (const ConditionVarDebug &copy) {
nassertv(false);
}
////////////////////////////////////////////////////////////////////
// Function: ConditionVarDebug::get_mutex
// Access: Public
// Description: Returns the mutex associated with this condition
// variable.
////////////////////////////////////////////////////////////////////
INLINE MutexDebug &ConditionVarDebug::
get_mutex() const {
return _mutex;
}

View File

@ -0,0 +1,139 @@
// Filename: conditionVarDebug.cxx
// Created by: drose (13Feb06)
//
////////////////////////////////////////////////////////////////////
//
// 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 "conditionVarDebug.h"
#include "thread.h"
#ifdef DEBUG_THREADS
////////////////////////////////////////////////////////////////////
// Function: ConditionVarDebug::Constructor
// Access: Public
// Description: You must pass in a Mutex to the condition variable
// constructor. This mutex may be shared by other
// condition variables, if desired. It is the caller's
// responsibility to ensure the Mutex object does not
// destruct during the lifetime of the condition
// variable.
////////////////////////////////////////////////////////////////////
ConditionVarDebug::
ConditionVarDebug(MutexDebug &mutex) :
_mutex(mutex),
_impl(mutex._global_mutex)
{
nassertv(!_mutex._allow_recursion);
}
////////////////////////////////////////////////////////////////////
// Function: ConditionVarDebug::Destructor
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
ConditionVarDebug::
~ConditionVarDebug() {
}
////////////////////////////////////////////////////////////////////
// Function: ConditionVarDebug::wait
// Access: Public
// Description: Waits on the condition. The caller must already be
// holding the lock associated with the condition
// variable before calling this function.
//
// wait() will release the lock, then go to sleep until
// some other thread calls signal() on this condition
// variable. At that time at least one thread waiting
// on the same ConditionVarDebug will grab the lock again,
// and then return from wait().
//
// It is possible that wait() will return even if no one
// has called signal(). It is the responsibility of the
// calling process to verify the condition on return
// from wait, and possibly loop back to wait again if
// necessary.
//
// Note the semantics of a condition variable: the mutex
// must be held before wait() is called, and it will
// still be held when wait() returns. However, it will
// be temporarily released during the wait() call
// itself.
////////////////////////////////////////////////////////////////////
void ConditionVarDebug::
wait() {
_mutex._global_mutex.lock();
if (!_mutex.do_debug_is_locked()) {
ostringstream ostr;
ostr << *Thread::get_current_thread() << " attempted to wait on "
<< *this << " without holding " << _mutex;
nassert_raise(ostr.str());
_mutex._global_mutex.release();
return;
}
_mutex.do_release();
_impl.wait();
_mutex.do_lock();
_mutex._global_mutex.release();
}
////////////////////////////////////////////////////////////////////
// Function: ConditionVarDebug::signal
// Access: Public
// Description: Informs one of the other threads who are currently
// blocked on wait() that the relevant condition has
// changed. If multiple threads are currently waiting,
// at least one of them will be woken up, although there
// is no way to predict which one.
//
// The caller must be holding the mutex associated with
// the condition variable before making this call, which
// will not release the mutex.
//
// If no threads are waiting, this is a no-op: the
// signal is lost.
////////////////////////////////////////////////////////////////////
void ConditionVarDebug::
signal() {
_mutex._global_mutex.lock();
if (!_mutex.do_debug_is_locked()) {
ostringstream ostr;
ostr << *Thread::get_current_thread() << " attempted to signal "
<< *this << " without holding " << _mutex;
nassert_raise(ostr.str());
_mutex._global_mutex.release();
return;
}
_impl.signal();
_mutex._global_mutex.release();
}
////////////////////////////////////////////////////////////////////
// Function: ConditionVarDebug::output
// Access: Public, Virtual
// Description: This method is declared virtual in ConditionVarDebug,
// but non-virtual in ConditionVarDirect.
////////////////////////////////////////////////////////////////////
void ConditionVarDebug::
output(ostream &out) const {
out << "ConditionVar " << (void *)this;
}
#endif // DEBUG_THREADS

View File

@ -0,0 +1,70 @@
// Filename: conditionVarDebug.h
// Created by: drose (13Feb06)
//
////////////////////////////////////////////////////////////////////
//
// 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 CONDITIONVARDEBUG_H
#define CONDITIONVARDEBUG_H
#include "pandabase.h"
#include "pmutex.h"
#include "conditionVarImpl.h"
#ifdef DEBUG_THREADS
////////////////////////////////////////////////////////////////////
// Class : ConditionVarDebug
// Description : A condition variable, usually used to communicate
// information about changing state to a thread that is
// waiting for something to happen. A condition
// variable can be used to "wake up" a thread when some
// arbitrary condition has changed.
//
// A condition variable is associated with a single
// mutex, and several condition variables may share the
// same mutex.
////////////////////////////////////////////////////////////////////
class EXPCL_PANDAEXPRESS ConditionVarDebug {
public:
ConditionVarDebug(MutexDebug &mutex);
virtual ~ConditionVarDebug();
private:
INLINE ConditionVarDebug(const ConditionVarDebug &copy);
INLINE void operator = (const ConditionVarDebug &copy);
public:
INLINE MutexDebug &get_mutex() const;
void wait();
void signal();
virtual void output(ostream &out) const;
private:
MutexDebug &_mutex;
ConditionVarImpl _impl;
};
INLINE ostream &
operator << (ostream &out, const ConditionVarDebug &cv) {
cv.output(out);
return out;
}
#include "conditionVarDebug.I"
#endif // DEBUG_THREADS
#endif

View File

@ -0,0 +1,129 @@
// Filename: conditionVarDirect.I
// Created by: drose (13Feb06)
//
////////////////////////////////////////////////////////////////////
//
// 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: ConditionVarDirect::Constructor
// Access: Public
// Description: You must pass in a Mutex to the condition variable
// constructor. This mutex may be shared by other
// condition variables, if desired. It is the caller's
// responsibility to ensure the Mutex object does not
// destruct during the lifetime of the condition
// variable.
////////////////////////////////////////////////////////////////////
INLINE ConditionVarDirect::
ConditionVarDirect(MutexDirect &mutex) :
_mutex(mutex),
_impl(mutex._impl)
{
}
////////////////////////////////////////////////////////////////////
// Function: ConditionVarDirect::Destructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
INLINE ConditionVarDirect::
~ConditionVarDirect() {
}
////////////////////////////////////////////////////////////////////
// Function: ConditionVarDirect::Copy Constructor
// Access: Private
// Description: Do not attempt to copy condition variables.
////////////////////////////////////////////////////////////////////
INLINE ConditionVarDirect::
ConditionVarDirect(const ConditionVarDirect &copy) :
_mutex(copy._mutex),
_impl(_mutex._impl)
{
nassertv(false);
}
////////////////////////////////////////////////////////////////////
// Function: ConditionVarDirect::Copy Assignment Operator
// Access: Private
// Description: Do not attempt to copy condition variables.
////////////////////////////////////////////////////////////////////
INLINE void ConditionVarDirect::
operator = (const ConditionVarDirect &copy) {
nassertv(false);
}
////////////////////////////////////////////////////////////////////
// Function: ConditionVarDirect::get_mutex
// Access: Public
// Description: Returns the mutex associated with this condition
// variable.
////////////////////////////////////////////////////////////////////
INLINE MutexDirect &ConditionVarDirect::
get_mutex() const {
return _mutex;
}
////////////////////////////////////////////////////////////////////
// Function: ConditionVarDirect::wait
// Access: Public
// Description: Waits on the condition. The caller must already be
// holding the lock associated with the condition
// variable before calling this function.
//
// wait() will release the lock, then go to sleep until
// some other thread calls signal() on this condition
// variable. At that time at least one thread waiting
// on the same ConditionVarDirect will grab the lock again,
// and then return from wait().
//
// It is possible that wait() will return even if no one
// has called signal(). It is the responsibility of the
// calling process to verify the condition on return
// from wait, and possibly loop back to wait again if
// necessary.
//
// Note the semantics of a condition variable: the mutex
// must be held before wait() is called, and it will
// still be held when wait() returns. However, it will
// be temporarily released during the wait() call
// itself.
////////////////////////////////////////////////////////////////////
INLINE void ConditionVarDirect::
wait() {
_impl.wait();
}
////////////////////////////////////////////////////////////////////
// Function: ConditionVarDirect::signal
// Access: Public
// Description: Informs one of the other threads who are currently
// blocked on wait() that the relevant condition has
// changed. If multiple threads are currently waiting,
// at least one of them will be woken up, although there
// is no way to predict which one.
//
// The caller must be holding the mutex associated with
// the condition variable before making this call, which
// will not release the mutex.
//
// If no threads are waiting, this is a no-op: the
// signal is lost.
////////////////////////////////////////////////////////////////////
INLINE void ConditionVarDirect::
signal() {
_impl.signal();
}

View File

@ -0,0 +1,34 @@
// Filename: conditionVarDirect.cxx
// Created by: drose (13Feb06)
//
////////////////////////////////////////////////////////////////////
//
// 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 "conditionVarDirect.h"
#ifndef DEBUG_THREADS
////////////////////////////////////////////////////////////////////
// Function: ConditionVarDirect::output
// Access: Public
// Description: This method is declared virtual in ConditionVarDebug,
// but non-virtual in ConditionVarDirect.
////////////////////////////////////////////////////////////////////
void ConditionVarDirect::
output(ostream &out) const {
out << "ConditionVar " << (void *)this;
}
#endif // !DEBUG_THREADS

View File

@ -0,0 +1,70 @@
// Filename: conditionVarDirect.h
// Created by: drose (13Feb06)
//
////////////////////////////////////////////////////////////////////
//
// 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 CONDITIONVARDIRECT_H
#define CONDITIONVARDIRECT_H
#include "pandabase.h"
#include "mutexDirect.h"
#include "conditionVarImpl.h"
#ifndef DEBUG_THREADS
////////////////////////////////////////////////////////////////////
// Class : ConditionVarDirect
// Description : A condition variable, usually used to communicate
// information about changing state to a thread that is
// waiting for something to happen. A condition
// variable can be used to "wake up" a thread when some
// arbitrary condition has changed.
//
// A condition variable is associated with a single
// mutex, and several condition variables may share the
// same mutex.
////////////////////////////////////////////////////////////////////
class EXPCL_PANDAEXPRESS ConditionVarDirect {
public:
INLINE ConditionVarDirect(MutexDirect &mutex);
INLINE ~ConditionVarDirect();
private:
INLINE ConditionVarDirect(const ConditionVarDirect &copy);
INLINE void operator = (const ConditionVarDirect &copy);
public:
INLINE MutexDirect &get_mutex() const;
INLINE void wait();
INLINE void signal();
void output(ostream &out) const;
private:
MutexDirect &_mutex;
ConditionVarImpl _impl;
};
INLINE ostream &
operator << (ostream &out, const ConditionVarDirect &cv) {
cv.output(out);
return out;
}
#include "conditionVarDirect.I"
#endif // !DEBUG_THREADS
#endif

View File

@ -185,10 +185,15 @@ init_libexpress() {
init_system_type_handles();
#ifdef HAVE_ZLIB
PandaSystem *ps = PandaSystem::get_global_ptr();
#ifdef HAVE_ZLIB
ps->add_system("zlib");
#endif
#ifdef HAVE_THREADS
ps->add_system("threads");
#endif
}

View File

@ -7,6 +7,8 @@
#include "checksumHashGenerator.cxx"
#include "clockObject.cxx"
#include "conditionVar.cxx"
#include "conditionVarDebug.cxx"
#include "conditionVarDirect.cxx"
#include "conditionVarDummyImpl.cxx"
#include "conditionVarNsprImpl.cxx"
#include "conditionVarPosixImpl.cxx"
@ -29,6 +31,8 @@
#include "memoryUsagePointerCounts.cxx"
#include "memoryUsagePointers.cxx"
#include "multifile.cxx"
#include "mutexDebug.cxx"
#include "mutexDirect.cxx"
#include "mutexHolder.cxx"
#include "mutexDummyImpl.cxx"
#include "mutexNsprImpl.cxx"

109
panda/src/express/mutexDebug.I Executable file
View File

@ -0,0 +1,109 @@
// Filename: mutexDebug.I
// Created by: drose (13Feb06)
//
////////////////////////////////////////////////////////////////////
//
// 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: MutexDebug::Constructor
// Access: Protected
// Description:
////////////////////////////////////////////////////////////////////
INLINE MutexDebug::
MutexDebug(bool allow_recursion) :
_allow_recursion(allow_recursion),
_locking_thread(NULL),
_lock_count(0),
_cvar(_global_mutex)
{
}
////////////////////////////////////////////////////////////////////
// Function: MutexDebug::Copy Constructor
// Access: Private
// Description: Do not attempt to copy mutexes.
////////////////////////////////////////////////////////////////////
INLINE MutexDebug::
MutexDebug(const MutexDebug &copy) : _cvar(_global_mutex) {
nassertv(false);
}
////////////////////////////////////////////////////////////////////
// Function: MutexDebug::Copy Assignment Operator
// Access: Private
// Description: Do not attempt to copy mutexes.
////////////////////////////////////////////////////////////////////
INLINE void MutexDebug::
operator = (const MutexDebug &copy) {
nassertv(false);
}
////////////////////////////////////////////////////////////////////
// Function: MutexDebug::lock
// Access: Public
// Description: Grabs the mutex if it is available. If it is not
// available, blocks until it becomes available, then
// grabs it. In either case, the function does not
// return until the mutex is held; you should then call
// unlock().
//
// This method is considered const so that you can lock
// and unlock const mutexes, mainly to allow thread-safe
// access to otherwise const data.
//
// Also see MutexHolder.
////////////////////////////////////////////////////////////////////
INLINE void MutexDebug::
lock() const {
_global_mutex.lock();
((MutexDebug *)this)->do_lock();
_global_mutex.release();
}
////////////////////////////////////////////////////////////////////
// Function: MutexDebug::release
// Access: Public
// Description: Releases the mutex. It is an error to call this if
// the mutex was not already locked.
//
// This method is considered const so that you can lock
// and unlock const mutexes, mainly to allow thread-safe
// access to otherwise const data.
////////////////////////////////////////////////////////////////////
INLINE void MutexDebug::
release() const {
_global_mutex.lock();
((MutexDebug *)this)->do_release();
_global_mutex.release();
}
////////////////////////////////////////////////////////////////////
// Function: MutexDebug::debug_is_locked
// Access: Public
// Description: Returns true if the current thread has locked the
// Mutex, false otherwise. This method is only intended
// for use in debugging, hence the method name; in the
// MutexDebug case, it always returns true, since
// there's not a reliable way to determine this
// otherwise.
////////////////////////////////////////////////////////////////////
INLINE bool MutexDebug::
debug_is_locked() const {
_global_mutex.lock();
bool is_locked = do_debug_is_locked();
_global_mutex.release();
return is_locked;
}

217
panda/src/express/mutexDebug.cxx Executable file
View File

@ -0,0 +1,217 @@
// Filename: mutexDebug.cxx
// Created by: drose (13Feb06)
//
////////////////////////////////////////////////////////////////////
//
// 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 "mutexDebug.h"
#include "thread.h"
#ifdef DEBUG_THREADS
MutexImpl MutexDebug::_global_mutex;
////////////////////////////////////////////////////////////////////
// Function: MutexDebug::Destructor
// Access: Protected, Virtual
// Description:
////////////////////////////////////////////////////////////////////
MutexDebug::
~MutexDebug() {
nassertv(_locking_thread == NULL && _lock_count == 0);
// Put a distinctive, bogus lock count in upon destruction, so we'll
// be more likely to notice a floating pointer.
_lock_count = -100;
}
////////////////////////////////////////////////////////////////////
// Function: MutexDebug::output
// Access: Public, Virtual
// Description: This method is declared virtual in MutexDebug, but
// non-virtual in MutexDirect.
////////////////////////////////////////////////////////////////////
void MutexDebug::
output(ostream &out) const {
if (_allow_recursion) {
out << "ReMutex " << (void *)this;
} else {
out << "Mutex " << (void *)this;
}
}
////////////////////////////////////////////////////////////////////
// Function: MutexDebug::do_lock
// Access: Private
// Description: The private implementation of lock() assumes that
// _global_mutex is held.
////////////////////////////////////////////////////////////////////
void MutexDebug::
do_lock() {
// If this assertion is triggered, you tried to lock a
// recently-destructed mutex.
nassertv(_lock_count != -100);
Thread *this_thread = Thread::get_current_thread();
if (_locking_thread == (Thread *)NULL) {
// The mutex is not already locked by anyone. Lock it.
_locking_thread = this_thread;
++_lock_count;
nassertv(_lock_count == 1);
} else if (_locking_thread == this_thread) {
// The mutex is already locked by this thread. Increment the lock
// count.
nassertv(_lock_count >= 0);
if (!_allow_recursion && _lock_count == 0) {
ostringstream ostr;
ostr << *_locking_thread << " attempted to re-lock non-reentrant "
<< *this;
nassert_raise(ostr.str());
return;
}
++_lock_count;
} else {
// The mutex is locked by some other thread. Check for deadlock?
MutexDebug *next_mutex = this;
while (next_mutex != NULL) {
if (next_mutex->_locking_thread == this_thread) {
// Whoops, the thread is blocked on me! Deadlock!
report_deadlock(this_thread);
nassert_raise("Deadlock");
_global_mutex.release();
return;
}
Thread *next_thread = next_mutex->_locking_thread;
if (next_thread == NULL) {
// Looks like this mutex isn't actually locked, which means
// the last thread isn't really blocked--it just hasn't woken
// up yet to discover that. In any case, no deadlock.
break;
}
// The last thread is blocked on this "next thread"'s mutex, but
// what mutex is the next thread blocked on?
next_mutex = next_thread->_blocked_on_mutex;
}
// OK, no deadlock detected. Carry on.
this_thread->_blocked_on_mutex = this;
// Go to sleep on the condition variable until it's unlocked.
if (thread_cat.is_spam()) {
thread_cat.spam()
<< *this_thread << " blocking on " << *this << "\n";
}
while (_locking_thread != (Thread *)NULL) {
_cvar.wait();
}
if (thread_cat.is_spam()) {
thread_cat.spam()
<< *this_thread << " awake\n";
}
this_thread->_blocked_on_mutex = NULL;
_locking_thread = this_thread;
++_lock_count;
nassertv(_lock_count == 1);
}
}
////////////////////////////////////////////////////////////////////
// Function: MutexDebug::do_release
// Access: Private
// Description: The private implementation of lock() assumes that
// _global_mutex is held.
////////////////////////////////////////////////////////////////////
void MutexDebug::
do_release() {
// If this assertion is triggered, you tried to release a
// recently-destructed mutex.
nassertv(_lock_count != -100);
Thread *this_thread = Thread::get_current_thread();
if (_locking_thread != this_thread) {
ostringstream ostr;
ostr << *this_thread << " attempted to release "
<< *this << " which it does not own";
nassert_raise(ostr.str());
_global_mutex.release();
return;
}
nassertv(_lock_count > 0);
--_lock_count;
if (_lock_count == 0) {
// That was the last lock held by this thread. Release the lock.
_locking_thread = (Thread *)NULL;
_cvar.signal();
}
}
////////////////////////////////////////////////////////////////////
// Function: MutexDebug::do_debug_is_locked
// Access: Private
// Description: The private implementation of debug_is_locked()
// assumes that _global_mutex is held.
////////////////////////////////////////////////////////////////////
bool MutexDebug::
do_debug_is_locked() const {
return (_locking_thread == Thread::get_current_thread());
}
////////////////////////////////////////////////////////////////////
// Function: MutexDebug::report_deadlock
// Access: Private
// Description: Reports a detected deadlock situation. _global_mutex
// should be already held.
////////////////////////////////////////////////////////////////////
void MutexDebug::
report_deadlock(Thread *this_thread) {
thread_cat.error()
<< "\n\n"
<< "****************************************************************\n"
<< "***** Deadlock detected! *****\n"
<< "****************************************************************\n"
<< "\n";
thread_cat.error()
<< *this_thread << " attempted to lock " << *this
<< " which is held by " << *_locking_thread << "\n";
MutexDebug *next_mutex = this;
Thread *next_thread = next_mutex->_locking_thread;
next_mutex = next_thread->_blocked_on_mutex;
while (next_mutex != NULL) {
thread_cat.error()
<< *next_thread << " is blocked waiting on "
<< *next_mutex << " which is held by "
<< *next_mutex->_locking_thread << "\n";
next_thread = next_mutex->_locking_thread;
next_mutex = next_thread->_blocked_on_mutex;
}
thread_cat.error()
<< "Deadlock!\n";
}
#endif // DEBUG_THREADS

79
panda/src/express/mutexDebug.h Executable file
View File

@ -0,0 +1,79 @@
// Filename: mutexDebug.h
// Created by: drose (13Feb06)
//
////////////////////////////////////////////////////////////////////
//
// 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 MUTEXDEBUG_H
#define MUTEXDEBUG_H
#include "pandabase.h"
#include "mutexImpl.h"
#include "conditionVarImpl.h"
class Thread;
#ifdef DEBUG_THREADS
////////////////////////////////////////////////////////////////////
// Class : MutexDebug
// Description : This class implements a standard mutex the hard way,
// by doing everything by hand. This does allow fancy
// things like deadlock detection, however.
////////////////////////////////////////////////////////////////////
class EXPCL_PANDAEXPRESS MutexDebug {
protected:
INLINE MutexDebug(bool allow_recursion);
virtual ~MutexDebug();
private:
INLINE MutexDebug(const MutexDebug &copy);
INLINE void operator = (const MutexDebug &copy);
public:
INLINE void lock() const;
INLINE void release() const;
INLINE bool debug_is_locked() const;
virtual void output(ostream &out) const;
private:
void do_lock();
void do_release();
bool do_debug_is_locked() const;
void report_deadlock(Thread *this_thread);
private:
bool _allow_recursion;
Thread *_locking_thread;
int _lock_count;
ConditionVarImpl _cvar;
static MutexImpl _global_mutex;
friend class ConditionVarDebug;
};
INLINE ostream &
operator << (ostream &out, const MutexDebug &m) {
m.output(out);
return out;
}
#include "mutexDebug.I"
#endif // DEBUG_THREADS
#endif

106
panda/src/express/mutexDirect.I Executable file
View File

@ -0,0 +1,106 @@
// Filename: mutexDirect.I
// Created by: drose (13Feb06)
//
////////////////////////////////////////////////////////////////////
//
// 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: MutexDirect::Constructor
// Access: Protected
// Description:
////////////////////////////////////////////////////////////////////
INLINE MutexDirect::
MutexDirect() {
}
////////////////////////////////////////////////////////////////////
// Function: MutexDirect::Destructor
// Access: Protected
// Description:
////////////////////////////////////////////////////////////////////
INLINE MutexDirect::
~MutexDirect() {
}
////////////////////////////////////////////////////////////////////
// Function: MutexDirect::Copy Constructor
// Access: Private
// Description: Do not attempt to copy mutexes.
////////////////////////////////////////////////////////////////////
INLINE MutexDirect::
MutexDirect(const MutexDirect &copy) {
nassertv(false);
}
////////////////////////////////////////////////////////////////////
// Function: MutexDirect::Copy Assignment Operator
// Access: Private
// Description: Do not attempt to copy mutexes.
////////////////////////////////////////////////////////////////////
INLINE void MutexDirect::
operator = (const MutexDirect &copy) {
nassertv(false);
}
////////////////////////////////////////////////////////////////////
// Function: MutexDirect::lock
// Access: Public
// Description: Grabs the mutex if it is available. If it is not
// available, blocks until it becomes available, then
// grabs it. In either case, the function does not
// return until the mutex is held; you should then call
// unlock().
//
// This method is considered const so that you can lock
// and unlock const mutexes, mainly to allow thread-safe
// access to otherwise const data.
//
// Also see MutexHolder.
////////////////////////////////////////////////////////////////////
INLINE void MutexDirect::
lock() const {
((MutexDirect *)this)->_impl.lock();
}
////////////////////////////////////////////////////////////////////
// Function: MutexDirect::release
// Access: Public
// Description: Releases the mutex. It is an error to call this if
// the mutex was not already locked.
//
// This method is considered const so that you can lock
// and unlock const mutexes, mainly to allow thread-safe
// access to otherwise const data.
////////////////////////////////////////////////////////////////////
INLINE void MutexDirect::
release() const {
((MutexDirect *)this)->_impl.release();
}
////////////////////////////////////////////////////////////////////
// Function: MutexDirect::debug_is_locked
// Access: Public
// Description: Returns true if the current thread has locked the
// Mutex, false otherwise. This method is only intended
// for use in debugging, hence the method name; in the
// MutexDirect case, it always returns true, since
// there's not a reliable way to determine this
// otherwise.
////////////////////////////////////////////////////////////////////
INLINE bool MutexDirect::
debug_is_locked() const {
return true;
}

View File

@ -0,0 +1,34 @@
// Filename: mutexDirect.cxx
// Created by: drose (13Feb06)
//
////////////////////////////////////////////////////////////////////
//
// 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 "mutexDirect.h"
#ifndef DEBUG_THREADS
////////////////////////////////////////////////////////////////////
// Function: MutexDirect::output
// Access: Public
// Description: This method is declared virtual in MutexDebug, but
// non-virtual in MutexDirect.
////////////////////////////////////////////////////////////////////
void MutexDirect::
output(ostream &out) const {
out << "Mutex " << (void *)this;
}
#endif // !DEBUG_THREADS

66
panda/src/express/mutexDirect.h Executable file
View File

@ -0,0 +1,66 @@
// Filename: mutexDirect.h
// Created by: drose (13Feb06)
//
////////////////////////////////////////////////////////////////////
//
// 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 MUTEXDIRECT_H
#define MUTEXDIRECT_H
#include "pandabase.h"
#include "mutexImpl.h"
class Thread;
#ifndef DEBUG_THREADS
////////////////////////////////////////////////////////////////////
// Class : MutexDirect
// Description : This class implements a standard mutex by making
// direct calls to the underlying implementation layer.
// It doesn't perform any debugging operations.
////////////////////////////////////////////////////////////////////
class EXPCL_PANDAEXPRESS MutexDirect {
protected:
INLINE MutexDirect();
INLINE ~MutexDirect();
private:
INLINE MutexDirect(const MutexDirect &copy);
INLINE void operator = (const MutexDirect &copy);
public:
INLINE void lock() const;
INLINE void release() const;
INLINE bool debug_is_locked() const;
void output(ostream &out) const;
private:
MutexImpl _impl;
friend class ConditionVarDirect;
};
INLINE ostream &
operator << (ostream &out, const MutexDirect &m) {
m.output(out);
return out;
}
#include "mutexDirect.I"
#endif // !DEBUG_THREADS
#endif

View File

@ -24,9 +24,6 @@
////////////////////////////////////////////////////////////////////
INLINE MutexDummyImpl::
MutexDummyImpl() {
#ifdef CHECK_REENTRANT_MUTEX
_lock_count = 0;
#endif
}
////////////////////////////////////////////////////////////////////
@ -36,9 +33,6 @@ MutexDummyImpl() {
////////////////////////////////////////////////////////////////////
INLINE MutexDummyImpl::
~MutexDummyImpl() {
#ifdef CHECK_REENTRANT_MUTEX
nassertv(_lock_count == 0);
#endif
}
////////////////////////////////////////////////////////////////////
@ -48,10 +42,6 @@ INLINE MutexDummyImpl::
////////////////////////////////////////////////////////////////////
INLINE void MutexDummyImpl::
lock() {
#ifdef CHECK_REENTRANT_MUTEX
_lock_count++;
nassertv(_lock_count == 1);
#endif
}
////////////////////////////////////////////////////////////////////
@ -61,8 +51,4 @@ lock() {
////////////////////////////////////////////////////////////////////
INLINE void MutexDummyImpl::
release() {
#ifdef CHECK_REENTRANT_MUTEX
_lock_count--;
nassertv(_lock_count == 0);
#endif
}

View File

@ -30,8 +30,7 @@
// Class : MutexDummyImpl
// Description : A fake mutex implementation for single-threaded
// applications that don't need any synchronization
// control. This does nothing but assert that the same
// process does not try to grab the mutex twice.
// control. This does nothing at all.
////////////////////////////////////////////////////////////////////
class EXPCL_PANDAEXPRESS MutexDummyImpl {
public:
@ -40,11 +39,6 @@ public:
INLINE void lock();
INLINE void release();
private:
#ifdef CHECK_REENTRANT_MUTEX
int _lock_count;
#endif
};
#include "mutexDummyImpl.I"

View File

@ -26,7 +26,8 @@
#include "mutexDummyImpl.h"
typedef MutexDummyImpl MutexImpl;
#undef HAVE_REMUTEXIMPL
typedef MutexDummyImpl ReMutexImpl;
#define HAVE_REMUTEXIMPL 1
#elif defined(THREAD_WIN32_IMPL)
@ -46,7 +47,7 @@ typedef ReMutexPosixImpl ReMutexImpl;
#include "mutexNsprImpl.h"
typedef MutexNsprImpl MutexImpl;
#undef HAVE_REMUTEXIMPL
#undef HAVE_REMUTEXIMPL // NSPR doesn't provide a reentrant mutex.
#endif

View File

@ -48,6 +48,18 @@ lock() {
PR_Lock(_lock);
}
////////////////////////////////////////////////////////////////////
// Function: MutexNsprImpl::try_lock
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
INLINE bool MutexNsprImpl::
try_lock() {
// NSPR doesn't define a try_lock function. Too bad. We just
// report that it would always block (since we don't know).
return false;
}
////////////////////////////////////////////////////////////////////
// Function: MutexNsprImpl::release
// Access: Public

View File

@ -28,6 +28,8 @@
#include <prlock.h>
#undef MUTEX_DEFINES_TRYLOCK
////////////////////////////////////////////////////////////////////
// Class : MutexNsprImpl
// Description : Uses NSPR to implement a mutex.
@ -38,6 +40,7 @@ public:
INLINE ~MutexNsprImpl();
INLINE void lock();
INLINE bool try_lock();
INLINE void release();
private:

View File

@ -54,6 +54,18 @@ lock() {
nassertv(result == 0);
}
////////////////////////////////////////////////////////////////////
// Function: MutexPosixImpl::try_lock
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
INLINE bool MutexPosixImpl::
try_lock() {
int result = pthread_mutex_trylock(&_lock);
nassertr(result == 0 || result == EBUSY, false);
return (result == 0);
}
////////////////////////////////////////////////////////////////////
// Function: MutexPosixImpl::release
// Access: Public
@ -102,6 +114,18 @@ lock() {
nassertv(result == 0);
}
////////////////////////////////////////////////////////////////////
// Function: ReMutexPosixImpl::try_lock
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
INLINE bool ReMutexPosixImpl::
try_lock() {
int result = pthread_mutex_trylock(&_lock);
nassertr(result == 0 || result == EBUSY, false);
return (result == 0);
}
////////////////////////////////////////////////////////////////////
// Function: ReMutexPosixImpl::release
// Access: Public

View File

@ -27,6 +27,9 @@
#include "notify.h"
#include <pthread.h>
#include <errno.h>
#define MUTEX_DEFINES_TRYLOCK 1
////////////////////////////////////////////////////////////////////
// Class : MutexPosixImpl
@ -38,6 +41,7 @@ public:
INLINE ~MutexPosixImpl();
INLINE void lock();
INLINE bool try_lock();
INLINE void release();
private:
@ -55,6 +59,7 @@ public:
INLINE ~ReMutexPosixImpl();
INLINE void lock();
INLINE bool try_lock();
INLINE void release();
private:

View File

@ -47,6 +47,16 @@ lock() {
EnterCriticalSection(&_lock);
}
////////////////////////////////////////////////////////////////////
// Function: MutexWin32Impl::try_lock
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
INLINE bool MutexWin32Impl::
try_lock() {
return TryEnterCriticalSection(&_lock);
}
////////////////////////////////////////////////////////////////////
// Function: MutexWin32Impl::release
// Access: Public

View File

@ -28,6 +28,8 @@
#include <windows.h>
#define MUTEX_DEFINES_TRYLOCK 1
////////////////////////////////////////////////////////////////////
// Class : MutexWin32Impl
// Description : Uses Windows native calls to implement a mutex.
@ -38,6 +40,7 @@ public:
INLINE ~MutexWin32Impl();
INLINE void lock();
INLINE bool try_lock();
INLINE void release();
private:

View File

@ -23,7 +23,12 @@
// Description:
////////////////////////////////////////////////////////////////////
INLINE Mutex::
Mutex() {
#ifdef DEBUG_THREADS
Mutex() : MutexDebug(false)
#else
Mutex()
#endif // DEBUG_THREADS
{
}
////////////////////////////////////////////////////////////////////
@ -41,7 +46,12 @@ INLINE Mutex::
// Description: Do not attempt to copy mutexes.
////////////////////////////////////////////////////////////////////
INLINE Mutex::
Mutex(const Mutex &copy) {
#ifdef DEBUG_THREADS
Mutex(const Mutex &copy) : MutexDebug(false)
#else
Mutex(const Mutex &copy)
#endif // DEBUG_THREADS
{
nassertv(false);
}
@ -54,73 +64,3 @@ INLINE void Mutex::
operator = (const Mutex &copy) {
nassertv(false);
}
////////////////////////////////////////////////////////////////////
// Function: Mutex::lock
// Access: Public
// Description: Grabs the mutex if it is available. If it is not
// available, blocks until it becomes available, then
// grabs it. In either case, the function does not
// return until the mutex is held; you should then call
// unlock().
//
// This method is considered const so that you can lock
// and unlock const mutexes, mainly to allow thread-safe
// access to otherwise const data.
//
// Also see MutexHolder.
////////////////////////////////////////////////////////////////////
INLINE void Mutex::
lock() const {
#ifndef CHECK_REENTRANT_MUTEX
// In the production case, just lock the thing immediately. Don't
// bother with the out-of-line do_lock() method, since we won't be
// performing any checks anyway.
((MutexImpl &)_impl).lock();
#else
// In development mode, call the out-of-line do_lock() method, which
// might perform an additional check or two.
((Mutex *)this)->do_lock();
#endif
}
////////////////////////////////////////////////////////////////////
// Function: Mutex::release
// Access: Public
// Description: Releases the mutex. It is an error to call this if
// the mutex was not already locked.
//
// This method is considered const so that you can lock
// and unlock const mutexes, mainly to allow thread-safe
// access to otherwise const data.
////////////////////////////////////////////////////////////////////
INLINE void Mutex::
release() const {
#ifndef CHECK_REENTRANT_MUTEX
// In the production case, just release the thing immediately.
// Don't bother with the out-of-line do_release() method, since we
// won't be performing any checks anyway.
((MutexImpl &)_impl).release();
#else
// In development mode, call the out-of-line do_release() method,
// which might perform an additional check or two.
((Mutex *)this)->do_release();
#endif
}
#ifndef CHECK_REENTRANT_MUTEX
////////////////////////////////////////////////////////////////////
// Function: Mutex::debug_is_locked
// Access: Public
// Description: Returns true if the current thread has locked the
// Mutex, false otherwise. This method is only
// meaningful if CHECK_REENTRANT_MUTEX is defined;
// otherwise, it always returns true.
////////////////////////////////////////////////////////////////////
INLINE bool Mutex::
debug_is_locked() const {
return true;
}
#endif // CHECK_REENTRANT_MUTEX

View File

@ -19,49 +19,4 @@
#include "pmutex.h"
#include "thread.h"
#ifdef CHECK_REENTRANT_MUTEX
////////////////////////////////////////////////////////////////////
// Function: Mutex::debug_is_locked
// Access: Public
// Description: Returns true if the current thread has locked the
// Mutex, false otherwise. This method is only
// meaningful if CHECK_REENTRANT_MUTEX is defined;
// otherwise, it always returns true.
////////////////////////////////////////////////////////////////////
bool Mutex::
debug_is_locked() const {
return (_locking_thread == Thread::get_current_thread());
}
#endif // CHECK_REENTRANT_MUTEX
////////////////////////////////////////////////////////////////////
// Function: Mutex::do_lock
// Access: Private
// Description: The private implementation of lock().
////////////////////////////////////////////////////////////////////
void Mutex::
do_lock() {
#ifdef CHECK_REENTRANT_MUTEX
nassertv(_locking_thread != Thread::get_current_thread());
#endif
_impl.lock();
#ifdef CHECK_REENTRANT_MUTEX
_locking_thread = Thread::get_current_thread();
#endif
}
////////////////////////////////////////////////////////////////////
// Function: Mutex::do_release
// Access: Private
// Description: The private implementation of release().
////////////////////////////////////////////////////////////////////
void Mutex::
do_release() {
#ifdef CHECK_REENTRANT_MUTEX
nassertv(_locking_thread == Thread::get_current_thread());
_locking_thread = (Thread *)NULL;
#endif
_impl.release();
}
Mutex Mutex::_notify_mutex;

View File

@ -20,9 +20,8 @@
#define PMUTEX_H
#include "pandabase.h"
#include "mutexImpl.h"
class Thread;
#include "mutexDebug.h"
#include "mutexDirect.h"
////////////////////////////////////////////////////////////////////
// Class : Mutex
@ -37,13 +36,18 @@ class Thread;
// work on all platforms; on some platforms, a thread
// can deadlock itself by attempting to lock the same
// mutex twice. If your code requires a reentrant
// mutex, use the ReMutex class instead. When this code
// is compiled with CHECK_REENTRANT_MUTEX true, then it
// will enable assertion checks to ensure that a
// particular thread doesn't try to lock the same mutex
// twice.
// mutex, use the ReMutex class instead.
//
// This class inherits its implementation either from
// MutexDebug or MutexDirect, depending on the
// definition of DEBUG_THREADS.
////////////////////////////////////////////////////////////////////
class EXPCL_PANDAEXPRESS Mutex {
#ifdef DEBUG_THREADS
class EXPCL_PANDAEXPRESS Mutex : public MutexDebug
#else
class EXPCL_PANDAEXPRESS Mutex : public MutexDirect
#endif // DEBUG_THREADS
{
public:
INLINE Mutex();
INLINE ~Mutex();
@ -52,29 +56,9 @@ private:
INLINE void operator = (const Mutex &copy);
public:
INLINE void lock() const;
INLINE void release() const;
#ifdef CHECK_REENTRANT_MUTEX
bool debug_is_locked() const;
#else
INLINE bool debug_is_locked() const;
#endif
private:
void do_lock();
void do_release();
private:
MutexImpl _impl;
#ifdef CHECK_REENTRANT_MUTEX
// Make sure that ordinary mutexes are not locked reentrantly
// (that's what a ReMutex is for).
Thread *_locking_thread;
#endif
friend class ConditionVar;
// This is a global mutex set aside for the purpose of protecting
// Notify messages from being interleaved between threads.
static Mutex _notify_mutex;
};
#include "pmutex.I"

View File

@ -17,17 +17,18 @@
////////////////////////////////////////////////////////////////////
#ifdef HAVE_REMUTEXIMPL
// In this branch, the ReMutex class is implemented as a thin wrapper
// around the native ReMutexImpl class.
////////////////////////////////////////////////////////////////////
// Function: ReMutex::Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
INLINE ReMutex::
ReMutex() {
#ifdef DEBUG_THREADS
ReMutex() : MutexDebug(true)
#else
ReMutex()
#endif // DEBUG_THREADS
{
}
////////////////////////////////////////////////////////////////////
@ -45,7 +46,12 @@ INLINE ReMutex::
// Description: Do not attempt to copy mutexes.
////////////////////////////////////////////////////////////////////
INLINE ReMutex::
ReMutex(const ReMutex &copy) {
#ifdef DEBUG_THREADS
ReMutex(const ReMutex &copy) : MutexDebug(true)
#else
ReMutex(const ReMutex &copy)
#endif // DEBUG_THREADS
{
nassertv(false);
}
@ -58,239 +64,3 @@ INLINE void ReMutex::
operator = (const ReMutex &copy) {
nassertv(false);
}
////////////////////////////////////////////////////////////////////
// Function: ReMutex::lock
// Access: Public
// Description: Grabs the mutex if it is available. If it is not
// available, blocks until it becomes available, then
// grabs it. In either case, the function does not
// return until the mutex is held; you should then call
// unlock().
//
// This method is considered const so that you can lock
// and unlock const mutexes, mainly to allow thread-safe
// access to otherwise const data.
//
// Also see MutexHolder.
////////////////////////////////////////////////////////////////////
INLINE void ReMutex::
lock() const {
((MutexImpl &)_impl).lock();
}
////////////////////////////////////////////////////////////////////
// Function: ReMutex::release
// Access: Public
// Description: Releases the mutex. It is an error to call this if
// the mutex was not already locked.
//
// This method is considered const so that you can lock
// and unlock const mutexes, mainly to allow thread-safe
// access to otherwise const data.
////////////////////////////////////////////////////////////////////
INLINE void ReMutex::
release() const {
((MutexImpl &)_impl).release();
}
////////////////////////////////////////////////////////////////////
// Function: ReMutex::debug_is_locked
// Access: Public
// Description: Returns true if the current thread has locked the
// Mutex, false otherwise. This method is only
// meaningful if CHECK_REENTRANT_MUTEX is defined;
// otherwise, it always returns true.
////////////////////////////////////////////////////////////////////
INLINE bool ReMutex::
debug_is_locked() const {
return true;
}
#elif !defined(THREAD_DUMMY_IMPL) || defined(CHECK_REENTRANT_MUTEX)
// In this branch, the ReMutex object is implemented as a heavy
// wrapper around the Mutex and ConditionVar classes, to impose
// reentrant semantics where the underlying MutexImpl doesn't provide
// them.
////////////////////////////////////////////////////////////////////
// Function: ReMutex::Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
INLINE ReMutex::
ReMutex() : _cvar(_mutex) {
_locking_thread = NULL;
_lock_count = 0;
}
////////////////////////////////////////////////////////////////////
// Function: ReMutex::Destructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
INLINE ReMutex::
~ReMutex() {
nassertv(_locking_thread == NULL && _lock_count == 0);
}
////////////////////////////////////////////////////////////////////
// Function: ReMutex::Copy Constructor
// Access: Private
// Description: Do not attempt to copy mutexes.
////////////////////////////////////////////////////////////////////
INLINE ReMutex::
ReMutex(const ReMutex &copy) : _cvar(_mutex) {
nassertv(false);
}
////////////////////////////////////////////////////////////////////
// Function: ReMutex::Copy Assignment Operator
// Access: Private
// Description: Do not attempt to copy mutexes.
////////////////////////////////////////////////////////////////////
INLINE void ReMutex::
operator = (const ReMutex &copy) {
nassertv(false);
}
////////////////////////////////////////////////////////////////////
// Function: ReMutex::lock
// Access: Public
// Description: Grabs the mutex if it is available. If it is not
// available, blocks until it becomes available, then
// grabs it. In either case, the function does not
// return until the mutex is held; you should then call
// unlock().
//
// This method is considered const so that you can lock
// and unlock const mutexes, mainly to allow thread-safe
// access to otherwise const data.
//
// Also see ReMutexHolder.
////////////////////////////////////////////////////////////////////
INLINE void ReMutex::
lock() const {
((ReMutex *)this)->do_lock();
}
////////////////////////////////////////////////////////////////////
// Function: ReMutex::release
// Access: Public
// Description: Releases the mutex. It is an error to call this if
// the mutex was not already locked.
//
// This method is considered const so that you can lock
// and unlock const mutexes, mainly to allow thread-safe
// access to otherwise const data.
////////////////////////////////////////////////////////////////////
INLINE void ReMutex::
release() const {
((ReMutex *)this)->do_release();
}
////////////////////////////////////////////////////////////////////
// Function: ReMutex::debug_is_locked
// Access: Public
// Description: Returns true if *this thread* already holds the lock,
// false if no one holds the lock or the lock is held by
// some other thread.
////////////////////////////////////////////////////////////////////
INLINE bool ReMutex::
debug_is_locked() const {
MutexHolder holder(_mutex);
return (_locking_thread == Thread::get_current_thread());
}
#else // !THREAD_DUMMY_IMPL || CHECK_REENTRANT_MUTEX
// In this branch, the mutex is stubbed out to do nothing.
////////////////////////////////////////////////////////////////////
// Function: ReMutex::Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
INLINE ReMutex::
ReMutex() {
}
////////////////////////////////////////////////////////////////////
// Function: ReMutex::Destructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
INLINE ReMutex::
~ReMutex() {
}
////////////////////////////////////////////////////////////////////
// Function: ReMutex::Copy Constructor
// Access: Private
// Description: Do not attempt to copy mutexes.
////////////////////////////////////////////////////////////////////
INLINE ReMutex::
ReMutex(const ReMutex &copy) {
nassertv(false);
}
////////////////////////////////////////////////////////////////////
// Function: ReMutex::Copy Assignment Operator
// Access: Private
// Description: Do not attempt to copy mutexes.
////////////////////////////////////////////////////////////////////
INLINE void ReMutex::
operator = (const ReMutex &copy) {
nassertv(false);
}
////////////////////////////////////////////////////////////////////
// Function: ReMutex::lock
// Access: Public
// Description: Grabs the mutex if it is available. If it is not
// available, blocks until it becomes available, then
// grabs it. In either case, the function does not
// return until the mutex is held; you should then call
// unlock().
//
// This method is considered const so that you can lock
// and unlock const mutexes, mainly to allow thread-safe
// access to otherwise const data.
//
// Also see ReMutexHolder.
////////////////////////////////////////////////////////////////////
INLINE void ReMutex::
lock() const {
}
////////////////////////////////////////////////////////////////////
// Function: ReMutex::release
// Access: Public
// Description: Releases the mutex. It is an error to call this if
// the mutex was not already locked.
//
// This method is considered const so that you can lock
// and unlock const mutexes, mainly to allow thread-safe
// access to otherwise const data.
////////////////////////////////////////////////////////////////////
INLINE void ReMutex::
release() const {
}
////////////////////////////////////////////////////////////////////
// Function: ReMutex::debug_is_locked
// Access: Public
// Description: Returns true if the current thread has locked the
// Mutex, false otherwise. This method is only
// meaningful if CHECK_REENTRANT_MUTEX is defined;
// otherwise, it always returns true.
////////////////////////////////////////////////////////////////////
INLINE bool ReMutex::
debug_is_locked() const {
return true;
}
#endif // !THREAD_DUMMY_IMPL || CHECK_REENTRANT_MUTEX

View File

@ -17,69 +17,3 @@
////////////////////////////////////////////////////////////////////
#include "reMutex.h"
#ifndef HAVE_REMUTEXIMPL
// Most of the methods in this class are stubbed out in the
// THREAD_DUMMY_IMPL case, especially when CHECK_REENTRANT_MUTEX is
// not defined. In this case, we're not performing any actual locking
// or verification, so there's no need to do anything at all here.
#if !defined(THREAD_DUMMY_IMPL) || defined(CHECK_REENTRANT_MUTEX)
////////////////////////////////////////////////////////////////////
// Function: ReMutex::do_lock
// Access: Private
// Description: The private implementation of lock().
////////////////////////////////////////////////////////////////////
void ReMutex::
do_lock() {
MutexHolder holder(_mutex);
if (_locking_thread == (Thread *)NULL) {
// The mutex is not already locked by anyone. Lock it.
_locking_thread = Thread::get_current_thread();
++_lock_count;
nassertv(_lock_count == 1);
} else if (_locking_thread == Thread::get_current_thread()) {
// The mutex is already locked by this thread. Increment the lock
// count.
++_lock_count;
} else {
// The mutex is locked by some other thread. Go to sleep on the
// condition variable until it's unlocked.
while (_locking_thread != (Thread *)NULL) {
_cvar.wait();
}
_locking_thread = Thread::get_current_thread();
++_lock_count;
nassertv(_lock_count == 1);
}
}
////////////////////////////////////////////////////////////////////
// Function: ReMutex::do_release
// Access: Private
// Description: The private implementation of release().
////////////////////////////////////////////////////////////////////
void ReMutex::
do_release() {
MutexHolder holder(_mutex);
nassertv(_locking_thread == Thread::get_current_thread());
nassertv(_lock_count > 0);
--_lock_count;
if (_lock_count == 0) {
// That was the last lock held by this thread. Release the lock.
_locking_thread = (Thread *)NULL;
_cvar.signal();
}
}
#endif // !THREAD_DUMMY_IMPL || CHECK_REENTRANT_MUTEX
#endif // !HAVE_REMUTEXIMPL

View File

@ -20,10 +20,8 @@
#define REMUTEX_H
#include "pandabase.h"
#include "pmutex.h"
#include "conditionVar.h"
#include "mutexHolder.h"
#include "thread.h"
#include "mutexDebug.h"
#include "reMutexDirect.h"
////////////////////////////////////////////////////////////////////
// Class : ReMutex
@ -31,36 +29,23 @@
// more than once by the thread that already holds it,
// without deadlock. The thread must eventually release
// the mutex the same number of times it locked it.
//
// This class inherits its implementation either from
// MutexDebug or ReMutexDirect, depending on the
// definition of DEBUG_THREADS.
////////////////////////////////////////////////////////////////////
class EXPCL_PANDAEXPRESS ReMutex {
#ifdef DEBUG_THREADS
class EXPCL_PANDAEXPRESS ReMutex : public MutexDebug
#else
class EXPCL_PANDAEXPRESS ReMutex : public ReMutexDirect
#endif // DEBUG_THREADS
{
public:
INLINE ReMutex();
INLINE ~ReMutex();
private:
INLINE ReMutex(const ReMutex &copy);
INLINE void operator = (const ReMutex &copy);
public:
INLINE void lock() const;
INLINE void release() const;
INLINE bool debug_is_locked() const;
private:
#ifdef HAVE_REMUTEXIMPL
// If the native Mutex implementation provides a reentrant flavor,
// just use that.
ReMutexImpl _impl;
#elif !defined(THREAD_DUMMY_IMPL) || defined(CHECK_REENTRANT_MUTEX)
void do_lock();
void do_release();
Mutex _mutex;
ConditionVar _cvar;
Thread *_locking_thread;
int _lock_count;
#endif // !THREAD_DUMMY_IMPL || CHECK_REENTRANT_MUTEX
};
#include "reMutex.I"

110
panda/src/express/reMutexDirect.I Executable file
View File

@ -0,0 +1,110 @@
// Filename: reMutexDirect.I
// Created by: drose (13Feb06)
//
////////////////////////////////////////////////////////////////////
//
// 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: ReMutexDirect::Constructor
// Access: Protected
// Description:
////////////////////////////////////////////////////////////////////
INLINE ReMutexDirect::
ReMutexDirect() {
}
////////////////////////////////////////////////////////////////////
// Function: ReMutexDirect::Destructor
// Access: Protected
// Description:
////////////////////////////////////////////////////////////////////
INLINE ReMutexDirect::
~ReMutexDirect() {
}
////////////////////////////////////////////////////////////////////
// Function: ReMutexDirect::Copy Constructor
// Access: Private
// Description: Do not attempt to copy reMutexes.
////////////////////////////////////////////////////////////////////
INLINE ReMutexDirect::
ReMutexDirect(const ReMutexDirect &copy) {
nassertv(false);
}
////////////////////////////////////////////////////////////////////
// Function: ReMutexDirect::Copy Assignment Operator
// Access: Private
// Description: Do not attempt to copy reMutexes.
////////////////////////////////////////////////////////////////////
INLINE void ReMutexDirect::
operator = (const ReMutexDirect &copy) {
nassertv(false);
}
#ifdef HAVE_REMUTEXIMPL
////////////////////////////////////////////////////////////////////
// Function: ReMutexDirect::lock
// Access: Public
// Description: Grabs the reMutex if it is available. If it is not
// available, blocks until it becomes available, then
// grabs it. In either case, the function does not
// return until the reMutex is held; you should then call
// unlock().
//
// This method is considered const so that you can lock
// and unlock const reMutexes, mainly to allow thread-safe
// access to otherwise const data.
//
// Also see ReMutexHolder.
////////////////////////////////////////////////////////////////////
INLINE void ReMutexDirect::
lock() const {
((ReMutexDirect *)this)->_impl.lock();
}
#endif // HAVE_REMUTEXIMPL
#ifdef HAVE_REMUTEXIMPL
////////////////////////////////////////////////////////////////////
// Function: ReMutexDirect::release
// Access: Public
// Description: Releases the reMutex. It is an error to call this if
// the reMutex was not already locked.
//
// This method is considered const so that you can lock
// and unlock const reMutexes, mainly to allow thread-safe
// access to otherwise const data.
////////////////////////////////////////////////////////////////////
INLINE void ReMutexDirect::
release() const {
((ReMutexDirect *)this)->_impl.release();
}
#endif // HAVE_REMUTEXIMPL
////////////////////////////////////////////////////////////////////
// Function: ReMutexDirect::debug_is_locked
// Access: Public
// Description: Returns true if the current thread has locked the
// ReMutex, false otherwise. This method is only intended
// for use in debugging, hence the method name; in the
// ReMutexDirect case, it always returns true, since
// there's not a reliable way to determine this
// otherwise.
////////////////////////////////////////////////////////////////////
INLINE bool ReMutexDirect::
debug_is_locked() const {
return true;
}

View File

@ -0,0 +1,116 @@
// Filename: reMutexDirect.cxx
// Created by: drose (13Feb06)
//
////////////////////////////////////////////////////////////////////
//
// 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 "reMutexDirect.h"
#ifndef HAVE_REMUTEXIMPL
MutexImpl ReMutexDirect::_global_lock;
#endif // !HAVE_REMUTEXIMPL
#ifndef HAVE_REMUTEXIMPL
////////////////////////////////////////////////////////////////////
// Function: ReMutexDirect::lock
// Access: Public
// Description: Grabs the reMutex if it is available. If it is not
// available, blocks until it becomes available, then
// grabs it. In either case, the function does not
// return until the reMutex is held; you should then call
// unlock().
//
// This method is considered const so that you can lock
// and unlock const reMutexes, mainly to allow thread-safe
// access to otherwise const data.
//
// Also see ReMutexHolder.
////////////////////////////////////////////////////////////////////
void ReMutexDirect::
lock() const {
_global_mutex.lock();
if (_locking_thread == (Thread *)NULL) {
// The mutex is not already locked by anyone. Lock it.
_locking_thread = Thread::get_current_thread();
++_lock_count;
nassertd(_lock_count == 1) {
}
} else if (_locking_thread == Thread::get_current_thread()) {
// The mutex is already locked by this thread. Increment the lock
// count.
++_lock_count;
nassertd(_lock_count > 0) {
}
} else {
// The mutex is locked by some other thread. Go to sleep on the
// condition variable until it's unlocked.
while (_locking_thread != (Thread *)NULL) {
_cvar.wait();
}
_locking_thread = Thread::get_current_thread();
++_lock_count;
nassertd(_lock_count == 1) {
}
}
_global_mutex.release();
}
#endif // !HAVE_REMUTEXIMPL
#ifndef HAVE_REMUTEXIMPL
////////////////////////////////////////////////////////////////////
// Function: ReMutexDirect::release
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
void ReMutexDirect::
release() {
_global_mutex.lock();
if (_locking_thread != Thread::get_current_thread()) {
ostringstream ostr;
ostr << *_locking_thread << " attempted to release "
<< *this << " which it does not own";
nassert_raise(ostr.str());
_global_mutex.release();
return;
}
nassertd(_lock_count > 0) {
}
--_lock_count;
if (_lock_count == 0) {
// That was the last lock held by this thread. Release the lock.
_locking_thread = (Thread *)NULL;
_cvar.signal();
}
_global_mutex.release();
}
#endif // !HAVE_REMUTEXIMPL
////////////////////////////////////////////////////////////////////
// Function: ReMutexDirect::output
// Access: Public
// Description: This method is declared virtual in MutexDebug, but
// non-virtual in ReMutexDirect.
////////////////////////////////////////////////////////////////////
void ReMutexDirect::
output(ostream &out) const {
out << "ReMutex " << (void *)this;
}

View File

@ -0,0 +1,77 @@
// Filename: reMutexDirect.h
// Created by: drose (13Feb06)
//
////////////////////////////////////////////////////////////////////
//
// 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 REMUTEXDIRECT_H
#define REMUTEXDIRECT_H
#include "pandabase.h"
#include "mutexImpl.h"
#include "conditionVarImpl.h"
class Thread;
////////////////////////////////////////////////////////////////////
// Class : ReMutexDirect
// Description : This class implements a standard reMutex by making
// direct calls to the underlying implementation layer.
// It doesn't perform any debugging operations.
////////////////////////////////////////////////////////////////////
class EXPCL_PANDAEXPRESS ReMutexDirect {
protected:
INLINE ReMutexDirect();
INLINE ~ReMutexDirect();
private:
INLINE ReMutexDirect(const ReMutexDirect &copy);
INLINE void operator = (const ReMutexDirect &copy);
public:
#ifdef HAVE_REMUTEXIMPL
INLINE void lock() const;
INLINE void release() const;
#else
void lock() const;
void release() const;
#endif
INLINE bool debug_is_locked() const;
void output(ostream &out) const;
private:
#ifdef HAVE_REMUTEXIMPL
ReMutexImpl _impl;
#else
// If we don't have a reentrant mutex, we have to hand-roll one.
Thread *_locking_thread;
int _lock_count;
ConditionVarImpl _cvar;
static MutexImpl _global_lock;
#endif // HAVE_REMUTEXIMPL
};
INLINE ostream &
operator << (ostream &out, const ReMutexDirect &m) {
m.output(out);
return out;
}
#include "reMutexDirect.I"
#endif

View File

@ -165,7 +165,7 @@ get_ref_count() const {
#ifndef NDEBUG
test_ref_count_integrity();
#endif
return _ref_count;
return AtomicAdjust::get(_ref_count);
}
////////////////////////////////////////////////////////////////////

View File

@ -55,5 +55,11 @@
#endif
// Let's also factor out some of the other configuration variables.
#if defined(DO_PIPELINING) && defined(HAVE_THREADS)
#define THREADED_PIPELINE 1
#else
#undef THREADED_PIPELINE
#endif
#endif

View File

@ -33,9 +33,7 @@
static int last_rand = 0;
#endif /* __WIN32__ */
Mutex print_mutex;
#define PRINTMSG(x) { MutexHolder l(print_mutex); x; }
#define PRINTMSG(x) { MutexHolder l(Mutex::_notify_mutex); x; }
Mutex rand_mutex;
@ -53,7 +51,16 @@ static double random_f(double max)
// afford luxuries like 2 chopsticks per person.
#define N_DINERS 5
Mutex chopsticks[N_DINERS];
class ChopstickMutex : public Mutex {
public:
void output(ostream &out) const {
out << "chopstick " << _n;
}
int _n;
};
ChopstickMutex chopsticks[N_DINERS];
// At most n philosophers are allowed into the room, others would have to
// wait at the door. This restriction demonstrates the use of condition
@ -81,17 +88,20 @@ private:
int r = l+1;
if (r == N_DINERS)
r = 0;
/*
if (l & 1) {
int t = l;
l = r;
r = t;
}
*/
PRINTMSG(cerr << "Philosopher #" << _id << " has entered the room."
<< endl);
int count = (int)random_f(10.0) + 1;
while (--count) {
chopsticks[l].lock();
chopsticks[r].lock();
Thread::sleep(1);
chopsticks[l].lock();
PRINTMSG(cerr << "Philosopher #" << _id
<< " is eating spaghetti now." << endl);
Thread::sleep(random_f(3.0));
@ -115,12 +125,19 @@ public:
philosopher(const int id) : Thread("philosopher", "a") {
_id = id;
}
virtual void output(ostream &out) const {
out << "philosopher " << _id;
}
};
int main(int, char**)
{
int i;
room_mutex.lock();
for (i=0; i<N_DINERS; ++i) {
chopsticks[i]._n = i;
}
for (i=0; i<N_DINERS; ++i) {
phils[i] = new philosopher(i);
phils[i]->start(TP_normal, false, false);

View File

@ -44,6 +44,10 @@ Thread(const string &name, const string &sync_name) :
_started = false;
_pstats_index = -1;
_pipeline_stage = 0;
#ifdef DEBUG_THREADS
_blocked_on_mutex = NULL;
#endif
}
////////////////////////////////////////////////////////////////////

View File

@ -31,6 +31,9 @@ TypeHandle Thread::_type_handle;
////////////////////////////////////////////////////////////////////
Thread::
~Thread() {
#ifdef DEBUG_THREADS
nassertv(_blocked_on_mutex == NULL);
#endif
}
////////////////////////////////////////////////////////////////////

View File

@ -26,6 +26,10 @@
#include "threadImpl.h"
#include "notify.h"
class Mutex;
class ReMutex;
class MutexDebug;
////////////////////////////////////////////////////////////////////
// Class : Thread
// Description : A thread; that is, a lightweight process. This is an
@ -87,12 +91,18 @@ private:
protected:
bool _started;
private:
string _name;
string _sync_name;
ThreadImpl _impl;
int _pstats_index;
int _pipeline_stage;
#ifdef DEBUG_THREADS
MutexDebug *_blocked_on_mutex;
#endif
private:
static Thread *_main_thread;
static Thread *_external_thread;
@ -114,6 +124,8 @@ public:
private:
static TypeHandle _type_handle;
friend class MutexDebug;
friend class ThreadDummyImpl;
friend class ThreadWin32Impl;
friend class ThreadPosixImpl;

View File

@ -23,7 +23,6 @@
#include "pointerTo.h"
#include "config_express.h"
#include "mutexHolder.h"
PRUintn ThreadNsprImpl::_pt_ptr_index = 0;
bool ThreadNsprImpl::_got_pt_ptr_index = false;
@ -89,11 +88,15 @@ ThreadNsprImpl::
////////////////////////////////////////////////////////////////////
bool ThreadNsprImpl::
start(ThreadPriority priority, bool global, bool joinable) {
MutexHolder holder(_mutex);
_mutex.lock();
if (thread_cat.is_debug()) {
thread_cat.debug() << "Starting thread " << _parent_obj->get_name() << "\n";
}
nassertr(_thread == (PRThread *)NULL, false);
nassertd(_thread == (PRThread *)NULL) {
_mutex.release();
return false;
}
_joinable = joinable;
if (!_got_pt_ptr_index) {
@ -107,10 +110,6 @@ start(ThreadPriority priority, bool global, bool joinable) {
nspr_pri = PR_PRIORITY_LOW;
break;
case TP_normal:
nspr_pri = PR_PRIORITY_NORMAL;
break;
case TP_high:
nspr_pri = PR_PRIORITY_HIGH;
break;
@ -119,8 +118,8 @@ start(ThreadPriority priority, bool global, bool joinable) {
nspr_pri = PR_PRIORITY_URGENT;
break;
case TP_normal:
default:
nassertr(false, false);
nspr_pri = PR_PRIORITY_NORMAL;
}
@ -140,10 +139,12 @@ start(ThreadPriority priority, bool global, bool joinable) {
// reference count we incremented above, and return false to
// indicate failure.
unref_delete(_parent_obj);
_mutex.release();
return false;
}
// Thread was successfully started.
_mutex.release();
return true;
}
@ -172,11 +173,12 @@ interrupt() {
////////////////////////////////////////////////////////////////////
void ThreadNsprImpl::
join() {
MutexHolder holder(_mutex);
_mutex.lock();
if (_joinable && _thread != (PRThread *)NULL) {
PR_JoinThread(_thread);
_thread = (PRThread *)NULL;
}
_mutex.release();
}
////////////////////////////////////////////////////////////////////

View File

@ -26,7 +26,7 @@
#include "notify.h"
#include "threadPriority.h"
#include "pmutex.h"
#include "mutexNsprImpl.h"
#include <prthread.h>
#include <prinit.h>
@ -57,7 +57,7 @@ private:
static void root_func(void *data);
static void init_pt_ptr_index();
Mutex _mutex;
MutexNsprImpl _mutex;
Thread *_parent_obj;
PRThread *_thread;
bool _joinable;

Some files were not shown because too many files have changed in this diff Show More