.gitignore added
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / honnef.co / go / tools@v0.1.1 / internal / robustio / robustio_flaky.go
1 // Copyright 2019 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 // +build windows darwin
6
7 package robustio
8
9 import (
10         "errors"
11         "io/ioutil"
12         "math/rand"
13         "os"
14         "syscall"
15         "time"
16 )
17
18 const arbitraryTimeout = 2000 * time.Millisecond
19
20 // retry retries ephemeral errors from f up to an arbitrary timeout
21 // to work around filesystem flakiness on Windows and Darwin.
22 func retry(f func() (err error, mayRetry bool)) error {
23         var (
24                 bestErr     error
25                 lowestErrno syscall.Errno
26                 start       time.Time
27                 nextSleep   time.Duration = 1 * time.Millisecond
28         )
29         for {
30                 err, mayRetry := f()
31                 if err == nil || !mayRetry {
32                         return err
33                 }
34
35                 var errno syscall.Errno
36                 if errors.As(err, &errno) && (lowestErrno == 0 || errno < lowestErrno) {
37                         bestErr = err
38                         lowestErrno = errno
39                 } else if bestErr == nil {
40                         bestErr = err
41                 }
42
43                 if start.IsZero() {
44                         start = time.Now()
45                 } else if d := time.Since(start) + nextSleep; d >= arbitraryTimeout {
46                         break
47                 }
48                 time.Sleep(nextSleep)
49                 nextSleep += time.Duration(rand.Int63n(int64(nextSleep)))
50         }
51
52         return bestErr
53 }
54
55 // rename is like os.Rename, but retries ephemeral errors.
56 //
57 // On Windows it wraps os.Rename, which (as of 2019-06-04) uses MoveFileEx with
58 // MOVEFILE_REPLACE_EXISTING.
59 //
60 // Windows also provides a different system call, ReplaceFile,
61 // that provides similar semantics, but perhaps preserves more metadata. (The
62 // documentation on the differences between the two is very sparse.)
63 //
64 // Empirical error rates with MoveFileEx are lower under modest concurrency, so
65 // for now we're sticking with what the os package already provides.
66 func rename(oldpath, newpath string) (err error) {
67         return retry(func() (err error, mayRetry bool) {
68                 err = os.Rename(oldpath, newpath)
69                 return err, isEphemeralError(err)
70         })
71 }
72
73 // readFile is like ioutil.ReadFile, but retries ephemeral errors.
74 func readFile(filename string) ([]byte, error) {
75         var b []byte
76         err := retry(func() (err error, mayRetry bool) {
77                 b, err = ioutil.ReadFile(filename)
78
79                 // Unlike in rename, we do not retry errFileNotFound here: it can occur
80                 // as a spurious error, but the file may also genuinely not exist, so the
81                 // increase in robustness is probably not worth the extra latency.
82                 return err, isEphemeralError(err) && !errors.Is(err, errFileNotFound)
83         })
84         return b, err
85 }
86
87 func removeAll(path string) error {
88         return retry(func() (err error, mayRetry bool) {
89                 err = os.RemoveAll(path)
90                 return err, isEphemeralError(err)
91         })
92 }