blob: 5f8a8523532a6a62e803cf68230d261a90583c3f [file]
// Copyright 2026 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package project
import (
"fmt"
"os"
"path/filepath"
"syscall"
)
// AcquireCacheLock implements fail-fast concurrency control across worktrees.
// If another worktree is currently synchronizing the shared cache, it returns an immediate error.
func AcquireCacheLock(cacheRoot string) (unlock func(), err error) {
if err := os.MkdirAll(cacheRoot, 0755); err != nil {
return nil, err
}
lockPath := filepath.Join(cacheRoot, ".cache.lock")
f, err := os.OpenFile(lockPath, os.O_CREATE|os.O_RDWR, 0644)
if err != nil {
return nil, err
}
// Non-blocking exclusive lock
if err := syscall.Flock(int(f.Fd()), syscall.LOCK_EX|syscall.LOCK_NB); err != nil {
f.Close()
return nil, fmt.Errorf("another Jiri process is currently synchronizing the package cache (lock held at %s); please wait and retry (error: %v)", lockPath, err)
}
return func() {
syscall.Flock(int(f.Fd()), syscall.LOCK_UN)
f.Close()
}, nil
}