github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/internal/pkg/gateway/commit/statusnotifier.go (about)

     1  /*
     2  Copyright hechain. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package commit
     8  
     9  import (
    10  	"sync"
    11  
    12  	"github.com/hechain20/hechain/core/ledger"
    13  )
    14  
    15  type statusListenerSet map[*statusListener]struct{}
    16  
    17  type statusNotifier struct {
    18  	lock            sync.Mutex
    19  	listenersByTxID map[string]statusListenerSet
    20  	closed          bool
    21  }
    22  
    23  func newStatusNotifier() *statusNotifier {
    24  	return &statusNotifier{
    25  		listenersByTxID: make(map[string]statusListenerSet),
    26  	}
    27  }
    28  
    29  func (notifier *statusNotifier) ReceiveBlock(blockEvent *ledger.CommitNotification) {
    30  	notifier.removeCompletedListeners()
    31  
    32  	for _, txInfo := range blockEvent.TxsInfo {
    33  		statusEvent := &Status{
    34  			BlockNumber:   blockEvent.BlockNumber,
    35  			TransactionID: txInfo.TxID,
    36  			Code:          txInfo.ValidationCode,
    37  		}
    38  		notifier.notify(statusEvent)
    39  	}
    40  }
    41  
    42  func (notifier *statusNotifier) removeCompletedListeners() {
    43  	notifier.lock.Lock()
    44  	defer notifier.lock.Unlock()
    45  
    46  	for transactionID, listeners := range notifier.listenersByTxID {
    47  		for listener := range listeners {
    48  			if listener.isDone() {
    49  				notifier.removeListener(transactionID, listener)
    50  			}
    51  		}
    52  	}
    53  }
    54  
    55  func (notifier *statusNotifier) notify(event *Status) {
    56  	notifier.lock.Lock()
    57  	defer notifier.lock.Unlock()
    58  
    59  	for listener := range notifier.listenersByTxID[event.TransactionID] {
    60  		listener.receive(event)
    61  		notifier.removeListener(event.TransactionID, listener)
    62  	}
    63  }
    64  
    65  func (notifier *statusNotifier) registerListener(done <-chan struct{}, transactionID string) <-chan *Status {
    66  	notifyChannel := make(chan *Status, 1) // Avoid blocking and only expect one notification per channel
    67  
    68  	notifier.lock.Lock()
    69  	defer notifier.lock.Unlock()
    70  
    71  	if notifier.closed {
    72  		close(notifyChannel)
    73  	} else {
    74  		listener := &statusListener{
    75  			done:          done,
    76  			notifyChannel: notifyChannel,
    77  		}
    78  		notifier.listenersForTxID(transactionID)[listener] = struct{}{}
    79  	}
    80  
    81  	return notifyChannel
    82  }
    83  
    84  func (notifier *statusNotifier) listenersForTxID(transactionID string) statusListenerSet {
    85  	listeners, exists := notifier.listenersByTxID[transactionID]
    86  	if !exists {
    87  		listeners = make(statusListenerSet)
    88  		notifier.listenersByTxID[transactionID] = listeners
    89  	}
    90  
    91  	return listeners
    92  }
    93  
    94  func (notifier *statusNotifier) removeListener(transactionID string, listener *statusListener) {
    95  	listener.close()
    96  
    97  	listeners := notifier.listenersByTxID[transactionID]
    98  	delete(listeners, listener)
    99  
   100  	if len(listeners) == 0 {
   101  		delete(notifier.listenersByTxID, transactionID)
   102  	}
   103  }
   104  
   105  func (notifier *statusNotifier) Close() {
   106  	notifier.lock.Lock()
   107  	defer notifier.lock.Unlock()
   108  
   109  	for _, listeners := range notifier.listenersByTxID {
   110  		for listener := range listeners {
   111  			listener.close()
   112  		}
   113  	}
   114  
   115  	notifier.listenersByTxID = nil
   116  	notifier.closed = true
   117  }
   118  
   119  type statusListener struct {
   120  	done          <-chan struct{}
   121  	notifyChannel chan<- *Status
   122  }
   123  
   124  func (listener *statusListener) isDone() bool {
   125  	select {
   126  	case <-listener.done:
   127  		return true
   128  	default:
   129  		return false
   130  	}
   131  }
   132  
   133  func (listener *statusListener) close() {
   134  	close(listener.notifyChannel)
   135  }
   136  
   137  func (listener *statusListener) receive(event *Status) {
   138  	listener.notifyChannel <- event
   139  }