Debug multithreading access check crash

Debug multithreading access check crash

Hi, first of all thanks for releasing Havok, its a fantastic tool that it's great to be able to use.

I've been experiencing a crash only in debug mode with the following call stack:
> BurneEngine.exe!hkMultiThreadCheck::accessCheck(hkMultiThreadCheck::AccessType type=HK_ACCESS_RO) Line 186 + 0x6f bytes C++ BurneEngine.exe!hkMultiThreadCheck::accessCheckWithParent(const hkMultiThreadCheck * parentLock=0x02a951d8, hkMultiThreadCheck::AccessType parentType=HK_ACCESS_RO, const hkMultiThreadCheck & lock={...}, hkMultiThreadCheck::AccessType type=HK_ACCESS_RO) Line 214 C++

BurneEngine.exe!hkpWorldConstraintUtil::findModifier(hkpConstraintInstance * instance=0x14595444, hkpConstraintAtom::AtomType type=TYPE_MODIFIER_MASS_CHANGER) Line 525 C++

BurneEngine.exe!hkpResponseModifier::setInvMassScalingForContact(hkpDynamicsContactMgr * manager=0x14595400, hkpRigidBody * bodyA=0x070a7680, hkpRigidBody * bodyB=0x0ee70370, hkpConstraintOwner & constraintOwner={...}, float factorA=1.0000000, float factorB=1.0000000) Line 45 + 0x8 bytes C++

BurneEngine.exe!hkCharacterRigidBodyCollisionListener::contactProcessCallback(hkpContactProcessEvent & event={...}) Line 222 + 0x29 bytes C++

BurneEngine.exe!hkpEntityCallbackUtil::fireContactProcessInternal(hkpEntity * entity=0x9084495f, hkpContactProcessEvent & event={...}) Line 191 C++

BurneEngine.exe!hkpSimpleConstraintContactMgr::processContactImpl(const hkpCollidable & a={...}, const hkpCollidable & b={...}, const hkpProcessCollisionInput & input={...}, hkpProcessCollisionData & collisionData={...}) Line 580 + 0x46 bytes C++

BurneEngine.exe!hkCpuAgentSectorJob(hkpMtThreadStructure & tl={...}, hkJobQueue & jobQueue={...}, hkJobQueue::JobQueueEntry & nextJobOut={...}) Line 212 C++

BurneEngine.exe!hkpMultiThreadedSimulation::processNextJob(hkJobQueue::ThreadType threadType=THREAD_TYPE_CPU_BROADPHASE) Line 215 + 0xc bytes C++

BurneEngine.exe!hkpMultiThreadedSimulation::stepProcessMt(const hkpThreadToken & token=HK_THREAD_TOKEN_SECOND) Line 746 C++

BurneEngine.exe!hkpWorld::stepProcessMt(const hkpThreadToken & token=HK_THREAD_TOKEN_SECOND) Line 2172 C++

BurneEngine.exe!WorkerThreadFunc(void * v=0x02a94b98) Line 525 C++
ntdll.dll!7c90e2dc()
[Frames below may be incorrect and/or missing, no symbols loaded for ntdll.dll]
kernel32.dll!7c80b683()
msvcr90d.dll!102c103e()

I'm guessing this has something to do with my hkCharacterRigidBody since I see hkCharacterRigidBodyCollisionListener in the call stack. I'm just using a slightly modified version of the ControlCharacterRbDemo code.

