forked from amit-davidson/Chronos
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Lockset.go
113 lines (100 loc) · 3.32 KB
/
Lockset.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
package domain
import (
"go/token"
"golang.org/x/tools/go/ssa"
)
type locksLastUse map[token.Pos]*ssa.CallCommon
type Lockset struct {
Locks locksLastUse
Unlocks locksLastUse
}
func NewLockset() *Lockset {
return &Lockset{
Locks: make(locksLastUse),
Unlocks: make(locksLastUse),
}
}
// The three following methods handle updating the lockset the same way. By recording each lock state (locked/unlocked)
// at the current point. It means that if a mutex was unlocked at some point but later was locked again, then it's latest
// status is locked, and the unlock status is removed. The difference between each algorithm is the context used.
//
// UpdateWithNewLockSet updates the lockset and expects newLocks, newUnlocks to contain the most up to date status of the
// mutex and update accordingly.
func (ls *Lockset) UpdateWithNewLockSet(newLocks, newUnlocks locksLastUse) {
for lockName, lock := range newLocks {
ls.Locks[lockName] = lock
}
for unlockName := range newUnlocks {
delete(ls.Locks, unlockName)
}
for unlockName, unlock := range newUnlocks {
ls.Unlocks[unlockName] = unlock
}
for lockName := range newLocks {
if _, ok := ls.Locks[lockName]; ok {
delete(ls.Unlocks, lockName)
}
}
}
// UpdateWithPrevLockset works the same as UpdateWithNewLockSet but expects prevLS to contain an earlier version of the
// status of the locks.
func (ls *Lockset) UpdateWithPrevLockset(prevLS *Lockset) {
for lockName, lock := range prevLS.Locks {
_, okLock := ls.Locks[lockName] // We check to see the lock doesn't exist to not override it with old reference of this lock
_, okUnlock := ls.Unlocks[lockName]
if !okLock && !okUnlock {
ls.Locks[lockName] = lock
}
}
for lockName, lock := range prevLS.Unlocks {
_, okLock := ls.Locks[lockName] // We check to see the unlock doesn't exist to not override it with old reference of this unlock
_, okUnlock := ls.Unlocks[lockName]
if !okLock && !okUnlock {
ls.Unlocks[lockName] = lock
}
}
}
// MergeSiblingLockset is called when merging different paths of the control flow graph. The mutex status should be
// merged and not appended. Because Locks is a must set, for a lock to appear in the result, an intersect
// between the branches' lockset is performed to make sure the lock appears in all branches. Unlock is a may set, so a
// union is applied since it's sufficient to have an unlock at least in one of the branches.
func (ls *Lockset) MergeSiblingLockset(locksetToMerge *Lockset) {
ls.Locks.intersect(locksetToMerge.Locks)
ls.Unlocks.union(locksetToMerge.Unlocks)
for unlockName := range ls.Unlocks {
// If there's a lock in one branch and an unlock in second, then unlock wins
delete(ls.Locks, unlockName)
}
}
func (ls *Lockset) Copy() *Lockset {
newLs := &Lockset{}
newLocks := make(locksLastUse, len(ls.Locks))
for key, value := range ls.Locks {
newLocks[key] = value
}
newLs.Locks = newLocks
newUnlocks := make(locksLastUse, len(ls.Unlocks))
for key, value := range ls.Unlocks {
newUnlocks[key] = value
}
newLs.Unlocks = newUnlocks
return newLs
}
func (ls *locksLastUse) intersect(newLS locksLastUse) {
found := false
for a := range *ls {
for b := range newLS {
if a == b {
found = true
}
}
if !found {
delete(*ls, a)
}
}
}
func (ls *locksLastUse) union(newLS locksLastUse) {
for b := range newLS {
(*ls)[b] = newLS[b]
}
}