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

Commit 104870d

Browse files
authored
Merge pull request #973 from iotaledger/fix/drr-scheduler-shutdown-deadlock
Fix deadlock in DDR scheduler shutdown
2 parents b2c1788 + 3e2cb84 commit 104870d

File tree

1 file changed

+72
-3
lines changed
  • pkg/protocol/engine/congestioncontrol/scheduler/drr

1 file changed

+72
-3
lines changed

pkg/protocol/engine/congestioncontrol/scheduler/drr/scheduler.go

Lines changed: 72 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"fmt"
55
"math"
66
"sync"
7+
"sync/atomic"
78
"time"
89

910
"github.com/iotaledger/hive.go/core/safemath"
@@ -44,6 +45,8 @@ type Scheduler struct {
4445

4546
workersWg sync.WaitGroup
4647
shutdownSignal chan struct{}
48+
// isShutdown is true if the scheduler was shutdown.
49+
isShutdown atomic.Bool
4750

4851
blockCache *blocks.Blocks
4952

@@ -69,6 +72,11 @@ func NewProvider(opts ...options.Option[Scheduler]) module.Provider[*engine.Engi
6972
e.Events.Notarization.LatestCommitmentUpdated.Hook(func(commitment *model.Commitment) {
7073
// when the last slot of an epoch is committed, remove the queues of validators that are no longer in the committee.
7174
if s.apiProvider.APIForSlot(commitment.Slot()).TimeProvider().SlotsBeforeNextEpoch(commitment.Slot()) == 0 {
75+
if s.IsShutdown() {
76+
// if the scheduler is already shutdown, we don't need to do anything.
77+
return
78+
}
79+
7280
s.bufferMutex.Lock()
7381
defer s.bufferMutex.Unlock()
7482

@@ -102,12 +110,22 @@ func NewProvider(opts ...options.Option[Scheduler]) module.Provider[*engine.Engi
102110
s.selectBlockToScheduleWithLocking()
103111
})
104112
e.Events.Ledger.AccountCreated.Hook(func(accountID iotago.AccountID) {
113+
if s.IsShutdown() {
114+
// if the scheduler is already shutdown, we don't need to do anything.
115+
return
116+
}
117+
105118
s.bufferMutex.Lock()
106119
defer s.bufferMutex.Unlock()
107120

108121
s.createIssuer(accountID)
109122
})
110123
e.Events.Ledger.AccountDestroyed.Hook(func(accountID iotago.AccountID) {
124+
if s.IsShutdown() {
125+
// if the scheduler is already shutdown, we don't need to do anything.
126+
return
127+
}
128+
111129
s.bufferMutex.Lock()
112130
defer s.bufferMutex.Unlock()
113131

@@ -142,10 +160,13 @@ func New(subModule module.Module, apiProvider iotago.APIProvider, opts ...option
142160
}
143161

144162
func (s *Scheduler) shutdown() {
145-
s.bufferMutex.Lock()
146-
defer s.bufferMutex.Unlock()
163+
if s.isShutdown.Swap(true) {
164+
return
165+
}
147166

167+
s.bufferMutex.Lock()
148168
s.validatorBuffer.Clear()
169+
s.bufferMutex.Unlock()
149170

150171
close(s.shutdownSignal)
151172

@@ -154,6 +175,10 @@ func (s *Scheduler) shutdown() {
154175
s.StoppedEvent().Trigger()
155176
}
156177

178+
func (s *Scheduler) IsShutdown() bool {
179+
return s.isShutdown.Load()
180+
}
181+
157182
// Start starts the scheduler.
158183
func (s *Scheduler) Start() {
159184
s.shutdownSignal = make(chan struct{}, 1)
@@ -200,13 +225,23 @@ func (s *Scheduler) MaxBufferSize() int {
200225

201226
// ReadyBlocksCount returns the number of ready blocks.
202227
func (s *Scheduler) ReadyBlocksCount() int {
228+
if s.IsShutdown() {
229+
// if the scheduler is already shutdown, we return 0.
230+
return 0
231+
}
232+
203233
s.bufferMutex.RLock()
204234
defer s.bufferMutex.RUnlock()
205235

206236
return s.basicBuffer.ReadyBlocksCount()
207237
}
208238

209239
func (s *Scheduler) IsBlockIssuerReady(accountID iotago.AccountID, workScores ...iotago.WorkScore) bool {
240+
if s.IsShutdown() {
241+
// if the scheduler is already shutdown, we return false.
242+
return false
243+
}
244+
210245
s.bufferMutex.RLock()
211246
defer s.bufferMutex.RUnlock()
212247

@@ -243,6 +278,11 @@ func (s *Scheduler) AddBlock(block *blocks.Block) {
243278

244279
// Reset resets the component to a clean state as if it was created at the last commitment.
245280
func (s *Scheduler) Reset() {
281+
if s.IsShutdown() {
282+
// if the scheduler is already shutdown, we don't need to do anything.
283+
return
284+
}
285+
246286
s.bufferMutex.Lock()
247287
defer s.bufferMutex.Unlock()
248288

@@ -251,6 +291,11 @@ func (s *Scheduler) Reset() {
251291
}
252292

253293
func (s *Scheduler) enqueueBasicBlock(block *blocks.Block) {
294+
if s.IsShutdown() {
295+
// if the scheduler is already shutdown, we don't need to do anything.
296+
return
297+
}
298+
254299
s.bufferMutex.Lock()
255300
defer s.bufferMutex.Unlock()
256301

@@ -289,6 +334,11 @@ func (s *Scheduler) enqueueBasicBlock(block *blocks.Block) {
289334
}
290335

291336
func (s *Scheduler) enqueueValidationBlock(block *blocks.Block) {
337+
if s.IsShutdown() {
338+
// if the scheduler is already shutdown, we don't need to do anything.
339+
return
340+
}
341+
292342
s.bufferMutex.Lock()
293343
defer s.bufferMutex.Unlock()
294344

@@ -338,6 +388,9 @@ loop:
338388
for {
339389
select {
340390
// on close, exit the loop
391+
case <-s.shutdownSignal:
392+
break loop
393+
// on close, exit the loop
341394
case <-validatorQueue.shutdownSignal:
342395
break loop
343396
// when a block is pushed by this validator queue.
@@ -384,6 +437,11 @@ func (s *Scheduler) scheduleValidationBlock(block *blocks.Block, validatorQueue
384437
}
385438

386439
func (s *Scheduler) selectBlockToScheduleWithLocking() {
440+
if s.IsShutdown() {
441+
// if the scheduler is already shutdown, we don't need to do anything.
442+
return
443+
}
444+
387445
s.bufferMutex.Lock()
388446
defer s.bufferMutex.Unlock()
389447

@@ -424,8 +482,14 @@ func (s *Scheduler) selectBasicBlockWithoutLocking() {
424482
})
425483
}
426484

485+
start := s.basicBuffer.Current()
486+
if start == nil {
487+
// if there are no queues in the buffer, we cannot schedule anything
488+
return
489+
}
490+
427491
// increment the deficit for all issuers before schedulingIssuer one more time
428-
for q := s.basicBuffer.Current(); q != schedulingIssuer; q = s.basicBuffer.Next() {
492+
for q := start; q != schedulingIssuer; q = s.basicBuffer.Next() {
429493
issuerID := q.IssuerID()
430494
newDeficit, err := s.incrementDeficit(issuerID, 1, slot)
431495
if err != nil {
@@ -652,6 +716,11 @@ func (s *Scheduler) tryReadyValidationBlock(block *blocks.Block) {
652716
// updateChildrenWithLocking locks the buffer mutex and iterates over the direct children of the given blockID and
653717
// tries to mark them as ready.
654718
func (s *Scheduler) updateChildrenWithLocking(block *blocks.Block) {
719+
if s.IsShutdown() {
720+
// if the scheduler is already shutdown, we don't need to do anything.
721+
return
722+
}
723+
655724
s.bufferMutex.Lock()
656725
defer s.bufferMutex.Unlock()
657726

0 commit comments

Comments
 (0)