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  }