fs: Fix interaction between --progress and --interactive

Before this change if both --progress and --interactive were set then
the screen display could become muddled.

This change makes --progress and --interactive use the same lock so
while rclone is asking for interactive questions, the progress will be
paused.

Fixes #6755
This commit is contained in:
Nick Craig-Wood 2023-03-03 14:17:02 +00:00
parent cdfa0beafb
commit e042d9089f
3 changed files with 22 additions and 12 deletions

View File

@ -75,14 +75,13 @@ func startProgress() func() {
// state for the progress printing // state for the progress printing
var ( var (
nlines = 0 // number of lines in the previous stats block nlines = 0 // number of lines in the previous stats block
progressMu sync.Mutex
) )
// printProgress prints the progress with an optional log // printProgress prints the progress with an optional log
func printProgress(logMessage string) { func printProgress(logMessage string) {
progressMu.Lock() operations.StdoutMutex.Lock()
defer progressMu.Unlock() defer operations.StdoutMutex.Unlock()
var buf bytes.Buffer var buf bytes.Buffer
w, _ := terminal.GetSize() w, _ := terminal.GetSize()

View File

@ -46,6 +46,9 @@ func ReadNonEmptyLine(prompt string) string {
// CommandDefault - choose one. If return is pressed then it will // CommandDefault - choose one. If return is pressed then it will
// chose the defaultIndex if it is >= 0 // chose the defaultIndex if it is >= 0
//
// Must not call fs.Log anything from here to avoid deadlock in
// --interactive --progress
func CommandDefault(commands []string, defaultIndex int) byte { func CommandDefault(commands []string, defaultIndex int) byte {
opts := []string{} opts := []string{}
for i, text := range commands { for i, text := range commands {

View File

@ -836,8 +836,8 @@ func ListFn(ctx context.Context, f fs.Fs, fn func(fs.Object)) error {
}) })
} }
// mutex for synchronized output // StdoutMutex mutex for synchronized output on stdout
var outMutex sync.Mutex var StdoutMutex sync.Mutex
// SyncPrintf is a global var holding the Printf function used in syncFprintf so that it can be overridden // SyncPrintf is a global var holding the Printf function used in syncFprintf so that it can be overridden
// Note, despite name, does not provide sync and should not be called directly // Note, despite name, does not provide sync and should not be called directly
@ -853,8 +853,8 @@ var SyncPrintf = func(format string, a ...interface{}) {
// Updated to print to terminal if no writer is defined // Updated to print to terminal if no writer is defined
// This special behavior is used to allow easier replacement of the print to terminal code by progress // This special behavior is used to allow easier replacement of the print to terminal code by progress
func syncFprintf(w io.Writer, format string, a ...interface{}) { func syncFprintf(w io.Writer, format string, a ...interface{}) {
outMutex.Lock() StdoutMutex.Lock()
defer outMutex.Unlock() defer StdoutMutex.Unlock()
if w == nil || w == os.Stdout { if w == nil || w == os.Stdout {
SyncPrintf(format, a...) SyncPrintf(format, a...)
} else { } else {
@ -2282,7 +2282,7 @@ func GetFsInfo(f fs.Fs) *FsInfo {
} }
var ( var (
interactiveMu sync.Mutex interactiveMu sync.Mutex // protects the following variables
skipped = map[string]bool{} skipped = map[string]bool{}
) )
@ -2290,14 +2290,22 @@ var (
// //
// Call with interactiveMu held // Call with interactiveMu held
func skipDestructiveChoose(ctx context.Context, subject interface{}, action string) (skip bool) { func skipDestructiveChoose(ctx context.Context, subject interface{}, action string) (skip bool) {
fmt.Printf("rclone: %s \"%v\"?\n", action, subject) // Lock the StdoutMutex - must not call fs.Log anything
switch i := config.CommandDefault([]string{ // otherwise it will deadlock with --interactive --progress
StdoutMutex.Lock()
fmt.Printf("\nrclone: %s \"%v\"?\n", action, subject)
i := config.CommandDefault([]string{
"yYes, this is OK", "yYes, this is OK",
"nNo, skip this", "nNo, skip this",
fmt.Sprintf("sSkip all %s operations with no more questions", action), fmt.Sprintf("sSkip all %s operations with no more questions", action),
fmt.Sprintf("!Do all %s operations with no more questions", action), fmt.Sprintf("!Do all %s operations with no more questions", action),
"qExit rclone now.", "qExit rclone now.",
}, 0); i { }, 0)
StdoutMutex.Unlock()
switch i {
case 'y': case 'y':
skip = false skip = false
case 'n': case 'n':