From 06ae4258be1833968e9d3d3c07daa726f3a3f086 Mon Sep 17 00:00:00 2001
From: Nick Craig-Wood <nick@craig-wood.com>
Date: Wed, 3 Oct 2018 21:46:18 +0100
Subject: [PATCH] cmd: Fix -P not ending with a new line

Before this fix rclone didn't wait for the stats to be finished before
exiting, so the final new line was never printed.

After this change rclone will wait for the stats routine to cease
before exiting.
---
 cmd/cmd.go            | 48 ++++++++++++++++++++++++-------------------
 cmd/mountlib/mount.go |  3 +--
 cmd/progress.go       | 12 ++++++++---
 3 files changed, 37 insertions(+), 26 deletions(-)

diff --git a/cmd/cmd.go b/cmd/cmd.go
index 46f0ae2d7..1f0189e2f 100644
--- a/cmd/cmd.go
+++ b/cmd/cmd.go
@@ -17,6 +17,7 @@ import (
 	"runtime/pprof"
 	"strconv"
 	"strings"
+	"sync"
 	"time"
 
 	"github.com/pkg/errors"
@@ -293,7 +294,7 @@ func ShowStats() bool {
 // Run the function with stats and retries if required
 func Run(Retry bool, showStats bool, cmd *cobra.Command, f func() error) {
 	var err error
-	var stopStats chan struct{}
+	stopStats := func() {}
 	if !showStats && ShowStats() {
 		showStats = true
 	}
@@ -331,9 +332,7 @@ func Run(Retry bool, showStats bool, cmd *cobra.Command, f func() error) {
 			time.Sleep(*retriesInterval)
 		}
 	}
-	if showStats {
-		close(stopStats)
-	}
+	stopStats()
 	if err != nil {
 		log.Printf("Failed to %s: %v", cmd.Name(), err)
 		resolveExitCode(err)
@@ -384,24 +383,31 @@ func CheckArgs(MinArgs, MaxArgs int, cmd *cobra.Command, args []string) {
 
 // StartStats prints the stats every statsInterval
 //
-// It returns a channel which should be closed to stop the stats.
-func StartStats() chan struct{} {
-	stopStats := make(chan struct{})
-	if *statsInterval > 0 {
-		go func() {
-			ticker := time.NewTicker(*statsInterval)
-			for {
-				select {
-				case <-ticker.C:
-					accounting.Stats.Log()
-				case <-stopStats:
-					ticker.Stop()
-					return
-				}
-			}
-		}()
+// It returns a func which should be called to stop the stats.
+func StartStats() func() {
+	if *statsInterval <= 0 {
+		return func() {}
+	}
+	stopStats := make(chan struct{})
+	var wg sync.WaitGroup
+	wg.Add(1)
+	go func() {
+		defer wg.Done()
+		ticker := time.NewTicker(*statsInterval)
+		for {
+			select {
+			case <-ticker.C:
+				accounting.Stats.Log()
+			case <-stopStats:
+				ticker.Stop()
+				return
+			}
+		}
+	}()
+	return func() {
+		close(stopStats)
+		wg.Wait()
 	}
-	return stopStats
 }
 
 // initConfig is run by cobra after initialising the flags
diff --git a/cmd/mountlib/mount.go b/cmd/mountlib/mount.go
index f3fe2ae4f..231923ee6 100644
--- a/cmd/mountlib/mount.go
+++ b/cmd/mountlib/mount.go
@@ -226,8 +226,7 @@ be copied to the vfs cache before opening with --vfs-cache-mode full.
 
 			// Show stats if the user has specifically requested them
 			if cmd.ShowStats() {
-				stopStats := cmd.StartStats()
-				defer close(stopStats)
+				defer cmd.StartStats()()
 			}
 
 			// Skip checkMountEmpty if --allow-non-empty flag is used or if
diff --git a/cmd/progress.go b/cmd/progress.go
index d2873f942..fe1a68be0 100644
--- a/cmd/progress.go
+++ b/cmd/progress.go
@@ -25,8 +25,8 @@ const (
 
 // startProgress starts the progress bar printing
 //
-// It returns a channel which should be closed to stop the stats.
-func startProgress() chan struct{} {
+// It returns a func which should be called to stop the stats.
+func startProgress() func() {
 	stopStats := make(chan struct{})
 	oldLogPrint := fs.LogPrint
 	if !log.Redirected() {
@@ -36,7 +36,10 @@ func startProgress() chan struct{} {
 
 		}
 	}
+	var wg sync.WaitGroup
+	wg.Add(1)
 	go func() {
+		defer wg.Done()
 		progressInterval := defaultProgressInterval
 		if ShowStats() && *statsInterval > 0 {
 			progressInterval = *statsInterval
@@ -54,7 +57,10 @@ func startProgress() chan struct{} {
 			}
 		}
 	}()
-	return stopStats
+	return func() {
+		close(stopStats)
+		wg.Wait()
+	}
 }
 
 // VT100 codes