-
Notifications
You must be signed in to change notification settings - Fork 0
Description
Make config.DefaultPaths() thread-safe and eliminate redundant filesystem calls.
Problem
DefaultPaths() is called repeatedly without proper synchronization:
// Current: not thread-safe, potential race condition
func DefaultPaths() *Paths {
if defaultPaths == nil {
defaultPaths = initPaths() // calls getRootDir() which calls os.UserHomeDir()
}
return defaultPaths
}Issues:
- Race condition: If called from multiple goroutines,
defaultPathscheck and assignment is not atomic - Repeated calls:
os.UserHomeDir()may be called multiple times before caching takes effect - No env var caching:
DTVEM_ROOTenvironment variable is checked on every initialization - Excessive path rebuilding: Every call to
DefaultPaths().Shims,DefaultPaths().Bin, etc. in different parts of the codebase relies on proper caching
Impact: Every shim invocation calls this multiple times (e.g., in mapShimToRuntime, findInSystemPath). While currently single-threaded, this is a latent bug that will manifest if concurrency is added.
Also Addresses: Excessive filepath.Join() and Path Resolution
This fix also resolves the concern about repeated filepath.Join() calls throughout the shim:
shimDir := config.DefaultPaths().Shims // Called here
// Later in mapShimToRuntime, another call to DefaultPaths()
// And potentially in findInSystemPathWith sync.Once, the Paths struct (containing Shims, Bin, Runtimes, Cache) is computed exactly once. All subsequent calls to DefaultPaths() return the cached struct with pre-computed absolute paths - no repeated filepath.Join() or os.Getwd() calls.
Recommended Solution
Use sync.Once for thread-safe, guaranteed single initialization:
var (
defaultPaths *Paths
pathsOnce sync.Once
)
func DefaultPaths() *Paths {
pathsOnce.Do(func() {
defaultPaths = initPaths()
})
return defaultPaths
}
func initPaths() *Paths {
// Cache env var lookup once
root := os.Getenv("DTVEM_ROOT")
if root == "" {
home, err := os.UserHomeDir()
if err != nil {
// Handle error appropriately
panic(fmt.Sprintf("failed to get home directory: %v", err))
}
root = filepath.Join(home, ".dtvem")
}
return &Paths{
Root: root,
Bin: filepath.Join(root, "bin"),
Shims: filepath.Join(root, "shims"),
Runtimes: filepath.Join(root, "runtimes"),
Cache: filepath.Join(root, "cache"),
}
}Implementation Tasks
- Add
sync.Oncevariable for path initialization - Refactor
DefaultPaths()to usesync.Once - Cache
DTVEM_ROOTenv var in the single initialization - Cache
os.UserHomeDir()result - Ensure all path fields use absolute paths (no relative path resolution needed)
- Add unit tests for concurrent access
- Add unit tests for
DTVEM_ROOToverride - Run race detector (
go test -race) to verify fix
Expected Impact
- Eliminates race condition in path initialization
- Guarantees
os.UserHomeDir()is called exactly once filepath.Join()for path construction happens once at startup- Environment variable lookup happens once at startup
- Safe for future concurrent usage
Related
- perf(shim): add shim-to-runtime mapping cache for O(1) lookups #93 - mapShimToRuntime optimization (similar sync.Once pattern)
- perf(shim): optimize shim startup time and binary size #72 (closed) - Shim startup optimization mentioned caching
os.UserHomeDir()