|
1 | 1 | package util
|
2 | 2 |
|
3 | 3 | import (
|
4 |
| - "errors" |
5 |
| - "fmt" |
6 | 4 | "io"
|
7 | 5 | "os"
|
8 |
| - "sync/atomic" |
| 6 | + "path/filepath" |
| 7 | + "strconv" |
| 8 | + "sync" |
9 | 9 | "time"
|
10 | 10 |
|
11 | 11 | "gopkg.in/src-d/go-billy.v3"
|
@@ -114,38 +114,58 @@ func WriteFile(fs billy.Basic, filename string, data []byte, perm os.FileMode) e
|
114 | 114 | return err
|
115 | 115 | }
|
116 | 116 |
|
117 |
| -var ( |
118 |
| - MaxTempFiles int32 = 1024 * 4 |
119 |
| - tempCount int32 |
120 |
| -) |
| 117 | +// Random number state. |
| 118 | +// We generate random temporary file names so that there's a good |
| 119 | +// chance the file doesn't exist yet - keeps the number of tries in |
| 120 | +// TempFile to a minimum. |
| 121 | +var rand uint32 |
| 122 | +var randmu sync.Mutex |
| 123 | + |
| 124 | +func reseed() uint32 { |
| 125 | + return uint32(time.Now().UnixNano() + int64(os.Getpid())) |
| 126 | +} |
| 127 | + |
| 128 | +func nextSuffix() string { |
| 129 | + randmu.Lock() |
| 130 | + r := rand |
| 131 | + if r == 0 { |
| 132 | + r = reseed() |
| 133 | + } |
| 134 | + r = r*1664525 + 1013904223 // constants from Numerical Recipes |
| 135 | + rand = r |
| 136 | + randmu.Unlock() |
| 137 | + return strconv.Itoa(int(1e9 + r%1e9))[1:] |
| 138 | +} |
121 | 139 |
|
122 | 140 | // TempFile creates a new temporary file in the directory dir with a name
|
123 | 141 | // beginning with prefix, opens the file for reading and writing, and returns
|
124 | 142 | // the resulting *os.File. If dir is the empty string, TempFile uses the default
|
125 |
| -// directory for temporary files (see os.TempDir). |
126 |
| -// |
127 |
| -// Multiple programs calling TempFile simultaneously will not choose the same |
128 |
| -// file. The caller can use f.Name() to find the pathname of the file. |
129 |
| -// |
130 |
| -// It is the caller's responsibility to remove the file when no longer needed. |
131 |
| -func TempFile(fs billy.Basic, dir, prefix string) (billy.File, error) { |
132 |
| - var fullpath string |
133 |
| - for { |
134 |
| - if tempCount >= MaxTempFiles { |
135 |
| - return nil, errors.New("max. number of tempfiles reached") |
136 |
| - } |
| 143 | +// directory for temporary files (see os.TempDir). Multiple programs calling |
| 144 | +// TempFile simultaneously will not choose the same file. The caller can use |
| 145 | +// f.Name() to find the pathname of the file. It is the caller's responsibility |
| 146 | +// to remove the file when no longer needed. |
| 147 | +func TempFile(fs billy.Basic, dir, prefix string) (f billy.File, err error) { |
| 148 | + // This implementation is based on stdlib ioutil.TempFile. |
| 149 | + |
| 150 | + if dir == "" { |
| 151 | + dir = os.TempDir() |
| 152 | + } |
137 | 153 |
|
138 |
| - fullpath = getTempFilename(fs, dir, prefix) |
| 154 | + nconflict := 0 |
| 155 | + for i := 0; i < 10000; i++ { |
| 156 | + name := filepath.Join(dir, prefix+nextSuffix()) |
| 157 | + f, err = fs.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0600) |
| 158 | + if os.IsExist(err) { |
| 159 | + if nconflict++; nconflict > 10 { |
| 160 | + randmu.Lock() |
| 161 | + rand = reseed() |
| 162 | + randmu.Unlock() |
| 163 | + } |
| 164 | + continue |
| 165 | + } |
139 | 166 | break
|
140 | 167 | }
|
141 |
| - |
142 |
| - return fs.Create(fullpath) |
143 |
| -} |
144 |
| - |
145 |
| -func getTempFilename(fs billy.Basic, dir, prefix string) string { |
146 |
| - atomic.AddInt32(&tempCount, 1) |
147 |
| - filename := fmt.Sprintf("%s_%d_%d", prefix, tempCount, time.Now().UnixNano()) |
148 |
| - return fs.Join(dir, filename) |
| 168 | + return |
149 | 169 | }
|
150 | 170 |
|
151 | 171 | type underlying interface {
|
|
0 commit comments