forked from TrueCloudLab/restic
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:
parent
5d9b0d894e
commit
2dd6769429
1 changed files with 11 additions and 7 deletions
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue