github.com/minio/minio@v0.0.0-20240328213742-3f72439b8a27/internal/lock/lock.go (about)

     1  // Copyright (c) 2015-2021 MinIO, Inc.
     2  //
     3  // This file is part of MinIO Object Storage stack
     4  //
     5  // This program is free software: you can redistribute it and/or modify
     6  // it under the terms of the GNU Affero General Public License as published by
     7  // the Free Software Foundation, either version 3 of the License, or
     8  // (at your option) any later version.
     9  //
    10  // This program is distributed in the hope that it will be useful
    11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13  // GNU Affero General Public License for more details.
    14  //
    15  // You should have received a copy of the GNU Affero General Public License
    16  // along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17  
    18  // Package lock - implements filesystem locking wrappers around an
    19  // open file descriptor.
    20  package lock
    21  
    22  import (
    23  	"errors"
    24  	"os"
    25  	"sync"
    26  )
    27  
    28  // ErrAlreadyLocked is returned if the underlying fd is already locked.
    29  var ErrAlreadyLocked = errors.New("file already locked")
    30  
    31  // RLockedFile represents a read locked file, implements a special
    32  // closer which only closes the associated *os.File when the ref count.
    33  // has reached zero, i.e when all the readers have given up their locks.
    34  type RLockedFile struct {
    35  	*LockedFile
    36  	mutex sync.Mutex
    37  	refs  int // Holds read lock refs.
    38  }
    39  
    40  // IsClosed - Check if the rlocked file is already closed.
    41  func (r *RLockedFile) IsClosed() bool {
    42  	r.mutex.Lock()
    43  	defer r.mutex.Unlock()
    44  	return r.refs == 0
    45  }
    46  
    47  // IncLockRef - is used by called to indicate lock refs.
    48  func (r *RLockedFile) IncLockRef() {
    49  	r.mutex.Lock()
    50  	r.refs++
    51  	r.mutex.Unlock()
    52  }
    53  
    54  // Close - this closer implements a special closer
    55  // closes the underlying fd only when the refs
    56  // reach zero.
    57  func (r *RLockedFile) Close() (err error) {
    58  	r.mutex.Lock()
    59  	defer r.mutex.Unlock()
    60  
    61  	if r.refs == 0 {
    62  		return os.ErrInvalid
    63  	}
    64  
    65  	r.refs--
    66  	if r.refs == 0 {
    67  		err = r.File.Close()
    68  	}
    69  
    70  	return err
    71  }
    72  
    73  // Provides a new initialized read locked struct from *os.File
    74  func newRLockedFile(lkFile *LockedFile) (*RLockedFile, error) {
    75  	if lkFile == nil {
    76  		return nil, os.ErrInvalid
    77  	}
    78  
    79  	return &RLockedFile{
    80  		LockedFile: lkFile,
    81  		refs:       1,
    82  	}, nil
    83  }
    84  
    85  // RLockedOpenFile - returns a wrapped read locked file, if the file
    86  // doesn't exist at path returns an error.
    87  func RLockedOpenFile(path string) (*RLockedFile, error) {
    88  	lkFile, err := LockedOpenFile(path, os.O_RDONLY, 0o666)
    89  	if err != nil {
    90  		return nil, err
    91  	}
    92  
    93  	return newRLockedFile(lkFile)
    94  }
    95  
    96  // LockedFile represents a locked file
    97  type LockedFile struct {
    98  	*os.File
    99  }