From b6848a3edb833af7b6749da9d3a9dc716e96d517 Mon Sep 17 00:00:00 2001 From: Nick Craig-Wood Date: Thu, 19 Jan 2017 18:01:31 +0000 Subject: [PATCH] Fix race in Lister.Finished which was causing the tests to be unreliable --- fs/lister.go | 38 ++++++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/fs/lister.go b/fs/lister.go index a9fe4011e..1d4ffcf2e 100644 --- a/fs/lister.go +++ b/fs/lister.go @@ -11,16 +11,16 @@ type listerResult struct { Err error } -// Lister objects are used for conniltrolling listing of Fs objects +// Lister objects are used for controlling listing of Fs objects type Lister struct { - mu sync.RWMutex - buffer int - abort bool - results chan listerResult - finished sync.Once - level int - filter *Filter - err error + mu sync.RWMutex + buffer int + abort bool + results chan listerResult + closeOnce sync.Once + level int + filter *Filter + err error } // NewLister creates a Lister object. @@ -172,6 +172,15 @@ func (o *Lister) IncludeDirectory(remote string) bool { return o.filter.IncludeDirectory(remote) } +// finished closes the results channel and sets abort - must be called +// with o.mu held. +func (o *Lister) finished() { + o.closeOnce.Do(func() { + close(o.results) + o.abort = true + }) +} + // SetError will set an error state, and will cause the listing to // be aborted. // Multiple goroutines can set the error state concurrently, @@ -181,19 +190,16 @@ func (o *Lister) SetError(err error) { if err != nil && !o.abort { o.err = err o.results <- listerResult{Err: err} + o.finished() } o.mu.Unlock() - o.Finished() } // Finished should be called when listing is finished func (o *Lister) Finished() { - o.finished.Do(func() { - o.mu.Lock() - o.abort = true - close(o.results) - o.mu.Unlock() - }) + o.mu.Lock() + o.finished() + o.mu.Unlock() } // IsFinished returns whether the directory listing is finished or not