Skip to content

Commit 4632ded

Browse files
authored
Harden pkg/locks with GCNamedMutex and fix unlock ordering
1 parent dbecc18 commit 4632ded

3 files changed

Lines changed: 168 additions & 510 deletions

File tree

pkg/locks/gc_named_locks.go

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -82,13 +82,12 @@ func (g *GCNamedMutex) Unlock(name string) {
8282
g.m.Unlock()
8383
return
8484
}
85+
resourceMutex.m.Unlock()
8586
resourceMutex.c--
8687
if resourceMutex.c == 0 {
8788
delete(g.mutexes, name)
8889
}
8990
g.m.Unlock()
90-
91-
resourceMutex.m.Unlock()
9291
}
9392

9493
func (g *GCNamedMutex) RLock(name string) {
@@ -111,13 +110,12 @@ func (g *GCNamedMutex) RUnlock(name string) {
111110
g.m.Unlock()
112111
return
113112
}
113+
resourceMutex.m.RUnlock()
114114
resourceMutex.c--
115115
if resourceMutex.c == 0 {
116116
delete(g.mutexes, name)
117117
}
118118
g.m.Unlock()
119-
120-
resourceMutex.m.RUnlock()
121119
}
122120

123121
// LockWithGuard acquires a write lock and returns a wrapper for convenient unlock handling.

pkg/locks/locks.go

Lines changed: 14 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -4,79 +4,26 @@ package locks
44

55
import (
66
"context"
7-
"sync"
8-
"sync/atomic"
97

10-
. "github.com/netapp/trident/logging"
8+
"github.com/netapp/trident/logging"
119
)
1210

13-
// sync.Map is like a Go map[interface{}]interface{} but is safe for concurrent use by multiple
14-
// goroutines without additional locking or coordination.
15-
var sharedLocks, waitQueue sync.Map
11+
// sharedLocks provides garbage-collected named locks for package-level Lock/Unlock.
12+
var sharedLocks = NewGCNamedMutex()
1613

17-
// getLock returns a mutex with the specified ID. If the lock does not exist, one is created.
18-
// This method uses sync.Map primitive (concurrency safe map) to defend against race conditions where multiple
19-
// callers try to get a lock at the same time.
20-
func getLock(ctx context.Context, lockID string) *sync.Mutex {
21-
newLock := &sync.Mutex{}
22-
storedLock, loaded := sharedLocks.LoadOrStore(lockID, newLock)
23-
if !loaded {
24-
Logc(ctx).WithField("lock", lockID).Debug("Created shared lock.")
25-
}
26-
return storedLock.(*sync.Mutex)
27-
}
28-
29-
// Lock acquires a mutex with the specified ID. The mutex does not need to exist before
30-
// calling this method. The semantics of this method are intentionally identical to sync.Mutex.Lock().
14+
// Lock acquires a mutex with the specified ID. The mutex does not need to exist
15+
// before calling this method. Semantics match sync.Mutex.Lock().
16+
//
17+
// ctx is used for logging only; acquisition is not cancelled when ctx is done.
3118
func Lock(ctx context.Context, lockContext, lockID string) {
32-
IncrementQueueSize(lockID)
33-
Logc(ctx).WithField("lockContext", lockContext).Debugf("Attempting to acquire shared lock (%s); %d position in the queue.",
34-
lockID, WaitQueueSize(lockID))
35-
36-
getLock(ctx, lockID).Lock()
37-
38-
DecrementQueueSize(lockID)
39-
Logc(ctx).WithField("lockContext", lockContext).Debugf("Acquired shared lock (%s).", lockID)
19+
logging.Logc(ctx).WithField("lockContext", lockContext).Debugf(
20+
"Attempting to acquire shared lock (%s).", lockID)
21+
sharedLocks.Lock(lockID)
22+
logging.Logc(ctx).WithField("lockContext", lockContext).Debugf("Acquired shared lock (%s).", lockID)
4023
}
4124

42-
// Unlock releases a mutex with the specified ID. The semantics of this method are intentionally
43-
// identical to sync.Mutex.Unlock().
25+
// Unlock releases a mutex with the specified ID. Semantics match sync.Mutex.Unlock().
4426
func Unlock(ctx context.Context, lockContext, lockID string) {
45-
getLock(ctx, lockID).Unlock()
46-
Logc(ctx).WithField("lockContext", lockContext).Debugf("Released shared lock (%s).", lockID)
47-
}
48-
49-
// IncrementQueueSize increments the wait queue size by 1
50-
func IncrementQueueSize(lockID string) {
51-
currentWait, ok := waitQueue.Load(lockID)
52-
if !ok {
53-
valPtr := new(uint32(1))
54-
waitQueue.Store(lockID, valPtr)
55-
} else {
56-
if ptr, ok := currentWait.(*uint32); ok {
57-
atomic.AddUint32(ptr, 1)
58-
}
59-
}
60-
}
61-
62-
// DecrementQueueSize decrements the wait queue size by 1
63-
func DecrementQueueSize(lockID string) {
64-
currentWait, ok := waitQueue.Load(lockID)
65-
if ok {
66-
if ptr, ok := currentWait.(*uint32); ok {
67-
if atomic.LoadUint32(ptr) > 0 {
68-
atomic.AddUint32(ptr, ^uint32(0))
69-
}
70-
}
71-
}
72-
}
73-
74-
// WaitQueueSize returns the wait queue size
75-
func WaitQueueSize(lockID string) uint32 {
76-
currentWait, ok := waitQueue.Load(lockID)
77-
if !ok {
78-
return 0
79-
}
80-
81-
return atomic.LoadUint32(currentWait.(*uint32))
27+
sharedLocks.Unlock(lockID)
28+
logging.Logc(ctx).WithField("lockContext", lockContext).Debugf("Released shared lock (%s).", lockID)
8229
}

0 commit comments

Comments
 (0)