void ControllableEntity::Update(
float deltaTime)
{
hkpWorld* m_world = EntityManager::GetInstance()->p->physicsWorld;
m_world->lock();
if (markForDelete)
{
delete this;
return;
}

if (GetBasicBody()->getWorld()!= HK_NULL)
{
if (GetBasicBody()->isActive())
{
hkVector4 temp = GetBasicBody()->getPosition();
node->setPosition(temp(0),temp(1),temp(2));
hkQuaternion temp2 = GetBasicBody()->getRotation();
node->setOrientation(temp2(3),temp2(0),temp2(1), temp2(2));
//correction for discrepancy in havok and ogre coord systems
node->pitch(Degree(90));
}
}
else
{
//Physics Body has been removed from the world: delete the damn thing.
delete this;
return;
}

// Get user input data
{
m_currentAngle += deltaAngle;
if (m_currentAngle >= 2 * HK_REAL_PI)
m_currentAngle -= 2 * HK_REAL_PI;
if (m_currentAngle <= 2 * HK_REAL_PI)
m_currentAngle += 2 * HK_REAL_PI;

deltaAngle = 0.f;
m_currentOrient.setAxisAngle(UP, m_currentAngle);
}

//ROTATE THE PHYSICS BODY
GetBasicBody()->setRotation(m_currentOrient);

//
// Detect ladder
//
hkReal m_timestep = deltaTime / 1000.f;

HK_TIMER_BEGIN( "set character state", HK_NULL );

hkpCharacterInput input;
hkpCharacterOutput output;
&nbs
p; {
input.m_inputLR = desiredZ;
input.m_inputUD = desiredX;

input.m_wantJump = false;
input.m_atLadder = false;

input.m_up = UP;
input.m_forward.set(0,0,-1);
input.m_forward.setRotatedDir( m_currentOrient, input.m_forward );

hkStepInfo stepInfo;
stepInfo.m_deltaTime = deltaTime / 1000.f;
stepInfo.m_invDeltaTime = 1.0f/(deltaTime / 1000.f);

input.m_stepInfo = stepInfo;

input.m_characterGravity.set(0,-9.8,0);
input.m_velocity = m_characterRigidBody->getRigidBody()->getLinearVelocity();
input.m_position = m_characterRigidBody->getRigidBody()->getPosition();

bool atLadder = false;
if (atLadder)
{

}
else
{

hkpSurfaceInfo ground;
m_characterRigidBody->checkSupport(stepInfo, ground);

// Avoid accidental state changes (Smooth movement on stairs)
// During transition supported->unsupported continue to return N-frames hkpSurfaceInfo data from previous supported state
{

// Number of frames to skip (continue with previous hkpSurfaceInfo data)
const int skipFramesInAir = 3;

if (input.m_wantJump)
{
m_framesInAir = skipFramesInAir;
}
&nbs
p;
if ( ground.m_supportedState != hkpSurfaceInfo::SUPPORTED )
{
if (m_framesInAir < skipFramesInAir)
{
input.m_isSupported = true;
input.m_surfaceNormal = m_previousGround->m_surfaceNormal;
input.m_surfaceVelocity = m_previousGround->m_surfaceVelocity;
input.m_surfaceMotionType = m_previousGround->m_surfaceMotionType;
}
else
{
input.m_isSupported = false;
input.m_surfaceNormal = ground.m_surfaceNormal;
input.m_surfaceVelocity = ground.m_surfaceVelocity;
input.m_surfaceMotionType = ground.m_surfaceMotionType;
}

m_framesInAir++;
}
else
{
input.m_isSupported = true;
input.m_surfaceNormal = ground.m_surfaceNormal;
&nb
sp; input.m_surfaceVelocity = ground.m_surfaceVelocity;
input.m_surfaceMotionType = ground.m_surfaceMotionType;

m_previousGround->set(ground);

// reset old number of frames
if (m_framesInAir > skipFramesInAir)
{
m_framesInAir = 0;
}

}
}
}

HK_TIMER_END();
}

// Apply the character state machine
{
HK_TIMER_BEGIN( "update character state", HK_NULL );

m_characterContext->update(input, output);

HK_TIMER_END();
}

//Apply the player character controller
{
HK_TIMER_BEGIN( "simulate character", HK_NULL );

// Set output velocity from state machine into character rigid body
m_characterRigidBody->setLinearVelocity(output.m_velocity, m_timestep);

HK_TIMER_END();

m_world->unlock();
}

// Display state
{
hkpCharacterStateType state = m_characterContext->getState();
char * stateStr;

switch (state)
{
case HK_CHARACTER_ON_GROUND:
stateStr = "On Ground"; break;
&nbs
p; case HK_CHARACTER_JUMPING:
stateStr = "Jumping"; break;
case HK_CHARACTER_IN_AIR:
stateStr = "In Air"; break;
case HK_CHARACTER_CLIMBING:
stateStr = "Climbing"; break;
default:
stateStr = "Other"; break;
}
char buffer[255];
hkString::snprintf(buffer, 255, "State : %s", stateStr);
//m_env->m_textDisplay->outputText(buffer, 20, 270, 0xffffffff);
}

//
// Handle crouching (only for capsule)
//
{
m_world->lock();
hkBool wantCrouch = false;

hkBool isCrouching = (m_characterRigidBody->getRigidBody()->getCollidable()->getShape() == m_crouchShape);

// We want to stand
if (isCrouching && !wantCrouch)
{
m_characterRigidBody->getRigidBody()->setShape(m_standShape);
}

// We want to crouch
if (!isCrouching && wantCrouch)
{
m_characterRigidBody->getRigidBody()->setShape(m_crouchShape);
}

m_world->unlock();
}
}

I've tried various things but haven't been able to pin down exactly why its crashing.
Thanks in advance for any help

8 posts / 0 new
Last post
For more complete information about compiler optimizations, see our Optimization Notice.

Hi Aglet,

