From 3304b0fcf0f1348db0cf569ecf0b467191edba2b Mon Sep 17 00:00:00 2001 From: George Armhold Date: Sat, 28 Oct 2017 18:39:56 -0400 Subject: [PATCH] prevent deadlock in List() for B2 when b2.connections=1 This is a fix for the following situation (gh-1188): List() grabs a semaphore token upon entry, starts a goroutine, and does not release the token until the routine exits (via a defer). The goroutine iterates over the results from ListCurrentObjects(), sending them one at a time to a channel, where they are ultimately processed by be.Load(). Since be.Load() also needs a token, this will result in deadlock if b2.connections=1. This fix changes List() so that the token is only held during the call to ListCurrentObjects(). --- internal/backend/b2/b2.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/internal/backend/b2/b2.go b/internal/backend/b2/b2.go index a91c19b6b..cd2eb7a9c 100644 --- a/internal/backend/b2/b2.go +++ b/internal/backend/b2/b2.go @@ -307,18 +307,17 @@ func (be *b2Backend) List(ctx context.Context, t restic.FileType) <-chan string ctx, cancel := context.WithCancel(ctx) - be.sem.GetToken() - go func() { defer close(ch) defer cancel() - defer be.sem.ReleaseToken() prefix := be.Dirname(restic.Handle{Type: t}) cur := &b2.Cursor{Prefix: prefix} for { + be.sem.GetToken() objs, c, err := be.bucket.ListCurrentObjects(ctx, be.listMaxItems, cur) + be.sem.ReleaseToken() if err != nil && err != io.EOF { return }