Source file src/cmd/go/internal/lockedfile/mutex.go

     1  // Copyright 2018 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package lockedfile
     6  
     7  import (
     8  	"fmt"
     9  	"os"
    10  	"sync"
    11  )
    12  
    13  // A Mutex provides mutual exclusion within and across processes by locking a
    14  // well-known file. Such a file generally guards some other part of the
    15  // filesystem: for example, a Mutex file in a directory might guard access to
    16  // the entire tree rooted in that directory.
    17  //
    18  // Mutex does not implement sync.Locker: unlike a sync.Mutex, a lockedfile.Mutex
    19  // can fail to lock (e.g. if there is a permission error in the filesystem).
    20  //
    21  // Like a sync.Mutex, a Mutex may be included as a field of a larger struct but
    22  // must not be copied after first use. The Path field must be set before first
    23  // use and must not be change thereafter.
    24  type Mutex struct {
    25  	Path string     // The path to the well-known lock file. Must be non-empty.
    26  	mu   sync.Mutex // A redundant mutex. The race detector doesn't know about file locking, so in tests we may need to lock something that it understands.
    27  }
    28  
    29  // MutexAt returns a new Mutex with Path set to the given non-empty path.
    30  func MutexAt(path string) *Mutex {
    31  	if path == "" {
    32  		panic("lockedfile.MutexAt: path must be non-empty")
    33  	}
    34  	return &Mutex{Path: path}
    35  }
    36  
    37  func (mu *Mutex) String() string {
    38  	return fmt.Sprintf("lockedfile.Mutex(%s)", mu.Path)
    39  }
    40  
    41  // Lock attempts to lock the Mutex.
    42  //
    43  // If successful, Lock returns a non-nil unlock function: it is provided as a
    44  // return-value instead of a separate method to remind the caller to check the
    45  // accompanying error. (See https://golang.org/issue/20803.)
    46  func (mu *Mutex) Lock() (unlock func(), err error) {
    47  	if mu.Path == "" {
    48  		panic("lockedfile.Mutex: missing Path during Lock")
    49  	}
    50  
    51  	// We could use either O_RDWR or O_WRONLY here. If we choose O_RDWR and the
    52  	// file at mu.Path is write-only, the call to OpenFile will fail with a
    53  	// permission error. That's actually what we want: if we add an RLock method
    54  	// in the future, it should call OpenFile with O_RDONLY and will require the
    55  	// files must be readable, so we should not let the caller make any
    56  	// assumptions about Mutex working with write-only files.
    57  	f, err := OpenFile(mu.Path, os.O_RDWR|os.O_CREATE, 0666)
    58  	if err != nil {
    59  		return nil, err
    60  	}
    61  	mu.mu.Lock()
    62  
    63  	return func() {
    64  		mu.mu.Unlock()
    65  		f.Close()
    66  	}, nil
    67  }
    68  

View as plain text