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  }