1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-03-01 15:09:43 +00:00

Use shared locks in physics system when using multithreaded bullet

This commit is contained in:
elsid 2023-02-12 14:51:46 +01:00
parent e0a25e02f0
commit 09199ea006
No known key found for this signature in database
GPG key ID: 4DE04C198CBA7625
2 changed files with 101 additions and 65 deletions

View file

@ -3,6 +3,7 @@
#include <optional> #include <optional>
#include <shared_mutex> #include <shared_mutex>
#include <mutex> #include <mutex>
#include <stdexcept>
#include <BulletCollision/BroadphaseCollision/btDbvtBroadphase.h> #include <BulletCollision/BroadphaseCollision/btDbvtBroadphase.h>
#include <BulletCollision/CollisionShapes/btCollisionShape.h> #include <BulletCollision/CollisionShapes/btCollisionShape.h>
@ -36,11 +37,11 @@
namespace namespace
{ {
template <class Mutex> template <class Mutex>
std::optional<std::unique_lock<Mutex>> makeExclusiveLock(Mutex& mutex, unsigned threadCount) std::optional<std::unique_lock<Mutex>> makeExclusiveLock(Mutex& mutex, MWPhysics::LockingPolicy lockingPolicy)
{ {
if (threadCount > 0) if (lockingPolicy == MWPhysics::LockingPolicy::NoLocks)
return std::unique_lock(mutex); return {};
return {}; return std::unique_lock(mutex);
} }
/// @brief A scoped lock that is either exclusive or inexistent depending on configuration /// @brief A scoped lock that is either exclusive or inexistent depending on configuration
@ -48,22 +49,21 @@ namespace
class MaybeExclusiveLock class MaybeExclusiveLock
{ {
public: public:
/// @param mutex a mutex explicit MaybeExclusiveLock(Mutex& mutex, MWPhysics::LockingPolicy lockingPolicy)
/// @param threadCount decide wether the excluse lock will be taken : mImpl(makeExclusiveLock(mutex, lockingPolicy))
explicit MaybeExclusiveLock(Mutex& mutex, unsigned threadCount) {
: mImpl(makeExclusiveLock(mutex, threadCount)) }
{}
private: private:
std::optional<std::unique_lock<Mutex>> mImpl; std::optional<std::unique_lock<Mutex>> mImpl;
}; };
template <class Mutex> template <class Mutex>
std::optional<std::shared_lock<Mutex>> makeSharedLock(Mutex& mutex, unsigned threadCount) std::optional<std::shared_lock<Mutex>> makeSharedLock(Mutex& mutex, MWPhysics::LockingPolicy lockingPolicy)
{ {
if (threadCount > 0) if (lockingPolicy == MWPhysics::LockingPolicy::NoLocks)
return std::shared_lock(mutex); return {};
return {}; return std::shared_lock(mutex);
} }
/// @brief A scoped lock that is either shared or inexistent depending on configuration /// @brief A scoped lock that is either shared or inexistent depending on configuration
@ -71,24 +71,31 @@ namespace
class MaybeSharedLock class MaybeSharedLock
{ {
public: public:
/// @param mutex a shared mutex explicit MaybeSharedLock(Mutex& mutex, MWPhysics::LockingPolicy lockingPolicy)
/// @param threadCount decide wether the shared lock will be taken : mImpl(makeSharedLock(mutex, lockingPolicy))
explicit MaybeSharedLock(Mutex& mutex, unsigned threadCount) {
: mImpl(makeSharedLock(mutex, threadCount)) }
{}
private: private:
std::optional<std::shared_lock<Mutex>> mImpl; std::optional<std::shared_lock<Mutex>> mImpl;
}; };
template <class Mutex> template <class Mutex>
std::variant<std::monostate, std::unique_lock<Mutex>, std::shared_lock<Mutex>> makeLock(Mutex& mutex, unsigned threadCount) std::variant<std::monostate, std::unique_lock<Mutex>, std::shared_lock<Mutex>> makeLock(
Mutex& mutex, MWPhysics::LockingPolicy lockingPolicy)
{ {
if (threadCount > 1) switch (lockingPolicy)
return std::shared_lock(mutex); {
if (threadCount == 1) case MWPhysics::LockingPolicy::NoLocks:
return std::unique_lock(mutex); return std::monostate{};
return std::monostate {}; case MWPhysics::LockingPolicy::ExclusiveLocksOnly:
return std::unique_lock(mutex);
case MWPhysics::LockingPolicy::AllowSharedLocks:
return std::shared_lock(mutex);
};
throw std::runtime_error("Unsupported LockingPolicy: "
+ std::to_string(static_cast<std::underlying_type_t<MWPhysics::LockingPolicy>>(lockingPolicy)));
} }
/// @brief A scoped lock that is either shared, exclusive or inexistent depending on configuration /// @brief A scoped lock that is either shared, exclusive or inexistent depending on configuration
@ -96,10 +103,10 @@ namespace
class MaybeLock class MaybeLock
{ {
public: public:
/// @param mutex a shared mutex explicit MaybeLock(Mutex& mutex, MWPhysics::LockingPolicy lockingPolicy)
/// @param threadCount decide wether the lock will be shared, exclusive or inexistent : mImpl(makeLock(mutex, lockingPolicy))
explicit MaybeLock(Mutex& mutex, unsigned threadCount) {
: mImpl(makeLock(mutex, threadCount)) {} }
private: private:
std::variant<std::monostate, std::unique_lock<Mutex>, std::shared_lock<Mutex>> mImpl; std::variant<std::monostate, std::unique_lock<Mutex>, std::shared_lock<Mutex>> mImpl;
@ -132,7 +139,7 @@ namespace
{ {
const Impl& mImpl; const Impl& mImpl;
std::shared_mutex& mCollisionWorldMutex; std::shared_mutex& mCollisionWorldMutex;
const unsigned mNumThreads; const MWPhysics::LockingPolicy mLockingPolicy;
template <class Ptr, class FrameData> template <class Ptr, class FrameData>
void operator()(MWPhysics::SimulationImpl<Ptr, FrameData>& sim) const void operator()(MWPhysics::SimulationImpl<Ptr, FrameData>& sim) const
@ -144,7 +151,7 @@ namespace
// Locked shared_ptr has to be destructed after releasing mCollisionWorldMutex to avoid // Locked shared_ptr has to be destructed after releasing mCollisionWorldMutex to avoid
// possible deadlock. Ptr destructor also acquires mCollisionWorldMutex. // possible deadlock. Ptr destructor also acquires mCollisionWorldMutex.
const std::pair arg(std::move(ptr), frameData); const std::pair arg(std::move(ptr), frameData);
const Lock<std::shared_mutex> lock(mCollisionWorldMutex, mNumThreads); const Lock<std::shared_mutex> lock(mCollisionWorldMutex, mLockingPolicy);
mImpl(arg); mImpl(arg);
} }
}; };
@ -284,23 +291,43 @@ namespace
} }
}; };
} }
}
namespace Config namespace MWPhysics
{
namespace
{ {
/// @return either the number of thread as configured by the user, or 1 if Bullet doesn't support multithreading and user requested more than 1 background threads int getMaxBulletSupportedThreads()
unsigned computeNumThreads()
{ {
int wantedThread = Settings::Manager::getInt("async num threads", "Physics");
auto broad = std::make_unique<btDbvtBroadphase>(); auto broad = std::make_unique<btDbvtBroadphase>();
auto maxSupportedThreads = broad->m_rayTestStacks.size(); return broad->m_rayTestStacks.size();
auto threadSafeBullet = (maxSupportedThreads > 1); }
if (!threadSafeBullet && wantedThread > 1)
LockingPolicy detectLockingPolicy()
{
if (Settings::Manager::getInt("async num threads", "Physics") < 1)
return LockingPolicy::NoLocks;
if (getMaxBulletSupportedThreads() > 1)
return LockingPolicy::AllowSharedLocks;
Log(Debug::Warning) << "Bullet was not compiled with multithreading support, 1 async thread will be used";
return LockingPolicy::ExclusiveLocksOnly;
}
unsigned getNumThreads(LockingPolicy lockingPolicy)
{
switch (lockingPolicy)
{ {
Log(Debug::Warning) << "Bullet was not compiled with multithreading support, 1 async thread will be used"; case LockingPolicy::NoLocks:
return 1; return 0;
case LockingPolicy::ExclusiveLocksOnly:
return 1;
case LockingPolicy::AllowSharedLocks:
return static_cast<unsigned>(std::max(
getMaxBulletSupportedThreads(), Settings::Manager::getInt("async num threads", "Physics")));
} }
return static_cast<unsigned>(std::max(0, wantedThread));
throw std::runtime_error("Unsupported LockingPolicy: "
+ std::to_string(static_cast<std::underlying_type_t<LockingPolicy>>(lockingPolicy)));
} }
} }
} }
@ -313,7 +340,8 @@ namespace MWPhysics
, mTimeAccum(0.f) , mTimeAccum(0.f)
, mCollisionWorld(collisionWorld) , mCollisionWorld(collisionWorld)
, mDebugDrawer(debugDrawer) , mDebugDrawer(debugDrawer)
, mNumThreads(Config::computeNumThreads()) , mLockingPolicy(detectLockingPolicy())
, mNumThreads(getNumThreads(mLockingPolicy))
, mNumJobs(0) , mNumJobs(0)
, mRemainingSteps(0) , mRemainingSteps(0)
, mLOSCacheExpiry(Settings::Manager::getInt("lineofsight keep inactive cache", "Physics")) , mLOSCacheExpiry(Settings::Manager::getInt("lineofsight keep inactive cache", "Physics"))
@ -354,7 +382,7 @@ namespace MWPhysics
{ {
waitForWorkers(); waitForWorkers();
{ {
MaybeExclusiveLock lock(mSimulationMutex, mNumThreads); MaybeExclusiveLock lock(mSimulationMutex, mLockingPolicy);
mQuit = true; mQuit = true;
mNumJobs = 0; mNumJobs = 0;
mRemainingSteps = 0; mRemainingSteps = 0;
@ -417,7 +445,7 @@ namespace MWPhysics
// This function run in the main thread. // This function run in the main thread.
// While the mSimulationMutex is held, background physics threads can't run. // While the mSimulationMutex is held, background physics threads can't run.
MaybeExclusiveLock lock(mSimulationMutex, mNumThreads); MaybeExclusiveLock lock(mSimulationMutex, mLockingPolicy);
double timeStart = mTimer->tick(); double timeStart = mTimer->tick();
@ -475,7 +503,7 @@ namespace MWPhysics
void PhysicsTaskScheduler::resetSimulation(const ActorMap& actors) void PhysicsTaskScheduler::resetSimulation(const ActorMap& actors)
{ {
waitForWorkers(); waitForWorkers();
MaybeExclusiveLock lock(mSimulationMutex, mNumThreads); MaybeExclusiveLock lock(mSimulationMutex, mLockingPolicy);
mBudget.reset(mDefaultPhysicsDt); mBudget.reset(mDefaultPhysicsDt);
mAsyncBudget.reset(0.0f); mAsyncBudget.reset(0.0f);
mSimulations.clear(); mSimulations.clear();
@ -488,25 +516,25 @@ namespace MWPhysics
void PhysicsTaskScheduler::rayTest(const btVector3& rayFromWorld, const btVector3& rayToWorld, btCollisionWorld::RayResultCallback& resultCallback) const void PhysicsTaskScheduler::rayTest(const btVector3& rayFromWorld, const btVector3& rayToWorld, btCollisionWorld::RayResultCallback& resultCallback) const
{ {
MaybeLock lock(mCollisionWorldMutex, mNumThreads); MaybeLock lock(mCollisionWorldMutex, mLockingPolicy);
mCollisionWorld->rayTest(rayFromWorld, rayToWorld, resultCallback); mCollisionWorld->rayTest(rayFromWorld, rayToWorld, resultCallback);
} }
void PhysicsTaskScheduler::convexSweepTest(const btConvexShape* castShape, const btTransform& from, const btTransform& to, btCollisionWorld::ConvexResultCallback& resultCallback) const void PhysicsTaskScheduler::convexSweepTest(const btConvexShape* castShape, const btTransform& from, const btTransform& to, btCollisionWorld::ConvexResultCallback& resultCallback) const
{ {
MaybeLock lock(mCollisionWorldMutex, mNumThreads); MaybeLock lock(mCollisionWorldMutex, mLockingPolicy);
mCollisionWorld->convexSweepTest(castShape, from, to, resultCallback); mCollisionWorld->convexSweepTest(castShape, from, to, resultCallback);
} }
void PhysicsTaskScheduler::contactTest(btCollisionObject* colObj, btCollisionWorld::ContactResultCallback& resultCallback) void PhysicsTaskScheduler::contactTest(btCollisionObject* colObj, btCollisionWorld::ContactResultCallback& resultCallback)
{ {
MaybeSharedLock lock(mCollisionWorldMutex, mNumThreads); MaybeSharedLock lock(mCollisionWorldMutex, mLockingPolicy);
ContactTestWrapper::contactTest(mCollisionWorld, colObj, resultCallback); ContactTestWrapper::contactTest(mCollisionWorld, colObj, resultCallback);
} }
std::optional<btVector3> PhysicsTaskScheduler::getHitPoint(const btTransform& from, btCollisionObject* target) std::optional<btVector3> PhysicsTaskScheduler::getHitPoint(const btTransform& from, btCollisionObject* target)
{ {
MaybeLock lock(mCollisionWorldMutex, mNumThreads); MaybeLock lock(mCollisionWorldMutex, mLockingPolicy);
// target the collision object's world origin, this should be the center of the collision object // target the collision object's world origin, this should be the center of the collision object
btTransform rayTo; btTransform rayTo;
rayTo.setIdentity(); rayTo.setIdentity();
@ -523,33 +551,33 @@ namespace MWPhysics
void PhysicsTaskScheduler::aabbTest(const btVector3& aabbMin, const btVector3& aabbMax, btBroadphaseAabbCallback& callback) void PhysicsTaskScheduler::aabbTest(const btVector3& aabbMin, const btVector3& aabbMax, btBroadphaseAabbCallback& callback)
{ {
MaybeSharedLock lock(mCollisionWorldMutex, mNumThreads); MaybeSharedLock lock(mCollisionWorldMutex, mLockingPolicy);
mCollisionWorld->getBroadphase()->aabbTest(aabbMin, aabbMax, callback); mCollisionWorld->getBroadphase()->aabbTest(aabbMin, aabbMax, callback);
} }
void PhysicsTaskScheduler::getAabb(const btCollisionObject* obj, btVector3& min, btVector3& max) void PhysicsTaskScheduler::getAabb(const btCollisionObject* obj, btVector3& min, btVector3& max)
{ {
MaybeSharedLock lock(mCollisionWorldMutex, mNumThreads); MaybeSharedLock lock(mCollisionWorldMutex, mLockingPolicy);
obj->getCollisionShape()->getAabb(obj->getWorldTransform(), min, max); obj->getCollisionShape()->getAabb(obj->getWorldTransform(), min, max);
} }
void PhysicsTaskScheduler::setCollisionFilterMask(btCollisionObject* collisionObject, int collisionFilterMask) void PhysicsTaskScheduler::setCollisionFilterMask(btCollisionObject* collisionObject, int collisionFilterMask)
{ {
MaybeExclusiveLock lock(mCollisionWorldMutex, mNumThreads); MaybeExclusiveLock lock(mCollisionWorldMutex, mLockingPolicy);
collisionObject->getBroadphaseHandle()->m_collisionFilterMask = collisionFilterMask; collisionObject->getBroadphaseHandle()->m_collisionFilterMask = collisionFilterMask;
} }
void PhysicsTaskScheduler::addCollisionObject(btCollisionObject* collisionObject, int collisionFilterGroup, int collisionFilterMask) void PhysicsTaskScheduler::addCollisionObject(btCollisionObject* collisionObject, int collisionFilterGroup, int collisionFilterMask)
{ {
mCollisionObjects.insert(collisionObject); mCollisionObjects.insert(collisionObject);
MaybeExclusiveLock lock(mCollisionWorldMutex, mNumThreads); MaybeExclusiveLock lock(mCollisionWorldMutex, mLockingPolicy);
mCollisionWorld->addCollisionObject(collisionObject, collisionFilterGroup, collisionFilterMask); mCollisionWorld->addCollisionObject(collisionObject, collisionFilterGroup, collisionFilterMask);
} }
void PhysicsTaskScheduler::removeCollisionObject(btCollisionObject* collisionObject) void PhysicsTaskScheduler::removeCollisionObject(btCollisionObject* collisionObject)
{ {
mCollisionObjects.erase(collisionObject); mCollisionObjects.erase(collisionObject);
MaybeExclusiveLock lock(mCollisionWorldMutex, mNumThreads); MaybeExclusiveLock lock(mCollisionWorldMutex, mLockingPolicy);
mCollisionWorld->removeCollisionObject(collisionObject); mCollisionWorld->removeCollisionObject(collisionObject);
} }
@ -561,14 +589,14 @@ namespace MWPhysics
} }
else else
{ {
MaybeExclusiveLock lock(mUpdateAabbMutex, mNumThreads); MaybeExclusiveLock lock(mUpdateAabbMutex, mLockingPolicy);
mUpdateAabb.insert(ptr); mUpdateAabb.insert(ptr);
} }
} }
bool PhysicsTaskScheduler::getLineOfSight(const std::shared_ptr<Actor>& actor1, const std::shared_ptr<Actor>& actor2) bool PhysicsTaskScheduler::getLineOfSight(const std::shared_ptr<Actor>& actor1, const std::shared_ptr<Actor>& actor2)
{ {
MaybeExclusiveLock lock(mLOSCacheMutex, mNumThreads); MaybeExclusiveLock lock(mLOSCacheMutex, mLockingPolicy);
auto req = LOSRequest(actor1, actor2); auto req = LOSRequest(actor1, actor2);
auto result = std::find(mLOSCache.begin(), mLOSCache.end(), req); auto result = std::find(mLOSCache.begin(), mLOSCache.end(), req);
@ -584,7 +612,7 @@ namespace MWPhysics
void PhysicsTaskScheduler::refreshLOSCache() void PhysicsTaskScheduler::refreshLOSCache()
{ {
MaybeSharedLock lock(mLOSCacheMutex, mNumThreads); MaybeSharedLock lock(mLOSCacheMutex, mLockingPolicy);
int job = 0; int job = 0;
int numLOS = mLOSCache.size(); int numLOS = mLOSCache.size();
while ((job = mNextLOS.fetch_add(1, std::memory_order_relaxed)) < numLOS) while ((job = mNextLOS.fetch_add(1, std::memory_order_relaxed)) < numLOS)
@ -603,7 +631,7 @@ namespace MWPhysics
void PhysicsTaskScheduler::updateAabbs() void PhysicsTaskScheduler::updateAabbs()
{ {
MaybeExclusiveLock lock(mUpdateAabbMutex, mNumThreads); MaybeExclusiveLock lock(mUpdateAabbMutex, mLockingPolicy);
std::for_each(mUpdateAabb.begin(), mUpdateAabb.end(), std::for_each(mUpdateAabb.begin(), mUpdateAabb.end(),
[this](const std::weak_ptr<PtrHolder>& ptr) [this](const std::weak_ptr<PtrHolder>& ptr)
{ {
@ -616,7 +644,7 @@ namespace MWPhysics
void PhysicsTaskScheduler::updatePtrAabb(const std::shared_ptr<PtrHolder>& ptr) void PhysicsTaskScheduler::updatePtrAabb(const std::shared_ptr<PtrHolder>& ptr)
{ {
MaybeExclusiveLock lock(mCollisionWorldMutex, mNumThreads); MaybeExclusiveLock lock(mCollisionWorldMutex, mLockingPolicy);
if (const auto actor = std::dynamic_pointer_cast<Actor>(ptr)) if (const auto actor = std::dynamic_pointer_cast<Actor>(ptr))
{ {
actor->updateCollisionObjectPosition(); actor->updateCollisionObjectPosition();
@ -653,7 +681,7 @@ namespace MWPhysics
void PhysicsTaskScheduler::updateActorsPositions() void PhysicsTaskScheduler::updateActorsPositions()
{ {
const Visitors::UpdatePosition impl{mCollisionWorld}; const Visitors::UpdatePosition impl{mCollisionWorld};
const Visitors::WithLockedPtr<Visitors::UpdatePosition, MaybeExclusiveLock> vis{impl, mCollisionWorldMutex, mNumThreads}; const Visitors::WithLockedPtr<Visitors::UpdatePosition, MaybeExclusiveLock> vis{impl, mCollisionWorldMutex, mLockingPolicy};
for (Simulation& sim : mSimulations) for (Simulation& sim : mSimulations)
std::visit(vis, sim); std::visit(vis, sim);
} }
@ -667,7 +695,7 @@ namespace MWPhysics
resultCallback.m_collisionFilterGroup = CollisionType_AnyPhysical; resultCallback.m_collisionFilterGroup = CollisionType_AnyPhysical;
resultCallback.m_collisionFilterMask = CollisionType_World|CollisionType_HeightMap|CollisionType_Door; resultCallback.m_collisionFilterMask = CollisionType_World|CollisionType_HeightMap|CollisionType_Door;
MaybeLock lockColWorld(mCollisionWorldMutex, mNumThreads); MaybeLock lockColWorld(mCollisionWorldMutex, mLockingPolicy);
mCollisionWorld->rayTest(pos1, pos2, resultCallback); mCollisionWorld->rayTest(pos1, pos2, resultCallback);
return !resultCallback.hasHit(); return !resultCallback.hasHit();
@ -680,7 +708,7 @@ namespace MWPhysics
mPreStepBarrier->wait([this] { afterPreStep(); }); mPreStepBarrier->wait([this] { afterPreStep(); });
int job = 0; int job = 0;
const Visitors::Move impl{mPhysicsDt, mCollisionWorld, *mWorldFrameData}; const Visitors::Move impl{mPhysicsDt, mCollisionWorld, *mWorldFrameData};
const Visitors::WithLockedPtr<Visitors::Move, MaybeLock> vis{impl, mCollisionWorldMutex, mNumThreads}; const Visitors::WithLockedPtr<Visitors::Move, MaybeLock> vis{impl, mCollisionWorldMutex, mLockingPolicy};
while ((job = mNextJob.fetch_add(1, std::memory_order_relaxed)) < mNumJobs) while ((job = mNextJob.fetch_add(1, std::memory_order_relaxed)) < mNumJobs)
std::visit(vis, mSimulations[job]); std::visit(vis, mSimulations[job]);
@ -708,7 +736,7 @@ namespace MWPhysics
void PhysicsTaskScheduler::debugDraw() void PhysicsTaskScheduler::debugDraw()
{ {
MaybeSharedLock lock(mCollisionWorldMutex, mNumThreads); MaybeSharedLock lock(mCollisionWorldMutex, mLockingPolicy);
mDebugDrawer->step(); mDebugDrawer->step();
} }
@ -734,7 +762,7 @@ namespace MWPhysics
if (!mRemainingSteps) if (!mRemainingSteps)
return; return;
const Visitors::PreStep impl{mCollisionWorld}; const Visitors::PreStep impl{mCollisionWorld};
const Visitors::WithLockedPtr<Visitors::PreStep, MaybeExclusiveLock> vis{impl, mCollisionWorldMutex, mNumThreads}; const Visitors::WithLockedPtr<Visitors::PreStep, MaybeExclusiveLock> vis{impl, mCollisionWorldMutex, mLockingPolicy};
for (auto& sim : mSimulations) for (auto& sim : mSimulations)
std::visit(vis, sim); std::visit(vis, sim);
} }
@ -752,7 +780,7 @@ namespace MWPhysics
void PhysicsTaskScheduler::afterPostSim() void PhysicsTaskScheduler::afterPostSim()
{ {
{ {
MaybeExclusiveLock lock(mLOSCacheMutex, mNumThreads); MaybeExclusiveLock lock(mLOSCacheMutex, mLockingPolicy);
mLOSCache.erase( mLOSCache.erase(
std::remove_if(mLOSCache.begin(), mLOSCache.end(), std::remove_if(mLOSCache.begin(), mLOSCache.end(),
[](const LOSRequest& req) { return req.mStale; }), [](const LOSRequest& req) { return req.mStale; }),

View file

@ -29,6 +29,13 @@ namespace MWRender
namespace MWPhysics namespace MWPhysics
{ {
enum class LockingPolicy
{
NoLocks,
ExclusiveLocksOnly,
AllowSharedLocks,
};
class PhysicsTaskScheduler class PhysicsTaskScheduler
{ {
public: public:
@ -92,6 +99,7 @@ namespace MWPhysics
std::unique_ptr<Misc::Barrier> mPostStepBarrier; std::unique_ptr<Misc::Barrier> mPostStepBarrier;
std::unique_ptr<Misc::Barrier> mPostSimBarrier; std::unique_ptr<Misc::Barrier> mPostSimBarrier;
LockingPolicy mLockingPolicy;
unsigned mNumThreads; unsigned mNumThreads;
int mNumJobs; int mNumJobs;
int mRemainingSteps; int mRemainingSteps;