@@ -79,15 +79,11 @@ type Manager struct {
7979
8080 blockCache * BlockCache
8181
82- // blockStoreMtx is used by blockStoreCond
83- blockStoreMtx * sync.Mutex
84- // blockStoreCond is used to notify sync goroutine (SyncLoop) that it needs to retrieve blocks from blockStore
85- blockStoreCond * sync.Cond
82+ // blockStoreCh is used to notify sync goroutine (SyncLoop) that it needs to retrieve blocks from blockStore
83+ blockStoreCh chan struct {}
8684
87- // retrieveMtx is used by retrieveCond
88- retrieveMtx * sync.Mutex
8985 // retrieveCond is used to notify sync goroutine (SyncLoop) that it needs to retrieve data
90- retrieveCond * sync. Cond
86+ retrieveCh chan struct {}
9187
9288 logger log.Logger
9389
@@ -96,9 +92,7 @@ type Manager struct {
9692 txsAvailable <- chan struct {}
9793 doneBuildingBlock chan struct {}
9894
99- // Maintains blocks that need to be published to DA layer
100- pendingBlocks []* types.Block
101- pendingBlocksMtx * sync.RWMutex
95+ pendingBlocks * PendingBlocks
10296}
10397
10498// getInitialState tries to load lastState from Store, and if it's not available it reads GenesisDoc.
@@ -181,21 +175,17 @@ func NewManager(
181175 HeaderCh : make (chan * types.SignedHeader , channelLength ),
182176 BlockCh : make (chan * types.Block , channelLength ),
183177 blockInCh : make (chan newBlockEvent , blockInChLength ),
184- blockStoreMtx : new (sync. Mutex ),
178+ blockStoreCh : make ( chan struct {}, 1 ),
185179 blockStore : blockStore ,
186- retrieveMtx : new (sync.Mutex ),
187180 lastStateMtx : new (sync.RWMutex ),
188181 blockCache : NewBlockCache (),
182+ retrieveCh : make (chan struct {}, 1 ),
189183 logger : logger ,
190184 txsAvailable : txsAvailableCh ,
191185 doneBuildingBlock : doneBuildingCh ,
192186 buildingBlock : false ,
193- pendingBlocks : make ([]* types.Block , 0 ),
194- pendingBlocksMtx : new (sync.RWMutex ),
187+ pendingBlocks : NewPendingBlocks (),
195188 }
196- agg .retrieveCond = sync .NewCond (agg .retrieveMtx )
197- agg .blockStoreCond = sync .NewCond (agg .blockStoreMtx )
198-
199189 return agg , nil
200190}
201191
@@ -291,6 +281,9 @@ func (m *Manager) BlockSubmissionLoop(ctx context.Context) {
291281 return
292282 case <- timer .C :
293283 }
284+ if m .pendingBlocks .isEmpty () {
285+ continue
286+ }
294287 err := m .submitBlocksToDA (ctx )
295288 if err != nil {
296289 m .logger .Error ("error while submitting block to DA" , "error" , err )
@@ -308,9 +301,9 @@ func (m *Manager) SyncLoop(ctx context.Context, cancel context.CancelFunc) {
308301 for {
309302 select {
310303 case <- daTicker .C :
311- m .retrieveCond . Signal ()
304+ m .sendNonBlockingSignalToRetrieveCh ()
312305 case <- blockTicker .C :
313- m .blockStoreCond . Signal ()
306+ m .sendNonBlockingSignalToBlockStoreCh ()
314307 case blockEvent := <- m .blockInCh :
315308 block := blockEvent .block
316309 daHeight := blockEvent .daHeight
@@ -327,8 +320,8 @@ func (m *Manager) SyncLoop(ctx context.Context, cancel context.CancelFunc) {
327320 }
328321 m .blockCache .setBlock (blockHeight , block )
329322
330- m .blockStoreCond . Signal ()
331- m .retrieveCond . Signal ()
323+ m .sendNonBlockingSignalToBlockStoreCh ()
324+ m .sendNonBlockingSignalToRetrieveCh ()
332325
333326 err := m .trySyncNextBlock (ctx , daHeight )
334327 if err != nil {
@@ -342,6 +335,20 @@ func (m *Manager) SyncLoop(ctx context.Context, cancel context.CancelFunc) {
342335 }
343336}
344337
338+ func (m * Manager ) sendNonBlockingSignalToBlockStoreCh () {
339+ select {
340+ case m .blockStoreCh <- struct {}{}:
341+ default :
342+ }
343+ }
344+
345+ func (m * Manager ) sendNonBlockingSignalToRetrieveCh () {
346+ select {
347+ case m .retrieveCh <- struct {}{}:
348+ default :
349+ }
350+ }
351+
345352// trySyncNextBlock tries to progress one step (one block) in sync process.
346353//
347354// To be able to apply block and height h, we need to have its Commit. It is contained in block at height h+1.
@@ -405,30 +412,12 @@ func (m *Manager) trySyncNextBlock(ctx context.Context, daHeight uint64) error {
405412
406413// BlockStoreRetrieveLoop is responsible for retrieving blocks from the Block Store.
407414func (m * Manager ) BlockStoreRetrieveLoop (ctx context.Context ) {
408- // waitCh is used to signal the block store retrieve loop, that it should check block store for new blocks
409- // blockStoreCond can be signalled in completely async manner, and goroutine below
410- // works as some kind of "buffer" for those signals
411- waitCh := make (chan interface {})
412415 lastBlockStoreHeight := uint64 (0 )
413- go func () {
414- for {
415- // This infinite loop is expected to be stopped once the context is
416- // cancelled or throws an error and cleaned up by the GC. This is OK
417- // because it waits using a conditional which is only signaled periodically.
418- m .blockStoreMtx .Lock ()
419- m .blockStoreCond .Wait ()
420- waitCh <- nil
421- m .blockStoreMtx .Unlock ()
422- if ctx .Err () != nil {
423- return
424- }
425- }
426- }()
427416 for {
428417 select {
429418 case <- ctx .Done ():
430419 return
431- case <- waitCh :
420+ case <- m . blockStoreCh :
432421 }
433422 blockStoreHeight := m .blockStore .Height ()
434423 if blockStoreHeight > lastBlockStoreHeight {
@@ -467,30 +456,11 @@ func (m *Manager) getBlocksFromBlockStore(ctx context.Context, startHeight, endH
467456
468457// RetrieveLoop is responsible for interacting with DA layer.
469458func (m * Manager ) RetrieveLoop (ctx context.Context ) {
470- // waitCh is used to signal the retrieve loop, that it should process next blocks
471- // retrieveCond can be signalled in completely async manner, and goroutine below
472- // works as some kind of "buffer" for those signals
473- waitCh := make (chan interface {})
474- go func () {
475- for {
476- // This infinite loop is expected to be stopped once the context is
477- // cancelled or throws an error and cleaned up by the GC. This is OK
478- // because it waits using a conditional which is only signaled periodically.
479- m .retrieveMtx .Lock ()
480- m .retrieveCond .Wait ()
481- waitCh <- nil
482- m .retrieveMtx .Unlock ()
483- if ctx .Err () != nil {
484- return
485- }
486- }
487- }()
488-
489459 for {
490460 select {
491461 case <- ctx .Done ():
492462 return
493- case <- waitCh :
463+ case <- m . retrieveCh :
494464 }
495465 daHeight := atomic .LoadUint64 (& m .daHeight )
496466 err := m .processNextDABlock (ctx )
@@ -677,9 +647,7 @@ func (m *Manager) publishBlock(ctx context.Context) error {
677647 }
678648
679649 // Submit block to be published to the DA layer
680- m .pendingBlocksMtx .Lock ()
681- m .pendingBlocks = append (m .pendingBlocks , block )
682- m .pendingBlocksMtx .Unlock ()
650+ m .pendingBlocks .addPendingBlock (block )
683651
684652 // Commit the new state and block which writes to disk on the proxy app
685653 _ , _ , err = m .executor .Commit (ctx , newState , block , responses )
@@ -719,12 +687,10 @@ func (m *Manager) publishBlock(ctx context.Context) error {
719687}
720688
721689func (m * Manager ) submitBlocksToDA (ctx context.Context ) error {
722- m .pendingBlocksMtx .Lock ()
723- defer m .pendingBlocksMtx .Unlock ()
724690 submitted := false
725691 backoff := initialBackoff
726692 for attempt := 1 ; ctx .Err () == nil && ! submitted && attempt <= maxSubmitAttempts ; attempt ++ {
727- res := m .dalc .SubmitBlocks (ctx , m .pendingBlocks )
693+ res := m .dalc .SubmitBlocks (ctx , m .pendingBlocks . getPendingBlocks () )
728694 if res .Code == da .StatusSuccess {
729695 m .logger .Info ("successfully submitted Rollkit block to DA layer" , "daHeight" , res .DAHeight )
730696 submitted = true
@@ -738,7 +704,7 @@ func (m *Manager) submitBlocksToDA(ctx context.Context) error {
738704 if ! submitted {
739705 return fmt .Errorf ("failed to submit block to DA layer after %d attempts" , maxSubmitAttempts )
740706 }
741- m .pendingBlocks = make ([] * types. Block , 0 )
707+ m .pendingBlocks . resetPendingBlocks ( )
742708 return nil
743709}
744710
0 commit comments