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  }