lock: Fix possible deadlock during refresh of stale lock

A delayed lock refresh could send a signal on the `refreshed` channel
while the `monitorLockRefresh` goroutine waits for a reply to its
`refreshLockRequest`. As the channels are unbuffered, this resulted in a
deadlock.
This commit is contained in:
Michael Eischer 2023-07-15 15:28:02 +02:00
parent 5d9b0d894e
commit 2dd6769429
1 changed files with 11 additions and 7 deletions

View File

@ -213,15 +213,21 @@ func monitorLockRefresh(ctx context.Context, lockInfo *lockContext, refreshed <-
lockInfo.refreshWG.Done() lockInfo.refreshWG.Done()
}() }()
var refreshStaleLockResult chan bool
for { for {
select { select {
case <-ctx.Done(): case <-ctx.Done():
debug.Log("terminate expiry monitoring") debug.Log("terminate expiry monitoring")
return return
case <-refreshed: case <-refreshed:
if refreshStaleLockResult != nil {
// ignore delayed refresh notifications while the stale lock is refreshed
continue
}
lastRefresh = time.Now().UnixNano() lastRefresh = time.Now().UnixNano()
case <-ticker.C: case <-ticker.C:
if time.Now().UnixNano()-lastRefresh < refreshabilityTimeout.Nanoseconds() { if time.Now().UnixNano()-lastRefresh < refreshabilityTimeout.Nanoseconds() || refreshStaleLockResult != nil {
continue continue
} }
@ -229,19 +235,17 @@ func monitorLockRefresh(ctx context.Context, lockInfo *lockContext, refreshed <-
refreshReq := refreshLockRequest{ refreshReq := refreshLockRequest{
result: make(chan bool), result: make(chan bool),
} }
refreshStaleLockResult = refreshReq.result
// inform refresh goroutine about forced refresh // inform refresh goroutine about forced refresh
select { select {
case <-ctx.Done(): case <-ctx.Done():
case forceRefresh <- refreshReq: case forceRefresh <- refreshReq:
} }
var success bool case success := <-refreshStaleLockResult:
select {
case <-ctx.Done():
case success = <-refreshReq.result:
}
if success { if success {
lastRefresh = time.Now().UnixNano() lastRefresh = time.Now().UnixNano()
refreshStaleLockResult = nil
continue continue
} }