diff options
Diffstat (limited to 'lib/model/util.go')
-rw-r--r-- | lib/model/util.go | 88 |
1 files changed, 0 insertions, 88 deletions
diff --git a/lib/model/util.go b/lib/model/util.go index 54d33c5808..2fefd3100a 100644 --- a/lib/model/util.go +++ b/lib/model/util.go @@ -11,100 +11,12 @@ import ( "errors" "fmt" "path/filepath" - "strings" - "sync" "time" "github.com/prometheus/client_golang/prometheus" - "github.com/syncthing/syncthing/lib/events" "github.com/syncthing/syncthing/lib/fs" - "github.com/syncthing/syncthing/lib/ur" ) -type Holdable interface { - Holders() string -} - -func newDeadlockDetector(timeout time.Duration, evLogger events.Logger, fatal func(error)) *deadlockDetector { - return &deadlockDetector{ - warnTimeout: timeout, - fatalTimeout: 10 * timeout, - lockers: make(map[string]sync.Locker), - evLogger: evLogger, - fatal: fatal, - } -} - -type deadlockDetector struct { - warnTimeout, fatalTimeout time.Duration - lockers map[string]sync.Locker - evLogger events.Logger - fatal func(error) -} - -func (d *deadlockDetector) Watch(name string, mut sync.Locker) { - d.lockers[name] = mut - go func() { - for { - time.Sleep(d.warnTimeout / 4) - done := make(chan struct{}, 1) - - go func() { - mut.Lock() - _ = 1 // empty critical section - mut.Unlock() - done <- struct{}{} - }() - - d.watchInner(name, done) - } - }() -} - -func (d *deadlockDetector) watchInner(name string, done chan struct{}) { - warn := time.NewTimer(d.warnTimeout) - fatal := time.NewTimer(d.fatalTimeout) - defer func() { - warn.Stop() - fatal.Stop() - }() - - select { - case <-warn.C: - failure := ur.FailureDataWithGoroutines(fmt.Sprintf("potential deadlock detected at %s (short timeout)", name)) - failure.Extra["timeout"] = d.warnTimeout.String() - d.evLogger.Log(events.Failure, failure) - case <-done: - return - } - - select { - case <-fatal.C: - err := fmt.Errorf("potential deadlock detected at %s (long timeout)", name) - failure := ur.FailureDataWithGoroutines(err.Error()) - failure.Extra["timeout"] = d.fatalTimeout.String() - others := d.otherHolders() - failure.Extra["other-holders"] = others - d.evLogger.Log(events.Failure, failure) - d.fatal(err) - // Give it a minute to shut down gracefully, maybe shutting down - // can get out of the deadlock (or it's not really a deadlock). - time.Sleep(time.Minute) - panic(fmt.Sprintf("%v:\n%v", err, others)) - case <-done: - } -} - -func (d *deadlockDetector) otherHolders() string { - var b strings.Builder - for otherName, otherMut := range d.lockers { - if otherHolder, ok := otherMut.(Holdable); ok { - b.WriteString("===" + otherName + "===\n" + otherHolder.Holders() + "\n") - } - } - return b.String() -} - // inWritableDir calls fn(path), while making sure that the directory // containing `path` is writable for the duration of the call. func inWritableDir(fn func(string) error, targetFs fs.Filesystem, path string, ignorePerms bool) error { |