mirror of
https://github.com/golang/go
synced 2024-11-19 06:14:39 -07:00
a00916dd39
The first deadlock involved differing mutex acquisition order in two code paths: 1. loadParseTypecheck() holds the "mcache" mutex then eventually acquires the "handleMu" file mutex. 2. (*goFile).invalidateContent() acquires the "handleMu" mutex first and then the "mcache" mutex. Fix by changing the acquisition order in invalidateContent(). The second deadlock involved the file watcher. The two code paths involved were: 1. (*goFile).GetPackages() holds the view mutex and eventually calls (*WatchMap).Watch, which acquires the watcher mutex. 2. (*session).openOverlay acquires the watcher mutex as it triggers a file's callbacks, and then the callback "(*goFile).invalidateContent" acquires the view mutex. Fix by not holding the watcher mutex as we invoke the callbacks. Fixes golang/go#32910 Change-Id: I9d060e0d80fd86a317a1d6c7aaa736a8ce10bd07 GitHub-Last-Rev: 04944fa0249c0e6f1022a415787e23abce21bc2e GitHub-Pull-Request: golang/tools#129 Reviewed-on: https://go-review.googlesource.com/c/tools/+/184880 Reviewed-by: Rebecca Stambler <rstambler@golang.org> Run-TryBot: Rebecca Stambler <rstambler@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org>
63 lines
1.3 KiB
Go
63 lines
1.3 KiB
Go
// Copyright 2019 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package cache
|
|
|
|
import (
|
|
"sync"
|
|
)
|
|
|
|
type watcher struct {
|
|
id uint64
|
|
callback func()
|
|
}
|
|
|
|
type WatchMap struct {
|
|
mu sync.Mutex
|
|
nextID uint64
|
|
watchers map[interface{}][]watcher
|
|
}
|
|
|
|
func NewWatchMap() *WatchMap {
|
|
return &WatchMap{watchers: make(map[interface{}][]watcher)}
|
|
}
|
|
func (w *WatchMap) Watch(key interface{}, callback func()) func() {
|
|
w.mu.Lock()
|
|
defer w.mu.Unlock()
|
|
id := w.nextID
|
|
w.nextID++
|
|
w.watchers[key] = append(w.watchers[key], watcher{
|
|
id: id,
|
|
callback: callback,
|
|
})
|
|
return func() {
|
|
// unwatch if invoked
|
|
w.mu.Lock()
|
|
defer w.mu.Unlock()
|
|
// find and delete the watcher entry
|
|
entries := w.watchers[key]
|
|
for i, entry := range entries {
|
|
if entry.id == id {
|
|
// found it
|
|
entries[i] = entries[len(entries)-1]
|
|
entries = entries[:len(entries)-1]
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func (w *WatchMap) Notify(key interface{}) {
|
|
// Make a copy of the watcher callbacks so we don't need to hold
|
|
// the mutex during the callbacks (to avoid deadlocks).
|
|
w.mu.Lock()
|
|
entries := w.watchers[key]
|
|
entriesCopy := make([]watcher, len(entries))
|
|
copy(entriesCopy, entries)
|
|
w.mu.Unlock()
|
|
|
|
for _, entry := range entriesCopy {
|
|
entry.callback()
|
|
}
|
|
}
|