github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/tm2/pkg/events/events.go (about) 1 // Package events - Pub-Sub in go with event caching 2 package events 3 4 import ( 5 "fmt" 6 "sync" 7 8 "github.com/gnolang/gno/tm2/pkg/service" 9 ) 10 11 // All implementors must be amino-encodable. 12 type Event interface{} 13 14 // Eventable is the interface reactors and other modules must export to become 15 // eventable. 16 type Eventable interface { 17 SetEventSwitch(evsw EventSwitch) 18 } 19 20 type Fireable interface { 21 FireEvent(ev Event) 22 } 23 24 type Listenable interface { 25 // Multiple callbacks can be registered for a given listenerID. Events are 26 // called back in the order that they were registered with this function. 27 AddListener(listenerID string, cb EventCallback) 28 // Removes all callbacks that match listenerID. 29 RemoveListener(listenerID string) 30 } 31 32 // EventSwitch is the interface for synchronous pubsub, where listeners 33 // subscribe to certain events and, when an event is fired (see Fireable), 34 // notified via a callback function. 35 // All listeners are expected to perform work quickly and not block processing 36 // of the main event emitter. 37 type EventSwitch interface { 38 service.Service 39 Fireable 40 Listenable 41 } 42 43 type EventCallback func(event Event) 44 45 type listenCell struct { 46 listenerID string 47 cb EventCallback 48 } 49 50 // This simple implementation is optimized for few listeners. 51 // This is faster for few listeners, especially for FireEvent. 52 type eventSwitch struct { 53 service.BaseService 54 55 mtx sync.RWMutex 56 listeners []listenCell 57 } 58 59 func NilEventSwitch() EventSwitch { 60 return (*eventSwitch)(nil) 61 } 62 63 func NewEventSwitch() EventSwitch { 64 evsw := &eventSwitch{ 65 listeners: make([]listenCell, 0, 10), 66 } 67 evsw.BaseService = *service.NewBaseService(nil, "EventSwitch", evsw) 68 return evsw 69 } 70 71 func (evsw *eventSwitch) OnStart() error { 72 return nil 73 } 74 75 func (evsw *eventSwitch) OnStop() {} 76 77 func (evsw *eventSwitch) AddListener(listenerID string, cb EventCallback) { 78 evsw.mtx.Lock() 79 evsw.listeners = append(evsw.listeners, listenCell{listenerID, cb}) 80 evsw.mtx.Unlock() 81 } 82 83 func (evsw *eventSwitch) RemoveListener(listenerID string) { 84 evsw.mtx.Lock() 85 newlisteners := make([]listenCell, 0, len(evsw.listeners)) 86 for _, cell := range evsw.listeners { 87 if cell.listenerID != listenerID { 88 newlisteners = append(newlisteners, cell) 89 } 90 } 91 evsw.listeners = newlisteners 92 evsw.mtx.Unlock() 93 } 94 95 // FireEvent on a nil switch is a noop, but no other operations are allowed for 96 // safety. 97 func (evsw *eventSwitch) FireEvent(event Event) { 98 if evsw == nil { 99 return 100 } 101 evsw.mtx.RLock() 102 listeners := make([]listenCell, len(evsw.listeners)) 103 copy(listeners, evsw.listeners) 104 evsw.mtx.RUnlock() 105 106 for _, cell := range listeners { 107 cell.cb(event) 108 } 109 } 110 111 func (evsw *eventSwitch) String() string { 112 if evsw == nil { 113 return "nil-eventSwitch" 114 } else { 115 evsw.mtx.RLock() 116 defer evsw.mtx.RUnlock() 117 118 return fmt.Sprintf("*eventSwitch{%v}", len(evsw.listeners)) 119 } 120 }