Other(refactor): Sort safe.Mutex types before locking to prevent deadlocks

This change implements safe.Mutex and safe.RWMutex, which wrap the
sync.Mutex and sync.RWMutex types and are assigned a globally unique
integer ID. The safe.Lock and safe.RLock methods sort the mutexes
by this integer ID before locking to ensure that locks for a given
set of mutexes are always performed in the same order, avoiding
deadlocks.
This commit is contained in:
James Houlahan
2022-10-27 01:21:40 +02:00
parent 5a4f733518
commit d4da325e57
11 changed files with 133 additions and 61 deletions

View File

@ -52,7 +52,7 @@ type Bridge struct {
// users holds authorized users.
users map[string]*user.User
usersLock sync.RWMutex
usersLock safe.RWMutex
// api manages user API clients.
api *liteapi.Manager
@ -226,7 +226,9 @@ func newBridge(
bridge := &Bridge{
vault: vault,
users: make(map[string]*user.User),
users: make(map[string]*user.User),
usersLock: safe.NewRWMutex(),
api: api,
proxyCtl: proxyCtl,
@ -363,7 +365,7 @@ func (bridge *Bridge) Close(ctx context.Context) {
for _, user := range bridge.users {
user.Close()
}
}, &bridge.usersLock)
}, bridge.usersLock)
// Stop all ongoing tasks.
bridge.tasks.Wait()
@ -433,7 +435,7 @@ func (bridge *Bridge) onStatusUp() {
for _, user := range bridge.users {
user.OnStatusUp()
}
}, &bridge.usersLock)
}, bridge.usersLock)
bridge.goLoad()
}
@ -445,7 +447,7 @@ func (bridge *Bridge) onStatusDown() {
for _, user := range bridge.users {
user.OnStatusDown()
}
}, &bridge.usersLock)
}, bridge.usersLock)
bridge.tasks.Once(func(ctx context.Context) {
backoff := time.Second