-
Notifications
You must be signed in to change notification settings - Fork 15
/
lockfile.go
130 lines (103 loc) · 2.77 KB
/
lockfile.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
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
package main
import (
"encoding/gob"
"errors"
"os"
"path/filepath"
"sync"
"time"
)
var defaultLockFile = filepath.Join(userCacheDir, "o", "lockfile.txt")
// LockKeeper keeps track of which files are currently being edited by o
type LockKeeper struct {
lockedFiles map[string]time.Time // from filename to lockfilestamp
mut *sync.RWMutex
lockFilename string
}
// NewLockKeeper takes an expanded path (not containing ~) to a lock file
// and creates a new LockKeeper struct, without loading the given lock file.
func NewLockKeeper(lockFilename string) *LockKeeper {
lockMap := make(map[string]time.Time)
return &LockKeeper{lockMap, &sync.RWMutex{}, lockFilename}
}
// Load loads the contents of the main lockfile
func (lk *LockKeeper) Load() error {
f, err := os.Open(lk.lockFilename)
if err != nil {
return err
}
defer f.Close()
f.Sync()
dec := gob.NewDecoder(f)
lockMap := make(map[string]time.Time)
err = dec.Decode(&lockMap)
if err != nil {
return err
}
lk.mut.Lock()
lk.lockedFiles = lockMap
lk.mut.Unlock()
return nil
}
// Save writes the contents of the main lockfile
func (lk *LockKeeper) Save() error {
if noWriteToCache {
return nil
}
// First create the folder for the lock file overview, if needed
folderPath := filepath.Dir(lk.lockFilename)
os.MkdirAll(folderPath, os.ModePerm)
f, err := os.OpenFile(lk.lockFilename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o600)
if err != nil {
return err
}
defer f.Close()
enc := gob.NewEncoder(f)
lk.mut.RLock()
err = enc.Encode(lk.lockedFiles)
lk.mut.RUnlock()
f.Sync()
return err
}
// Lock marks the given absolute filename as locked.
// If the file is already locked, an error is returned.
func (lk *LockKeeper) Lock(filename string) error {
// TODO: Make sure not to lock "-" or "/dev/*" files
var has bool
lk.mut.RLock()
_, has = lk.lockedFiles[filename]
lk.mut.RUnlock()
if has {
return errors.New("already locked: " + filename)
}
// Add the file to the map
lk.mut.Lock()
lk.lockedFiles[filename] = time.Now()
lk.mut.Unlock()
return nil
}
// Unlock marks the given absolute filename as unlocked.
// If the file is already unlocked, an error is returned.
func (lk *LockKeeper) Unlock(filename string) error {
var has bool
lk.mut.RLock()
_, has = lk.lockedFiles[filename]
lk.mut.RUnlock()
if !has {
// Caller can ignore this error if they want
return errors.New("already unlocked: " + filename)
}
// Remove the file from the map
lk.mut.Lock()
delete(lk.lockedFiles, filename)
lk.mut.Unlock()
return nil
}
// GetTimestamp assumes that the file is locked. A blank timestamp may be returned if not.
func (lk *LockKeeper) GetTimestamp(filename string) time.Time {
var timestamp time.Time
lk.mut.RLock()
timestamp = lk.lockedFiles[filename]
lk.mut.RUnlock()
return timestamp
}