Access checks are only part of multithreaded debugging and are compiled out for release for performance reasons. This access check is happening between the world and the simulation island one of the two rigid bodies are in. This seems to a problem with your world being locked by another thread when you are trying to access it to do the callback. You could try running in single threaded mode to confirm this.

One other thing to look at also is that "delete this" in the update method. It might be better to remove its reference and let the physics system remove it when it is safe.

I hope this helps. Let me know if you are still having trouble with this.

Thanks,
Sean

Developer Support Engineer Havok www.havok.com

Thanks Sean, I've narrowed down the crash to these lines of code

// Display state
{
hkpCharacterStateType state = m_characterContext->getState();
char * stateStr;

switch (state)
{
case HK_CHARACTER_ON_GROUND:
stateStr = "On Ground"; break;
case HK_CHARACTER_JUMPING:
stateStr = "Jumping"; break;
case HK_CHARACTER_IN_AIR:
stateStr = "In Air"; break;
case HK_CHARACTER_CLIMBING:
stateStr = "Climbing"; break;
default:
stateStr = "Other"; break;
}
char buffer[255];
hkString::snprintf(buffer, 255, "State : %s", stateStr);
//m_env->m_textDisplay->outputText(buffer, 20, 270, 0xffffffff);
}

//
// Handle crouching (only for capsule)
//
{
m_world->lock(); WRITE("locked");
hkBool wantCrouch = false; //( m_env->m_window->getMouse().getButtonState() & HKG_MOUSE_RIGHT_BUTTON )|| (m_env->m_gamePad->getButtonState() & HKG_PAD_BUTTON_2);

hkBool isCrouching = (m_characterRigidBody->getRigidBody()->getCollidable()->getShape() == m_crouchShape);

// We want to stand
if (isCrouching && !wantCrouch)
{
m_characterRigidBody->getRigidBody()->setShape(m_standShape);
}

// We want to crouch
if (!isCrouching && wantCrouch)
{
&nb
sp; m_characterRigidBody->getRigidBody()->setShape(m_crouchShape);
}

m_world->unlock(); WRITE("unlocked");
}

if either of these two sections are included I get the crash after a period of time. The world is unlocked when I get the crash. The only thing I notice that I'm doing obviously different from the demo is that I update my physics after these sections in a totally different class instead of updating before them like in the demo. Would this be the reason for my problem? With these two sections commented out it runs normally.

I also get the same debug access check problem very soon after adding a new instance of the object to the world while it's running. Adding instances to the world before I start my main loop seems to have no problem.

Actually it looks like I still have the problem it just takes longer to show up unfortunately. Setting multithreading to only work on one thread seems to stop the problem, but I have no idea why it shows up with multiple threads as all I'm doing to update my physics is:

// This must be called single threaded here, prior to calling startStepWorld() below.
physicsWorld->resetThreadTokens();

// This will signal all threads which are in the wait state, to start processing stepDeltaTime() concurrently.
multithreadingUtil->startStepWorld( timestep);

// We can now do other things in this thread if we wish, while the physics runs in separate threads.
// The call below will block until all physics processing the timestep is finished in all threads.
multithreadingUtil->waitForStepWorldFinished();

stepVisualDebugger( vDebug, multithreadingUtil, context );

Hi Aglet,

How are you running multithreading? Are you doing graphics in one thread and physics in others? I just want to understand the kind of multithreading model you are using. It might also be time to send me your code so i can try to repro it (if it isn't anything that you mind posting in the forum). Let me know what you think.

Thanks,
Sean

Developer Support Engineer Havok www.havok.com

At the moment I'm doing nothing at all with the multithreading. I run all my other functions afterwards. I'm aiming to have the graphics rendering and physics in separate threads if I can get this controllable entity problem fixed.

physicsWorld->resetThreadTokens();
multithreadingUtil->startStepWorld( timestep);
multithreadingUtil->waitForStepWorldFinished();
(Update all other functions)

Unfortunately I can't really send all my code, but I could send the controllable entity code if it would make things any clearer. It's basically identical to the ControlCharacterRbDemo code. Your help is much appreciated

Hi Aglet,

Looks like you hit an actual issue with Havok. There are a couple of things you can do. You can disable this assert with hkError::getInstance().setEnabled(0xf02e32e1, false); or you can try using the character proxy instead of the character rigid body. The character rigid body controller still has a few issues that need to be worked out. One thing to look out for though with disabling the assert is that it might cause problems later on if you don't hit the assert when you are doing a real thread-unsafe operation.

Let me know if this works for you.

Thanks,
Sean

Developer Support Engineer Havok www.havok.com

Thanks Sean for all your help. I'll be trying out disabling the assert for the moment but I'll move to a character proxy if I end up needing the asserts to check stuff out.

Login to leave a comment.