Skip to content

Commit

Permalink
Fix thread safety issues in UsdSkel_SkelDefinition.
Browse files Browse the repository at this point in the history
- In methods like _ComputeJointWorldInverseBindTransforms(), check the
  compute flag again after acquiring the lock to avoid potentially
  recomputing the result again if multiple threads were waiting on the
  mutex. Although the computed result would not change, it is
  not safe to call mutable member functions of the VtArray (which can
  cause a copy-on-write detach) while other threads may be in the middle
  of making a copy of it.

- Prefer using operator|= to atomically set the flag rather than
  doing a read -> bitwise OR -> atomic store sequence which could cause
  flags to be lost if there are concurrent writes.
  Currently the writes are all guarded by the same mutex so the previous
  approach was not problematic, but the new approach is safer if e.g. in the
  future there are separate locks for each cached array.

Bug: PixarAnimationStudios#1742
  • Loading branch information
cameronwhite committed Mar 28, 2023
1 parent 39148ce commit d2af1a4
Showing 1 changed file with 29 additions and 18 deletions.
47 changes: 29 additions & 18 deletions pxr/usd/usdSkel/skelDefinition.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -244,19 +244,21 @@ UsdSkel_SkelDefinition::_ComputeJointSkelRestTransforms()
if (TF_VERIFY(GetJointLocalRestTransforms(&jointLocalRestXforms))) {

std::lock_guard<std::mutex> lock(_mutex);

VtArray<Matrix4>& skelXforms = _jointSkelRestXforms.Get<Matrix4>();
skelXforms.resize(_topology.size());

const bool success =
UsdSkelConcatJointTransforms(_topology, jointLocalRestXforms,
skelXforms);
if (!(_flags & ComputeFlag)) {
VtArray<Matrix4>& skelXforms = _jointSkelRestXforms.Get<Matrix4>();
skelXforms.resize(_topology.size());

// XXX: Topology was validated when the definition was constructed,
/// so this should not have failed.
TF_VERIFY(success);
const bool success =
UsdSkelConcatJointTransforms(_topology, jointLocalRestXforms,
skelXforms);

_flags = _flags|ComputeFlag;
// XXX: Topology was validated when the definition was constructed,
/// so this should not have failed.
TF_VERIFY(success);

_flags |= ComputeFlag;
}

return true;
}
Expand Down Expand Up @@ -360,11 +362,18 @@ UsdSkel_SkelDefinition::_ComputeJointWorldInverseBindTransforms()

std::lock_guard<std::mutex> lock(_mutex);

_InvertTransforms<Matrix4>(
jointWorldBindXforms,
&_jointWorldInverseBindXforms.Get<Matrix4>());
// Check the flag again after acquiring the lock to avoid re-computing.
// It is not safe to call mutating member functions that can cause a
// copy-on-write detach while other threads may be making copies of the
// array.
if (!(_flags & ComputeFlag)) {
_InvertTransforms<Matrix4>(
jointWorldBindXforms,
&_jointWorldInverseBindXforms.Get<Matrix4>());

_flags |= ComputeFlag;
}

_flags = _flags|ComputeFlag;
return true;
}
return false;
Expand Down Expand Up @@ -430,11 +439,13 @@ UsdSkel_SkelDefinition::_ComputeJointLocalInverseRestTransforms()

std::lock_guard<std::mutex> lock(_mutex);

_InvertTransforms<Matrix4>(
jointLocalRestXforms,
&_jointLocalInverseRestXforms.Get<Matrix4>());
if (!(_flags & ComputeFlag)) {
_InvertTransforms<Matrix4>(
jointLocalRestXforms,
&_jointLocalInverseRestXforms.Get<Matrix4>());

_flags = _flags|ComputeFlag;
_flags |= ComputeFlag;
}

return true;
}
Expand Down

0 comments on commit d2af1a4

Please sign in to comment.