github.com/cockroachdb/pebble@v0.0.0-20231214172447-ab4952c5f87b/vfs/file_lock_unix.go (about)

     1  // Copyright 2014 The LevelDB-Go and Pebble Authors. All rights reserved. Use
     2  // of this source code is governed by a BSD-style license that can be found in
     3  // the LICENSE file.
     4  
     5  //go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris
     6  // +build darwin dragonfly freebsd linux netbsd openbsd solaris
     7  
     8  package vfs
     9  
    10  import (
    11  	"io"
    12  	"os"
    13  	"sync"
    14  
    15  	"github.com/cockroachdb/errors"
    16  	"golang.org/x/sys/unix"
    17  )
    18  
    19  var lockedFiles struct {
    20  	mu struct {
    21  		sync.Mutex
    22  		files map[string]bool
    23  	}
    24  }
    25  
    26  // lockCloser hides all of an os.File's methods, except for Close.
    27  type lockCloser struct {
    28  	name string
    29  	f    *os.File
    30  }
    31  
    32  func (l lockCloser) Close() error {
    33  	lockedFiles.mu.Lock()
    34  	defer lockedFiles.mu.Unlock()
    35  	if !lockedFiles.mu.files[l.name] {
    36  		panic(errors.Errorf("lock file %q is not locked", l.name))
    37  	}
    38  	delete(lockedFiles.mu.files, l.name)
    39  
    40  	return l.f.Close()
    41  }
    42  
    43  func (defaultFS) Lock(name string) (io.Closer, error) {
    44  	lockedFiles.mu.Lock()
    45  	defer lockedFiles.mu.Unlock()
    46  	if lockedFiles.mu.files == nil {
    47  		lockedFiles.mu.files = map[string]bool{}
    48  	}
    49  	if lockedFiles.mu.files[name] {
    50  		return nil, errors.New("lock held by current process")
    51  	}
    52  
    53  	f, err := os.Create(name)
    54  	if err != nil {
    55  		return nil, err
    56  	}
    57  	spec := unix.Flock_t{
    58  		Type:   unix.F_WRLCK,
    59  		Whence: io.SeekStart,
    60  		Start:  0,
    61  		Len:    0, // 0 means to lock the entire file.
    62  		Pid:    int32(os.Getpid()),
    63  	}
    64  	if err := unix.FcntlFlock(f.Fd(), unix.F_SETLK, &spec); err != nil {
    65  		f.Close()
    66  		return nil, err
    67  	}
    68  	lockedFiles.mu.files[name] = true
    69  	return lockCloser{name, f}, nil
    70  }