common/pathutil/runtime.go (53 lines of code) (raw):
package pathutil
import (
"os"
"path/filepath"
"sync"
)
var (
_currentVersionDir string
_currentVersionDirRWLock sync.RWMutex
_crossVersionDir string
_crossVersionDirRWLock sync.RWMutex
)
func getCurrentVersionDir() (string, error) {
_currentVersionDirRWLock.RLock()
if _currentVersionDir == "" {
_currentVersionDirRWLock.RUnlock()
_currentVersionDirRWLock.Lock()
defer _currentVersionDirRWLock.Unlock()
if _currentVersionDir == "" {
path, err := os.Executable()
if err != nil {
return "", err
}
_currentVersionDir = filepath.Dir(path)
}
} else {
defer _currentVersionDirRWLock.RUnlock()
}
return _currentVersionDir, nil
}
func GetCrossVersionDir() (string, error) {
_crossVersionDirRWLock.RLock()
if _crossVersionDir == "" {
_crossVersionDirRWLock.RUnlock()
_crossVersionDirRWLock.Lock()
defer _crossVersionDirRWLock.Unlock()
if _crossVersionDir == "" {
versionDir, err := getCurrentVersionDir()
if err != nil {
return "", err
}
absoluteVersionDir, err := filepath.Abs(versionDir)
if err != nil {
return "", err
}
// Although filepath.Dir method would call filepath.Clean internally, here
// explicitly call the method to guarantee no trailing slash in path
cleanedVersionDir := filepath.Clean(absoluteVersionDir)
_crossVersionDir = filepath.Dir(cleanedVersionDir)
}
} else {
defer _crossVersionDirRWLock.RUnlock()
}
return _crossVersionDir, nil
}