use a single coarse package lock
Brian Tiger Chow committed
Jan 13, 2015 at 20:46 UTC
a3d236269108d759359109cc9d91922057cd58d3
2 files changed
+25
-32
repo/fsrepo/fsrepo.go
+22
-16
@@ -7,6 +7,7 @@ import (
7
"os"
8
"path"
9
"path/filepath"
10
+ "sync"
11
12
repo "github.com/jbenet/go-ipfs/repo"
13
common "github.com/jbenet/go-ipfs/repo/common"
@@ -18,6 +19,13 @@ import (
19
)
20
21
var (
22
+
23
+ // packageLock must be held to while performing any operation that modifies an
24
+ // FSRepo's state field. This includes Init, Open, Close, and Remove.
25
+ packageLock sync.Mutex // protects openerCounter and lockfiles
26
+ // lockfiles holds references to the Closers that ensure that repos are
27
+ // only accessed by one process at a time.
28
+ lockfiles map[string]io.Closer
29
// openerCounter prevents the fsrepo from being removed while there exist open
30
// FSRepo handles. It also ensures that the Init is atomic.
31
//
@@ -26,8 +34,6 @@ var (
34
// If an operation is used when repo is Open and the operation does not
35
// change the repo's state, the package lock does not need to be acquired.
36
openerCounter *opener.Counter
29
-
30
- lockfiles map[string]io.Closer
37
)
38
39
func init() {
@@ -61,8 +67,8 @@ func ConfigAt(repoPath string) (*config.Config, error) {
67
68
// Init initializes a new FSRepo at the given path with the provided config.
69
func Init(path string, conf *config.Config) error {
64
- openerCounter.Lock() // lock must be held to ensure atomicity (prevent Removal)
65
- defer openerCounter.Unlock()
70
+ packageLock.Lock() // lock must be held to ensure atomicity (prevent Removal)
71
+ defer packageLock.Unlock()
72
73
if isInitializedUnsynced(path) {
74
return nil
@@ -79,8 +85,8 @@ func Init(path string, conf *config.Config) error {
85
86
// Remove recursively removes the FSRepo at |path|.
87
func Remove(path string) error {
82
- openerCounter.Lock()
83
- defer openerCounter.Unlock()
88
+ packageLock.Lock()
89
+ defer packageLock.Unlock()
90
if openerCounter.NumOpeners(path) != 0 {
91
return errors.New("repo in use")
92
}
@@ -90,16 +96,16 @@ func Remove(path string) error {
96
// LockedByOtherProcess returns true if the FSRepo is locked by another
97
// process. If true, then the repo cannot be opened by this process.
98
func LockedByOtherProcess(repoPath string) bool {
93
- openerCounter.Lock()
94
- defer openerCounter.Unlock()
99
+ packageLock.Lock()
100
+ defer packageLock.Unlock()
101
// NB: the lock is only held when repos are Open
102
return lockfile.Locked(repoPath) && openerCounter.NumOpeners(repoPath) == 0
103
}
104
105
// Open returns an error if the repo is not initialized.
106
func (r *FSRepo) Open() error {
101
- openerCounter.Lock()
102
- defer openerCounter.Unlock()
107
+ packageLock.Lock()
108
+ defer packageLock.Unlock()
109
if r.state != unopened {
110
return debugerror.Errorf("repo is %s", r.state)
111
}
@@ -232,8 +238,8 @@ func (r *FSRepo) SetConfigKey(key string, value interface{}) error {
238
239
// Close closes the FSRepo, releasing held resources.
240
func (r *FSRepo) Close() error {
235
- openerCounter.Lock()
236
- defer openerCounter.Unlock()
241
+ packageLock.Lock()
242
+ defer packageLock.Unlock()
243
if r.state != opened {
244
return debugerror.Errorf("repo is %s", r.state)
245
}
@@ -245,8 +251,8 @@ var _ repo.Repo = &FSRepo{}
251
252
// IsInitialized returns true if the repo is initialized at provided |path|.
253
func IsInitialized(path string) bool {
248
- openerCounter.Lock()
249
- defer openerCounter.Unlock()
254
+ packageLock.Lock()
255
+ defer packageLock.Unlock()
256
return isInitializedUnsynced(path)
257
}
258
@@ -279,7 +285,7 @@ func initCheckDir(path string) error {
285
}
286
287
// transitionToOpened manages the state transition to |opened|. Caller must hold
282
-// openerCounter lock.
288
+// the package mutex.
289
func transitionToOpened(r *FSRepo) error {
290
r.state = opened
291
if countBefore := openerCounter.NumOpeners(r.path); countBefore == 0 { // #first
@@ -293,7 +299,7 @@ func transitionToOpened(r *FSRepo) error {
299
}
300
301
// transitionToClosed manages the state transition to |closed|. Caller must
296
-// hold openerCounter lock.
302
+// hold the package mutex.
303
func transitionToClosed(r *FSRepo) error {
304
r.state = closed
305
if err := openerCounter.RemoveOpener(r.path); err != nil {
repo/fsrepo/opener/counter.go
+3
-16
@@ -1,13 +1,10 @@
1
package fsrepo
2
3
-import (
4
- "path"
5
- "sync"
6
-)
3
+import "path"
4
+
5
+// TODO this could be made into something more generic.
6
7
type Counter struct {
9
- // lock protects repos
10
- lock sync.Mutex
8
// repos maps repo paths to the number of openers holding an FSRepo handle
9
// to it
10
repos map[string]int
@@ -19,16 +16,6 @@ func NewCounter() *Counter {
16
}
17
}
18
22
-// Lock must be held to while performing any operation that modifies an
23
-// FSRepo's state field. This includes Init, Open, Close, and Remove.
24
-func (l *Counter) Lock() {
25
- l.lock.Lock()
26
-}
27
-
28
-func (l *Counter) Unlock() {
29
- l.lock.Unlock()
30
-}
31
-
19
// NumOpeners returns the number of FSRepos holding a handle to the repo at
20
// this path. This method is not thread-safe. The caller must have this object
21
// locked.