-
Notifications
You must be signed in to change notification settings - Fork 32
/
mapmutex.go
117 lines (99 loc) · 2.87 KB
/
mapmutex.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
package mapmutex
import (
"fmt"
"sync"
)
// untypedMapMutex wraps a map of mutexes. Each key locks separately.
// this implementation is a modified version of https://stackoverflow.com/a/62562831/1011803
// this is unexported on purpose to prevent possible issues with pointers not being equivalent.
// TODO: different implementations need to be moved to ginny: https://flaviocopes.com/golang-generic-generate/
type untypedMapMutex interface {
Lock(key interface{}) Unlocker
TryLock(key interface{}) (Unlocker, bool)
Keys() []interface{}
}
type untypedMapMutexImpl struct {
ml sync.Mutex // lock for entry map
ma map[interface{}]*mentry // entry map
}
type mentry struct {
// m point back to untypedMapMutexImpl, so we can synchronize removing this mentry when cnt==0
m *untypedMapMutexImpl
// el is an entry-specific lock
el sync.Mutex
// cnt is the reference count
cnt int
// key is the key of the memory entry
key interface{}
}
// Unlocker provides an Unlock method to release the lock.
type Unlocker interface {
Unlock()
}
// newMapMutex returns an initialized untypedMapMutexImpl.
func newMapMutex() untypedMapMutex {
return &untypedMapMutexImpl{ma: make(map[interface{}]*mentry)}
}
// Lock acquires a lock corresponding to this key.
// This method will never return nil and Unlock() must be called
// to release the lock when done.
func (m *untypedMapMutexImpl) Lock(key interface{}) Unlocker {
// read or create entry for this key atomically
m.ml.Lock()
e, ok := m.ma[key]
if !ok {
e = &mentry{m: m, key: key}
m.ma[key] = e
}
e.cnt++ // ref count
m.ml.Unlock()
// acquire lock, will block here until e.cnt==1
e.el.Lock()
return e
}
// TryLock tries to acquire the lock, if this can't be done instantly false is returned. Otherwise
// true and the unlocker are returned.
func (m *untypedMapMutexImpl) TryLock(key interface{}) (Unlocker, bool) {
// read or create entry for this key atomically
m.ml.Lock()
defer m.ml.Unlock()
e, ok := m.ma[key]
if !ok {
e = &mentry{m: m, key: key}
m.ma[key] = e
}
if e.el.TryLock() {
e.cnt++
return e, true
}
return nil, false
}
// Keys returns all keys in the map.
func (m *untypedMapMutexImpl) Keys() []interface{} {
m.ml.Lock()
defer m.ml.Unlock()
keys := make([]interface{}, 0, len(m.ma))
for k := range m.ma {
keys = append(keys, k)
}
return keys
}
// Unlock releases the lock for this entry.
func (me *mentry) Unlock() {
m := me.m
// decrement and if needed remove entry atomically
m.ml.Lock()
e, ok := m.ma[me.key]
if !ok { // entry must exist
m.ml.Unlock()
panic(fmt.Errorf("unlock requested for key=%v but no entry found", me.key))
}
e.cnt-- // ref count
if e.cnt < 1 { // if it hits zero then we own it and remove from map
delete(m.ma, me.key)
}
m.ml.Unlock()
// now that map stuff is handled, we unlock and let
// anything else waiting on this key through
e.el.Unlock()
}