tweak to locking which should avoid stall/deadlock w/ mutex check

The view.go change is straightforward and fairly obviously more
correct.

The field.go change avoids holding the field read lock for the
duration of the mutex check request. The thinking was that while the
read lock was held something else was attempting to get a write lock,
which blocked all other read locks and something was getting into a
loop. Seebs might have a more detailed explanation, but that's as far
as my understanding goes at the moment. I believe this change is safe
though as we don't read/modify any field level data structures after
grabbing the standard view.
This commit is contained in:
Matthew Jaffee 2021-09-14 17:00:00 -05:00 committed by Seebs
parent e7e3331fb4
commit 3e222d8771
2 changed files with 9 additions and 3 deletions

View file

@ -1098,9 +1098,15 @@ func (f *Field) MutexCheck(ctx context.Context, qcx *Qcx, details bool, limit in
if f.Type() != FieldTypeMutex {
return nil, errors.New("mutex check only valid for mutex fields")
}
// Rather than deferring the unlock, we grab the standard view
// from the field's viewMap and unlock immediately. This avoids
// holding the rlock for a potentially long time which blocks any
// write lock, and pending write locks block other read locks.
f.mu.RLock()
defer f.mu.RUnlock()
standard := f.viewMap[viewStandard]
f.mu.RUnlock()
if standard == nil {
// no standard view present means we've never needed to create it,
// so it has no bits set, so it has no extra bits set.

View file

@ -300,8 +300,8 @@ func (v *view) Fragment(shard uint64) *fragment {
// allFragments returns a list of all fragments in the view.
func (v *view) allFragments() []*fragment {
v.mu.Lock()
defer v.mu.Unlock()
v.mu.RLock()
defer v.mu.RUnlock()
other := make([]*fragment, 0, len(v.fragments))
for _, fragment := range v.fragments {