...

Source file src/pkg/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