-
Notifications
You must be signed in to change notification settings - Fork 1.1k
/
lock.go
71 lines (62 loc) 路 2.71 KB
/
lock.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
// Copyright 2016-2021, Pulumi Corporation.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package fsutil
import (
"sync"
"github.com/rogpeppe/go-internal/lockedfile"
)
// FileMutex is a mutex that serializes both within and across processes. When acquired, it can be assumed that the
// caller holds exclusive access over te protected resources, even if there are other consumers both within and outside
// of the same process.
type FileMutex struct {
proclock sync.Mutex // lock serializing in-process access to the protected resource
fslock *lockedfile.Mutex // lock serializing out-of-process access to the protected resource
fsunlock func()
}
// NewFileMutex creates a new FileMutex using the given file as a file lock.
func NewFileMutex(path string) *FileMutex {
return &FileMutex{
fslock: lockedfile.MutexAt(path),
}
}
// Lock locks the file mutex. It does this in two phases: first, it locks the process lock, which when held guarantees
// exclusive access to the resource within the current process. Second, with the process lock held, it locks the file
// lock. The flock system call operates on a process granularity and, if one process attempts to lock the same file
// multiple times, flock will consider the lock to be held by the process even if different threads are acquiring the
// lock.
//
// Because of this, the two-pronged approach to locking guarantees exclusive access to the resource by locking a process
// shared mutex and a global shared mutex. Once this method returns without an error, callers can be sure that the
// calling goroutine completely owns the resource.
func (fm *FileMutex) Lock() error {
fm.proclock.Lock()
fsunlock, err := fm.fslock.Lock()
if err != nil {
fm.proclock.Unlock()
return err
}
fm.fsunlock = fsunlock
return nil
}
// Unlock unlocks the file mutex. It first unlocks the file lock, which allows other processes to lock the file lock,
// after which it unlocks the proc lock. Unlocking the file lock first ensures that it is not possible for two
// goroutines to lock or unlock the file mutex without first holding the proc lock.
func (fm *FileMutex) Unlock() error {
if fm.fsunlock != nil {
fm.fsunlock()
fm.fsunlock = nil
}
fm.proclock.Unlock()
return nil
}