You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@iceberg.apache.org by ze...@apache.org on 2026/07/16 17:51:21 UTC
(iceberg-go) branch main updated: fix(io): avoid deadlock in MemFS WalkDir callbacks (#1485)
This is an automated email from the ASF dual-hosted git repository.
zeroshade pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/iceberg-go.git
The following commit(s) were added to refs/heads/main by this push:
new 04f7c734 fix(io): avoid deadlock in MemFS WalkDir callbacks (#1485)
04f7c734 is described below
commit 04f7c734dbffb8b46625044a877c07088a6ca8c8
Author: Minh Vu <vu...@gmail.com>
AuthorDate: Thu Jul 16 19:51:16 2026 +0200
fix(io): avoid deadlock in MemFS WalkDir callbacks (#1485)
---
io/mem.go | 21 +++++++++++++++++----
io/mem_test.go | 36 ++++++++++++++++++++++++++++++++++++
2 files changed, 53 insertions(+), 4 deletions(-)
diff --git a/io/mem.go b/io/mem.go
index 1e4c3d64..27640059 100644
--- a/io/mem.go
+++ b/io/mem.go
@@ -106,16 +106,29 @@ func (m *MemFS) WriteFile(name string, content []byte) error {
}
func (m *MemFS) WalkDir(root string, fn fs.WalkDirFunc) error {
- m.mu.RLock()
- defer m.mu.RUnlock()
+ type walkEntry struct {
+ path string
+ size int64
+ }
root = strings.TrimRight(root, "/")
+
+ m.mu.RLock()
+ var entries []walkEntry
for key, data := range m.files {
if !memPathInRoot(key, root) {
continue
}
- info := &memFileInfo{name: filepath.Base(key), size: int64(len(data))}
- if err := fn(key, fs.FileInfoToDirEntry(info), nil); err != nil {
+ entries = append(entries, walkEntry{
+ path: key,
+ size: int64(len(data)),
+ })
+ }
+ m.mu.RUnlock()
+
+ for _, entry := range entries {
+ info := &memFileInfo{name: filepath.Base(entry.path), size: entry.size}
+ if err := fn(entry.path, fs.FileInfoToDirEntry(info), nil); err != nil {
return err
}
}
diff --git a/io/mem_test.go b/io/mem_test.go
index 02cc4a63..bd1e8519 100644
--- a/io/mem_test.go
+++ b/io/mem_test.go
@@ -22,6 +22,7 @@ import (
"io"
"io/fs"
"testing"
+ "time"
icebergio "github.com/apache/iceberg-go/io"
"github.com/stretchr/testify/assert"
@@ -200,3 +201,38 @@ func TestMemIO_WalkDirDoesNotIncludeSiblingPrefixes(t *testing.T) {
})
}
}
+
+func TestMemIO_WalkDirCallbackCanRemoveFiles(t *testing.T) {
+ memIO := icebergio.NewMemFS()
+ require.NoError(t, memIO.WriteFile("mem://bucket/root/1.txt", []byte("1")))
+ require.NoError(t, memIO.WriteFile("mem://bucket/root/2.txt", []byte("22")))
+
+ done := make(chan error, 1)
+ go func() {
+ done <- memIO.WalkDir("mem://bucket/root", func(path string, _ fs.DirEntry, err error) error {
+ if err != nil {
+ return err
+ }
+
+ return memIO.Remove(path)
+ })
+ }()
+
+ select {
+ case err := <-done:
+ require.NoError(t, err)
+ case <-time.After(2 * time.Second):
+ t.Fatal("WalkDir deadlocked when its callback removed a file")
+ }
+
+ var remaining []string
+ require.NoError(t, memIO.WalkDir("mem://bucket/root", func(path string, _ fs.DirEntry, err error) error {
+ if err != nil {
+ return err
+ }
+ remaining = append(remaining, path)
+
+ return nil
+ }))
+ assert.Empty(t, remaining)
+}