Skip to content
This repository was archived by the owner on May 20, 2025. It is now read-only.

Commit 91372a8

Browse files
committed
Feat: started implementing reentrancelock
1 parent bbab455 commit 91372a8

File tree

6 files changed

+296
-74
lines changed

6 files changed

+296
-74
lines changed

packages/protocol/engine/ledger/mempool/newconflictdag/conflict/conflict.go

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"fmt"
66
"sync"
77

8+
"github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/reentrantmutex"
89
"github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/weight"
910
"github.com/iotaledger/hive.go/ds/advancedset"
1011
"github.com/iotaledger/hive.go/lo"
@@ -15,7 +16,7 @@ import (
1516
type Conflict[ConflictID, ResourceID IDType] struct {
1617
// PreferredInsteadUpdated is triggered whenever preferred conflict is updated. It carries two values:
1718
// the new preferred conflict and a set of conflicts visited
18-
PreferredInsteadUpdated *event.Event2[*Conflict[ConflictID, ResourceID], TriggerContext[ConflictID]]
19+
PreferredInsteadUpdated *event.Event2[*Conflict[ConflictID, ResourceID], reentrantmutex.ThreadID]
1920

2021
id ConflictID
2122
parents *advancedset.AdvancedSet[ConflictID]
@@ -30,7 +31,7 @@ type Conflict[ConflictID, ResourceID IDType] struct {
3031

3132
func New[ConflictID, ResourceID IDType](id ConflictID, parents *advancedset.AdvancedSet[ConflictID], conflictSets map[ResourceID]*Set[ConflictID, ResourceID], initialWeight *weight.Weight) *Conflict[ConflictID, ResourceID] {
3233
c := &Conflict[ConflictID, ResourceID]{
33-
PreferredInsteadUpdated: event.New2[*Conflict[ConflictID, ResourceID], TriggerContext[ConflictID]](),
34+
PreferredInsteadUpdated: event.New2[*Conflict[ConflictID, ResourceID], reentrantmutex.ThreadID](),
3435
id: id,
3536
parents: parents,
3637
children: advancedset.New[*Conflict[ConflictID, ResourceID]](),
@@ -39,9 +40,9 @@ func New[ConflictID, ResourceID IDType](id ConflictID, parents *advancedset.Adva
3940
}
4041

4142
c.conflictingConflicts = NewSortedSet[ConflictID, ResourceID](c)
42-
c.conflictingConflicts.HeaviestPreferredMemberUpdated.Hook(func(eventConflict *Conflict[ConflictID, ResourceID], visitedConflicts TriggerContext[ConflictID]) {
43-
fmt.Println(c.ID(), "prefers", eventConflict.ID())
44-
c.PreferredInsteadUpdated.Trigger(eventConflict, visitedConflicts)
43+
c.conflictingConflicts.HeaviestPreferredMemberUpdated.Hook(func(eventConflict *Conflict[ConflictID, ResourceID], threadID reentrantmutex.ThreadID) {
44+
fmt.Println(c.ID(), "prefers", eventConflict.ID(), threadID)
45+
c.PreferredInsteadUpdated.Trigger(eventConflict, threadID)
4546
})
4647

4748
// add existing conflicts first, so we can correctly determine the preferred instead flag
@@ -98,15 +99,12 @@ func (c *Conflict[ConflictID, ResourceID]) Compare(other *Conflict[ConflictID, R
9899
return bytes.Compare(lo.PanicOnErr(c.id.Bytes()), lo.PanicOnErr(other.id.Bytes()))
99100
}
100101

101-
func (c *Conflict[ConflictID, ResourceID]) PreferredInstead() *Conflict[ConflictID, ResourceID] {
102-
c.mutex.RLock()
103-
defer c.mutex.RUnlock()
104-
105-
return c.conflictingConflicts.HeaviestPreferredConflict()
102+
func (c *Conflict[ConflictID, ResourceID]) PreferredInstead(optThreadID ...reentrantmutex.ThreadID) *Conflict[ConflictID, ResourceID] {
103+
return c.conflictingConflicts.HeaviestPreferredConflict(optThreadID...)
106104
}
107105

108-
func (c *Conflict[ConflictID, ResourceID]) IsPreferred() bool {
109-
return c.PreferredInstead() == c
106+
func (c *Conflict[ConflictID, ResourceID]) IsPreferred(optThreadID ...reentrantmutex.ThreadID) bool {
107+
return c.PreferredInstead(optThreadID...) == c
110108
}
111109

112110
func (c *Conflict[ConflictID, ResourceID]) String() string {

packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedset.go

Lines changed: 54 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,12 @@ package conflict
22

33
import (
44
"fmt"
5-
"math/rand"
65
"sync"
76
"sync/atomic"
87

8+
"github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/reentrantmutex"
99
"github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/weight"
1010
"github.com/iotaledger/hive.go/ds/shrinkingmap"
11-
"github.com/iotaledger/hive.go/ds/types"
1211
"github.com/iotaledger/hive.go/runtime/event"
1312
"github.com/iotaledger/hive.go/runtime/syncutils"
1413
"github.com/iotaledger/hive.go/stringify"
@@ -17,7 +16,7 @@ import (
1716
// SortedSet is a set of Conflicts that is sorted by their weight.
1817
type SortedSet[ConflictID, ResourceID IDType] struct {
1918
// HeaviestPreferredMemberUpdated is triggered when the heaviest preferred member of the SortedSet changes.
20-
HeaviestPreferredMemberUpdated *event.Event2[*Conflict[ConflictID, ResourceID], TriggerContext[ConflictID]]
19+
HeaviestPreferredMemberUpdated *event.Event2[*Conflict[ConflictID, ResourceID], reentrantmutex.ThreadID]
2120

2221
// owner is the Conflict that owns this SortedSet.
2322
owner *Conflict[ConflictID, ResourceID]
@@ -47,17 +46,18 @@ type SortedSet[ConflictID, ResourceID IDType] struct {
4746
isShutdown atomic.Bool
4847

4948
// mutex is used to synchronize access to the SortedSet.
50-
mutex sync.RWMutex
49+
mutex *reentrantmutex.ReEntrantMutex
5150
}
5251

5352
// NewSortedSet creates a new SortedSet that is owned by the given Conflict.
5453
func NewSortedSet[ConflictID, ResourceID IDType](owner *Conflict[ConflictID, ResourceID]) *SortedSet[ConflictID, ResourceID] {
5554
s := &SortedSet[ConflictID, ResourceID]{
56-
HeaviestPreferredMemberUpdated: event.New2[*Conflict[ConflictID, ResourceID], TriggerContext[ConflictID]](),
55+
HeaviestPreferredMemberUpdated: event.New2[*Conflict[ConflictID, ResourceID], reentrantmutex.ThreadID](),
5756
owner: owner,
5857
members: shrinkingmap.New[ConflictID, *sortedSetMember[ConflictID, ResourceID]](),
5958
pendingWeightUpdates: shrinkingmap.New[ConflictID, *sortedSetMember[ConflictID, ResourceID]](),
6059
pendingWeightUpdatesCounter: syncutils.NewCounter(),
60+
mutex: reentrantmutex.New(owner.ID().String()),
6161
}
6262
s.pendingWeightUpdatesSignal = sync.NewCond(&s.pendingWeightUpdatesMutex)
6363

@@ -70,9 +70,13 @@ func NewSortedSet[ConflictID, ResourceID IDType](owner *Conflict[ConflictID, Res
7070
}
7171

7272
// Add adds the given Conflict to the SortedSet.
73-
func (s *SortedSet[ConflictID, ResourceID]) Add(conflict *Conflict[ConflictID, ResourceID]) {
74-
s.mutex.Lock()
75-
defer s.mutex.Unlock()
73+
func (s *SortedSet[ConflictID, ResourceID]) Add(conflict *Conflict[ConflictID, ResourceID], optThreadID ...reentrantmutex.ThreadID) {
74+
if len(optThreadID) == 0 {
75+
optThreadID = []reentrantmutex.ThreadID{reentrantmutex.NewThreadID()}
76+
}
77+
78+
s.mutex.Lock(optThreadID[0])
79+
defer s.mutex.UnLock(optThreadID[0])
7680

7781
newMember, isNew := s.members.GetOrCreate(conflict.id, func() *sortedSetMember[ConflictID, ResourceID] {
7882
return newSortedSetMember[ConflictID, ResourceID](s, conflict)
@@ -119,17 +123,21 @@ func (s *SortedSet[ConflictID, ResourceID]) Add(conflict *Conflict[ConflictID, R
119123
}
120124
}
121125

122-
if conflict.IsPreferred() && newMember.Compare(s.heaviestPreferredMember) == weight.Heavier {
126+
if conflict.IsPreferred(optThreadID[0]) && newMember.Compare(s.heaviestPreferredMember) == weight.Heavier {
123127
s.heaviestPreferredMember = newMember
124128

125-
s.HeaviestPreferredMemberUpdated.Trigger(conflict, NewTriggerContext(conflict.ID()))
129+
s.HeaviestPreferredMemberUpdated.Trigger(conflict, optThreadID[0])
126130
}
127131
}
128132

129133
// ForEach iterates over all Conflicts of the SortedSet and calls the given callback for each of them.
130-
func (s *SortedSet[ConflictID, ResourceID]) ForEach(callback func(*Conflict[ConflictID, ResourceID]) error) error {
131-
s.mutex.RLock()
132-
defer s.mutex.RUnlock()
134+
func (s *SortedSet[ConflictID, ResourceID]) ForEach(callback func(*Conflict[ConflictID, ResourceID]) error, optThreadID ...reentrantmutex.ThreadID) error {
135+
if len(optThreadID) == 0 {
136+
optThreadID = []reentrantmutex.ThreadID{reentrantmutex.NewThreadID()}
137+
}
138+
139+
s.mutex.RLock(optThreadID[0])
140+
defer s.mutex.RUnlock(optThreadID[0])
133141

134142
for currentMember := s.heaviestMember; currentMember != nil; currentMember = currentMember.lighterMember {
135143
if err := callback(currentMember.Conflict); err != nil {
@@ -141,9 +149,13 @@ func (s *SortedSet[ConflictID, ResourceID]) ForEach(callback func(*Conflict[Conf
141149
}
142150

143151
// HeaviestConflict returns the heaviest Conflict of the SortedSet.
144-
func (s *SortedSet[ConflictID, ResourceID]) HeaviestConflict() *Conflict[ConflictID, ResourceID] {
145-
s.mutex.RLock()
146-
defer s.mutex.RUnlock()
152+
func (s *SortedSet[ConflictID, ResourceID]) HeaviestConflict(optThreadID ...reentrantmutex.ThreadID) *Conflict[ConflictID, ResourceID] {
153+
if len(optThreadID) == 0 {
154+
optThreadID = []reentrantmutex.ThreadID{reentrantmutex.NewThreadID()}
155+
}
156+
157+
s.mutex.RLock(optThreadID[0])
158+
defer s.mutex.RUnlock(optThreadID[0])
147159

148160
if s.heaviestMember == nil {
149161
return nil
@@ -153,14 +165,16 @@ func (s *SortedSet[ConflictID, ResourceID]) HeaviestConflict() *Conflict[Conflic
153165
}
154166

155167
// HeaviestPreferredConflict returns the heaviest preferred Conflict of the SortedSet.
156-
func (s *SortedSet[ConflictID, ResourceID]) HeaviestPreferredConflict() *Conflict[ConflictID, ResourceID] {
157-
a := rand.Float64()
168+
func (s *SortedSet[ConflictID, ResourceID]) HeaviestPreferredConflict(optThreadID ...reentrantmutex.ThreadID) *Conflict[ConflictID, ResourceID] {
169+
if len(optThreadID) == 0 {
170+
optThreadID = []reentrantmutex.ThreadID{reentrantmutex.NewThreadID()}
171+
}
158172

159-
fmt.Println("HeaviestPreferreConflict", s.owner.ID(), a)
160-
defer fmt.Println("unlocked HeaviestPreferreConflict", s.owner.ID(), a)
173+
fmt.Println("HeaviestPreferreConflict", s.owner.ID(), optThreadID[0])
174+
defer fmt.Println("unlocked HeaviestPreferreConflict", s.owner.ID(), optThreadID[0])
161175

162-
s.mutex.RLock()
163-
defer s.mutex.RUnlock()
176+
s.mutex.RLock(optThreadID[0])
177+
defer s.mutex.RUnlock(optThreadID[0])
164178

165179
if s.heaviestPreferredMember == nil {
166180
return nil
@@ -200,17 +214,17 @@ func (s *SortedSet[ConflictID, ResourceID]) notifyPendingWeightUpdate(member *so
200214
}
201215

202216
// notifyPreferredInsteadUpdate notifies the SortedSet about a member that changed its preferred instead flag.
203-
func (s *SortedSet[ConflictID, ResourceID]) notifyPreferredInsteadUpdate(member *sortedSetMember[ConflictID, ResourceID], preferred bool, visitedConflicts TriggerContext[ConflictID]) {
204-
fmt.Println("Write-Lock", s.owner.ID(), "notifyPreferredInsteadUpdate(", member.ID(), ",", preferred, ",", visitedConflicts, ")")
205-
defer fmt.Println("Write-Unlock", s.owner.ID(), "notifyPreferredInsteadUpdate(", member.ID(), ",", preferred, ",", visitedConflicts, ")")
217+
func (s *SortedSet[ConflictID, ResourceID]) notifyPreferredInsteadUpdate(member *sortedSetMember[ConflictID, ResourceID], preferred bool, threadID reentrantmutex.ThreadID) {
218+
fmt.Println("Write-Lock", s.owner.ID(), "notifyPreferredInsteadUpdate(", member.ID(), ",", preferred, ",", threadID, ")")
219+
defer fmt.Println("Write-Unlock", s.owner.ID(), "notifyPreferredInsteadUpdate(", member.ID(), ",", preferred, ",", threadID, ")")
206220

207-
s.mutex.Lock()
208-
defer s.mutex.Unlock()
221+
s.mutex.Lock(threadID)
222+
defer s.mutex.UnLock(threadID)
209223

210224
if preferred {
211225
if member.Compare(s.heaviestPreferredMember) == weight.Heavier {
212226
s.heaviestPreferredMember = member
213-
s.HeaviestPreferredMemberUpdated.Trigger(member.Conflict, visitedConflicts)
227+
s.HeaviestPreferredMemberUpdated.Trigger(member.Conflict, threadID)
214228
}
215229

216230
return
@@ -221,12 +235,12 @@ func (s *SortedSet[ConflictID, ResourceID]) notifyPreferredInsteadUpdate(member
221235
}
222236

223237
currentMember := member.lighterMember
224-
for currentMember.Conflict != s.owner && !currentMember.IsPreferred() && currentMember.PreferredInstead() != member.Conflict {
238+
for currentMember.Conflict != s.owner && !currentMember.IsPreferred(threadID) && currentMember.PreferredInstead(threadID) != member.Conflict {
225239
currentMember = currentMember.lighterMember
226240
}
227241

228242
s.heaviestPreferredMember = currentMember
229-
s.HeaviestPreferredMemberUpdated.Trigger(currentMember.Conflict, visitedConflicts)
243+
s.HeaviestPreferredMemberUpdated.Trigger(currentMember.Conflict, threadID)
230244
}
231245

232246
// nextPendingWeightUpdate returns the next member that needs to be updated (or nil if the shutdown flag is set).
@@ -260,21 +274,24 @@ func (s *SortedSet[ConflictID, ResourceID]) fixMemberPositionWorker() {
260274

261275
// fixMemberPosition fixes the position of the given member in the SortedSet.
262276
func (s *SortedSet[ConflictID, ResourceID]) fixMemberPosition(member *sortedSetMember[ConflictID, ResourceID]) {
277+
threadID := reentrantmutex.NewThreadID()
278+
263279
fmt.Println("Write-Lock", s.owner.ID(), "fixMemberPosition(", member.ID(), ")")
264280
defer fmt.Println("Write-Unlock", s.owner.ID(), "fixMemberPosition(", member.ID(), ")")
265281

266-
s.mutex.Lock()
267-
defer s.mutex.Unlock()
282+
s.mutex.Lock(threadID)
283+
defer s.mutex.UnLock(threadID)
268284

269-
preferredMember := s.preferredInstead(member)
285+
preferredMember := member.PreferredInstead(threadID)
270286

271287
// the member needs to be moved up in the list
272288
for currentMember := member.heavierMember; currentMember != nil && currentMember.Compare(member) == weight.Lighter; currentMember = member.heavierMember {
273289
s.swapNeighbors(member, currentMember)
274290

275291
if currentMember.ID() == preferredMember.ID() {
276292
s.heaviestPreferredMember = member
277-
s.HeaviestPreferredMemberUpdated.Trigger(member.Conflict, NewTriggerContext(s.owner.ID()))
293+
fmt.Println("TRIGGER1", threadID)
294+
s.HeaviestPreferredMemberUpdated.Trigger(member.Conflict, threadID)
278295
}
279296
}
280297

@@ -283,27 +300,16 @@ func (s *SortedSet[ConflictID, ResourceID]) fixMemberPosition(member *sortedSetM
283300
for currentMember := member.lighterMember; currentMember != nil && currentMember.Compare(member) == weight.Heavier; currentMember = member.lighterMember {
284301
s.swapNeighbors(currentMember, member)
285302

286-
if memberIsHeaviestPreferred && s.isPreferred(currentMember) {
303+
if memberIsHeaviestPreferred && currentMember.IsPreferred(threadID) {
287304
s.heaviestPreferredMember = currentMember
288-
s.HeaviestPreferredMemberUpdated.Trigger(currentMember.Conflict, TriggerContext[ConflictID]{s.owner.ID(): types.Void})
305+
fmt.Println("TRIGGER2", threadID)
306+
s.HeaviestPreferredMemberUpdated.Trigger(currentMember.Conflict, threadID)
289307

290308
memberIsHeaviestPreferred = false
291309
}
292310
}
293311
}
294312

295-
func (s *SortedSet[ConflictID, ResourceID]) preferredInstead(member *sortedSetMember[ConflictID, ResourceID]) *Conflict[ConflictID, ResourceID] {
296-
if member.Conflict == s.owner {
297-
return s.heaviestPreferredMember.Conflict
298-
}
299-
300-
return member.PreferredInstead()
301-
}
302-
303-
func (s *SortedSet[ConflictID, ResourceID]) isPreferred(member *sortedSetMember[ConflictID, ResourceID]) bool {
304-
return s.preferredInstead(member) == member.Conflict
305-
}
306-
307313
// swapNeighbors swaps the given members in the SortedSet.
308314
func (s *SortedSet[ConflictID, ResourceID]) swapNeighbors(heavierMember, lighterMember *sortedSetMember[ConflictID, ResourceID]) {
309315
if heavierMember.lighterMember != nil {

packages/protocol/engine/ledger/mempool/newconflictdag/conflict/sortedsetmember.go

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,10 @@ package conflict
22

33
import (
44
"bytes"
5-
"fmt"
65
"sync"
76

7+
"github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/reentrantmutex"
88
"github.com/iotaledger/goshimmer/packages/protocol/engine/ledger/mempool/newconflictdag/weight"
9-
"github.com/iotaledger/hive.go/ds/types"
109
"github.com/iotaledger/hive.go/lo"
1110
"github.com/iotaledger/hive.go/runtime/event"
1211
)
@@ -35,7 +34,7 @@ type sortedSetMember[ConflictID, ResourceID IDType] struct {
3534
onUpdateHook *event.Hook[func(weight.Value)]
3635

3736
// onPreferredUpdatedHook is the hook that is triggered when the preferredInstead value of the Conflict is updated.
38-
onPreferredUpdatedHook *event.Hook[func(*Conflict[ConflictID, ResourceID], TriggerContext[ConflictID])]
37+
onPreferredUpdatedHook *event.Hook[func(*Conflict[ConflictID, ResourceID], reentrantmutex.ThreadID)]
3938

4039
// Conflict is the wrapped Conflict.
4140
*Conflict[ConflictID, ResourceID]
@@ -53,8 +52,8 @@ func newSortedSetMember[ConflictID, ResourceID IDType](set *SortedSet[ConflictID
5352

5453
// do not attach to event from ourselves
5554
if set.owner != conflict {
56-
s.onPreferredUpdatedHook = conflict.PreferredInsteadUpdated.Hook(func(newPreferredConflict *Conflict[ConflictID, ResourceID], visitedConflicts TriggerContext[ConflictID]) {
57-
s.notifyPreferredInsteadUpdate(newPreferredConflict, visitedConflicts)
55+
s.onPreferredUpdatedHook = conflict.PreferredInsteadUpdated.Hook(func(newPreferredConflict *Conflict[ConflictID, ResourceID], threadID reentrantmutex.ThreadID) {
56+
s.notifyPreferredInsteadUpdate(newPreferredConflict, threadID)
5857
})
5958
}
6059

@@ -113,13 +112,6 @@ func (s *sortedSetMember[ConflictID, ResourceID]) weightUpdateApplied() bool {
113112
}
114113

115114
// notifyPreferredInsteadUpdate notifies the sortedSet that the preferred instead flag of the Conflict was updated.
116-
func (s *sortedSetMember[ConflictID, ResourceID]) notifyPreferredInsteadUpdate(newPreferredConflict *Conflict[ConflictID, ResourceID], visitedConflicts TriggerContext[ConflictID]) {
117-
if _, exists := visitedConflicts[s.sortedSet.owner.ID()]; !exists {
118-
visitedConflicts[s.ID()] = types.Void
119-
fmt.Println("notify", s.sortedSet.owner.ID(), "that", s.ID(), "prefers", newPreferredConflict.ID(), "with visited conflicts", visitedConflicts)
120-
121-
s.sortedSet.notifyPreferredInsteadUpdate(s, newPreferredConflict == s.Conflict, visitedConflicts)
122-
} else {
123-
fmt.Println("do not notify", s.sortedSet.owner.ID(), "that", s.ID(), "prefers", newPreferredConflict.ID(), "with visited conflicts", visitedConflicts)
124-
}
115+
func (s *sortedSetMember[ConflictID, ResourceID]) notifyPreferredInsteadUpdate(newPreferredConflict *Conflict[ConflictID, ResourceID], threadID reentrantmutex.ThreadID) {
116+
s.sortedSet.notifyPreferredInsteadUpdate(s, newPreferredConflict == s.Conflict, threadID)
125117
}

0 commit comments

Comments
 (0)