Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@itchyny
Created January 21, 2019 07:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save itchyny/4a9dc591cafedf27417acf8cd8bf66f8 to your computer and use it in GitHub Desktop.
Save itchyny/4a9dc591cafedf27417acf8cd8bf66f8 to your computer and use it in GitHub Desktop.
Atomic write testing in Golang (why this fails on Windows?)
package main
import (
"bytes"
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
"golang.org/x/sync/errgroup"
)
func main() {
if err := run(); err != nil {
fmt.Println(err)
os.Exit(1)
}
fmt.Printf("OK\n")
}
func run() error {
f, err := ioutil.TempFile("", "atomic-test")
if err != nil {
return err
}
f.Close()
os.Remove(f.Name())
defer os.Remove(f.Name())
contents := bytes.Repeat([]byte("abcde"), 100)
var eg errgroup.Group
for i := 0; i < 10; i++ {
eg.Go(func() error {
return writeFile(f.Name(), bytes.NewReader(contents))
})
}
if err := eg.Wait(); err != nil {
return err
}
written, err := ioutil.ReadFile(f.Name())
if err != nil {
return err
}
if string(written) != string(contents) {
return fmt.Errorf("file contents should be %q but got %q", string(contents), string(written))
}
return nil
}
func writeFile(name string, r io.Reader) error {
dir, base := filepath.Split(name)
f, err := ioutil.TempFile(dir, base)
if err != nil {
return err
}
defer os.Remove(f.Name())
if _, err := io.Copy(f, r); err != nil {
return err
}
f.Close()
return os.Rename(f.Name(), name)
}
@itchyny
Copy link
Author

itchyny commented Jan 21, 2019

Note that atomic write ensures it does not create a halfway file but not guarantees that all the multiple write request to be succeeded. Rename can fail, that's all.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment