github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/tm2/pkg/events/subscribe.go (about) 1 package events 2 3 import ( 4 "log" 5 "reflect" 6 "time" 7 ) 8 9 // Returns a synchronous event emitter. 10 func Subscribe(evsw EventSwitch, listenerID string) <-chan Event { 11 ch := make(chan Event, 0) // synchronous 12 return SubscribeOn(evsw, listenerID, ch) 13 } 14 15 // Like Subscribe, but lets the caller construct a channel. If the capacity of 16 // the provided channel is 0, it will be called synchronously; otherwise, it 17 // will drop when the capacity is reached and a select doesn't immediately 18 // send. 19 func SubscribeOn(evsw EventSwitch, listenerID string, ch chan Event) <-chan Event { 20 return SubscribeFilteredOn(evsw, listenerID, nil, ch) 21 } 22 23 func SubscribeToEvent(evsw EventSwitch, listenerID string, protoevent Event) <-chan Event { 24 ch := make(chan Event, 0) // synchronous 25 return SubscribeToEventOn(evsw, listenerID, protoevent, ch) 26 } 27 28 func SubscribeToEventOn(evsw EventSwitch, listenerID string, protoevent Event, ch chan Event) <-chan Event { 29 rt := reflect.TypeOf(protoevent) 30 return SubscribeFilteredOn(evsw, listenerID, func(event Event) bool { 31 return reflect.TypeOf(event) == rt 32 }, ch) 33 } 34 35 type EventFilter func(Event) bool 36 37 func SubscribeFiltered(evsw EventSwitch, listenerID string, filter EventFilter) <-chan Event { 38 ch := make(chan Event, 0) 39 return SubscribeFilteredOn(evsw, listenerID, filter, ch) 40 } 41 42 func SubscribeFilteredOn(evsw EventSwitch, listenerID string, filter EventFilter, ch chan Event) <-chan Event { 43 evsw.AddListener(listenerID, func(event Event) { 44 if filter != nil && !filter(event) { 45 return // filter 46 } 47 // NOTE: This callback must not block for performance. 48 if cap(ch) == 0 { 49 timeout := 10 * time.Second 50 LOOP: 51 for { 52 select { // sync 53 case ch <- event: 54 break LOOP 55 case <-evsw.Quit(): 56 close(ch) 57 break LOOP 58 case <-time.After(timeout): 59 // After a minute, print a message for debugging. 60 log.Printf("[WARN] EventSwitch subscriber %v blocked on %v for %v", listenerID, event, timeout) 61 // Exponentially back off warning messages. 62 timeout *= 2 63 } 64 } 65 } else { 66 select { 67 case ch <- event: 68 default: // async 69 evsw.RemoveListener(listenerID) // TODO log 70 close(ch) 71 } 72 } 73 }) 74 return ch 75 }