Luigit
repositories / bugabinga.net

bugabinga.net

personal infrastructure for bugabinga!

owned by admin

services/luci/internal/servicelock/lock.go

Raw
package servicelock

import (
	"errors"
	"os"
	"path/filepath"
	"syscall"
)

var ErrHeld = errors.New("luci serve consumer already holds this data directory")

type Lock struct{ file *os.File }

// Acquire exclusively locks one Luci data directory until Close or process exit.
func Acquire(dataDir string) (Lock, error) {
	if err := os.MkdirAll(dataDir, 0o755); err != nil {
		return Lock{}, err
	}
	file, err := os.OpenFile(filepath.Join(dataDir, ".serve.lock"), os.O_CREATE|os.O_RDWR, 0o644)
	if err != nil {
		return Lock{}, err
	}
	if err := syscall.Flock(int(file.Fd()), syscall.LOCK_EX|syscall.LOCK_NB); err != nil {
		_ = file.Close()
		if errors.Is(err, syscall.EWOULDBLOCK) || errors.Is(err, syscall.EAGAIN) {
			return Lock{}, ErrHeld
		}
		return Lock{}, err
	}
	return Lock{file: file}, nil
}

func (l Lock) Close() error {
	if l.file == nil {
		return nil
	}
	return l.file.Close()
}