1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
|
package main
import (
"bytes"
"flag"
"fmt"
"io/ioutil"
"os"
"strings"
"sync/atomic"
"syscall"
)
const fileCount = 100
type stats struct {
renameOk int
renameError int
readOk int
readError int
readContentMismatch int
}
func usage() {
fmt.Printf(`atomicrename creates %d "src" files in the current directory, renames
them in random order over a single "dst" file while reading the "dst"
file concurrently in a loop.
Progress and errors are reported as they occur in addition to a summary
printed at the end. cifs and fuse filesystems are known to fail, local
filesystems and nfs seem ok.
See https://github.com/hanwen/go-fuse/issues/398 for background info.
`, fileCount)
os.Exit(1)
}
func main() {
flag.Usage = usage
flag.Parse()
hello := []byte("hello world")
srcFiles := make(map[string]struct{})
// prepare source files
fmt.Print("creating files")
for i := 0; i < fileCount; i++ {
srcName := fmt.Sprintf("src.atomicrename.%d", i)
srcFiles[srcName] = struct{}{}
buf := bytes.Repeat([]byte("_"), i)
buf = append(buf, hello...)
if err := ioutil.WriteFile(srcName, buf, 0600); err != nil {
panic(err)
}
fmt.Print(".")
}
fmt.Print("\n")
// prepare destination file
const dstName = "dst.atomicrename"
if err := ioutil.WriteFile(dstName, hello, 0600); err != nil {
panic(err)
}
var running int32 = 1
stats := stats{}
// read thread
go func() {
for atomic.LoadInt32(&running) == 1 {
have, err := ioutil.ReadFile(dstName)
if err != nil {
fmt.Println(err)
stats.readError++
continue
}
if !strings.HasSuffix(string(have), string(hello)) {
fmt.Printf("content mismatch: have %q\n", have)
stats.readContentMismatch++
continue
}
fmt.Printf("content ok len=%d\n", len(have))
stats.readOk++
}
}()
// rename thread = main thread
for srcName := range srcFiles {
if err := os.Rename(srcName, dstName); err != nil {
fmt.Println(err)
stats.renameError++
}
stats.renameOk++
}
// Signal the Read goroutine to stop when loop is done
atomic.StoreInt32(&running, 0)
syscall.Unlink(dstName)
fmt.Printf("stats: %#v\n", stats)
}
|