~ knotless Search/
↑↓ move↵ open⇧↵ all results
0
0
main
84 lines · 1.7 KB Raw History
package request

import (
	"context"
	"errors"
	"fmt"
	"os"
	"path/filepath"

	"github.com/johnandersen777/socialweb-computer-kcp/internal/hostos"
)

const OwnerLockFile = "owner.lock"

type Owner struct {
	f *os.File
}

func openOwnerLock(dir string) (*os.File, error) {
	path := filepath.Join(dir, OwnerLockFile)
	info, err := os.Lstat(path)
	if err == nil && (info.Mode()&os.ModeSymlink != 0 || !info.Mode().IsRegular()) {
		return nil, fmt.Errorf("request: the owner lock %s is not a regular file", path)
	}
	if err != nil && !errors.Is(err, os.ErrNotExist) {
		return nil, err
	}
	f, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE, 0o600)
	if err != nil {
		return nil, fmt.Errorf("request: the owner lock: %w", err)
	}
	return f, nil
}

func TakeOwner(stateRoot, name string) (*Owner, bool, error) {
	return TakeOwnerDir(Dir(stateRoot, name))
}

func TakeOwnerDir(dir string) (*Owner, bool, error) {
	f, err := openOwnerLock(dir)
	if err != nil {
		return nil, false, err
	}
	if err := hostos.Lock(f, hostos.LockExclusive|hostos.LockNonBlocking); err != nil {
		_ = f.Close()
		return nil, false, nil
	}
	return &Owner{f: f}, true, nil
}

func AwaitOwnerGone(ctx context.Context, stateRoot, name string) error {
	f, err := openOwnerLock(Dir(stateRoot, name))
	if errors.Is(err, os.ErrNotExist) {
		return nil
	}
	if err != nil {
		return err
	}
	done := make(chan error, 1)
	go func() {
		done <- hostos.Lock(f, hostos.LockExclusive)
	}()
	select {
	case err := <-done:
		_ = hostos.Unlock(f)
		_ = f.Close()
		return err
	case <-ctx.Done():
		go func() {
			<-done
			_ = f.Close()
		}()
		return ctx.Err()
	}
}

func (o *Owner) Close() {
	if o == nil || o.f == nil {
		return
	}
	_ = hostos.Unlock(o.f)
	_ = o.f.Close()
	o.f = nil
}