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 }