github.com/phobos182/packer@v0.2.3-0.20130819023704-c84d2aeffc68/packer/cache.go (about) 1 package packer 2 3 import ( 4 "crypto/sha256" 5 "encoding/hex" 6 "path/filepath" 7 "strings" 8 "sync" 9 ) 10 11 // Cache implements a caching interface where files can be stored for 12 // re-use between multiple runs. 13 type Cache interface { 14 // Lock takes a key and returns the path where the file can be written to. 15 // Packer guarantees that no other process will write to this file while 16 // the lock is held. 17 // 18 // If the key has an extension (e.g., file.ext), the resulting path 19 // will have that extension as well. 20 // 21 // The cache will block and wait for the lock. 22 Lock(string) string 23 24 // Unlock will unlock a certain cache key. Be very careful that this 25 // is only called once per lock obtained. 26 Unlock(string) 27 28 // RLock returns the path to a key in the cache and locks it for reading. 29 // The second return parameter is whether the key existed or not. 30 // This will block if any locks are held for writing. No lock will be 31 // held if the key doesn't exist. 32 RLock(string) (string, bool) 33 34 // RUnlock will unlock a key for reading. 35 RUnlock(string) 36 } 37 38 // FileCache implements a Cache by caching the data directly to a cache 39 // directory. 40 type FileCache struct { 41 CacheDir string 42 l sync.Mutex 43 rw map[string]*sync.RWMutex 44 } 45 46 func (f *FileCache) Lock(key string) string { 47 hashKey := f.hashKey(key) 48 rw := f.rwLock(hashKey) 49 rw.Lock() 50 51 return f.cachePath(key, hashKey) 52 } 53 54 func (f *FileCache) Unlock(key string) { 55 hashKey := f.hashKey(key) 56 rw := f.rwLock(hashKey) 57 rw.Unlock() 58 } 59 60 func (f *FileCache) RLock(key string) (string, bool) { 61 hashKey := f.hashKey(key) 62 rw := f.rwLock(hashKey) 63 rw.RLock() 64 65 return f.cachePath(key, hashKey), true 66 } 67 68 func (f *FileCache) RUnlock(key string) { 69 hashKey := f.hashKey(key) 70 rw := f.rwLock(hashKey) 71 rw.RUnlock() 72 } 73 74 func (f *FileCache) cachePath(key string, hashKey string) string { 75 suffix := "" 76 endIndex := strings.Index(key, "?") 77 if endIndex == -1 { 78 endIndex = len(key) 79 } 80 81 dotIndex := strings.LastIndex(key[0:endIndex], ".") 82 if dotIndex > -1 { 83 suffix = key[dotIndex:endIndex] 84 } 85 86 return filepath.Join(f.CacheDir, hashKey+suffix) 87 } 88 89 func (f *FileCache) hashKey(key string) string { 90 sha := sha256.New() 91 sha.Write([]byte(key)) 92 return hex.EncodeToString(sha.Sum(nil)) 93 } 94 95 func (f *FileCache) rwLock(hashKey string) *sync.RWMutex { 96 f.l.Lock() 97 defer f.l.Unlock() 98 99 if f.rw == nil { 100 f.rw = make(map[string]*sync.RWMutex) 101 } 102 103 if result, ok := f.rw[hashKey]; ok { 104 return result 105 } 106 107 var result sync.RWMutex 108 f.rw[hashKey] = &result 109 return &result 110 }