march: fix deadlock when using --no-traverse - fixes #8656

This ocurred whenever there were more than 100 files in the source due
to the output channel filling up.

The fix is not to use list.NewSorter but take more care to output the
dst objects in the same order the src objects are delivered. As the
src objects are delivered sorted, no sorting is needed.

In order not to cause another deadlock, we need to send nil dst
objects which is safe since this adjusts the termination conditions
for the channels.

Thanks to @jeremy for the test script the Go tests are based on.
This commit is contained in:
Nick Craig-Wood
2025-07-02 10:26:42 +01:00
parent f904378c4d
commit 88e30eecbf
2 changed files with 85 additions and 29 deletions

View File

@ -216,6 +216,35 @@ func TestCopyNoTraverse(t *testing.T) {
r.CheckRemoteItems(t, file1)
}
func TestCopyNoTraverseDeadlock(t *testing.T) {
r := fstest.NewRun(t)
if !r.Fremote.Features().IsLocal {
t.Skip("Only runs on local")
}
const nFiles = 200
t1 := fstest.Time("2001-02-03T04:05:06.499999999Z")
// Create lots of source files.
items := make([]fstest.Item, nFiles)
for i := range items {
name := fmt.Sprintf("file%d.txt", i)
items[i] = r.WriteFile(name, fmt.Sprintf("content%d", i), t1)
}
r.CheckLocalItems(t, items...)
// Set --no-traverse
ctx, ci := fs.AddConfig(context.Background())
ci.NoTraverse = true
// Initial copy to establish destination.
require.NoError(t, CopyDir(ctx, r.Fremote, r.Flocal, false))
r.CheckRemoteItems(t, items...)
// Second copy which shouldn't deadlock
require.NoError(t, CopyDir(ctx, r.Flocal, r.Fremote, false))
r.CheckRemoteItems(t, items...)
}
// Now with --check-first
func TestCopyCheckFirst(t *testing.T) {
ctx := context.Background()