github.com/vipernet-xyz/tm@v0.34.24/libs/events/events.go (about)

     1  // Package events - Pub-Sub in go with event caching
     2  package events
     3  
     4  import (
     5  	"fmt"
     6  
     7  	"github.com/vipernet-xyz/tm/libs/service"
     8  	tmsync "github.com/vipernet-xyz/tm/libs/sync"
     9  )
    10  
    11  // ErrListenerWasRemoved is returned by AddEvent if the listener was removed.
    12  type ErrListenerWasRemoved struct {
    13  	listenerID string
    14  }
    15  
    16  // Error implements the error interface.
    17  func (e ErrListenerWasRemoved) Error() string {
    18  	return fmt.Sprintf("listener #%s was removed", e.listenerID)
    19  }
    20  
    21  // EventData is a generic event data can be typed and registered with
    22  // tendermint/go-amino via concrete implementation of this interface.
    23  type EventData interface{}
    24  
    25  // Eventable is the interface reactors and other modules must export to become
    26  // eventable.
    27  type Eventable interface {
    28  	SetEventSwitch(evsw EventSwitch)
    29  }
    30  
    31  // Fireable is the interface that wraps the FireEvent method.
    32  //
    33  // FireEvent fires an event with the given name and data.
    34  type Fireable interface {
    35  	FireEvent(event string, data EventData)
    36  }
    37  
    38  // EventSwitch is the interface for synchronous pubsub, where listeners
    39  // subscribe to certain events and, when an event is fired (see Fireable),
    40  // notified via a callback function.
    41  //
    42  // Listeners are added by calling AddListenerForEvent function.
    43  // They can be removed by calling either RemoveListenerForEvent or
    44  // RemoveListener (for all events).
    45  type EventSwitch interface {
    46  	service.Service
    47  	Fireable
    48  
    49  	AddListenerForEvent(listenerID, event string, cb EventCallback) error
    50  	RemoveListenerForEvent(event string, listenerID string)
    51  	RemoveListener(listenerID string)
    52  }
    53  
    54  type eventSwitch struct {
    55  	service.BaseService
    56  
    57  	mtx        tmsync.RWMutex
    58  	eventCells map[string]*eventCell
    59  	listeners  map[string]*eventListener
    60  }
    61  
    62  func NewEventSwitch() EventSwitch {
    63  	evsw := &eventSwitch{
    64  		eventCells: make(map[string]*eventCell),
    65  		listeners:  make(map[string]*eventListener),
    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) AddListenerForEvent(listenerID, event string, cb EventCallback) error {
    78  	// Get/Create eventCell and listener.
    79  	evsw.mtx.Lock()
    80  	eventCell := evsw.eventCells[event]
    81  	if eventCell == nil {
    82  		eventCell = newEventCell()
    83  		evsw.eventCells[event] = eventCell
    84  	}
    85  	listener := evsw.listeners[listenerID]
    86  	if listener == nil {
    87  		listener = newEventListener(listenerID)
    88  		evsw.listeners[listenerID] = listener
    89  	}
    90  	evsw.mtx.Unlock()
    91  
    92  	// Add event and listener.
    93  	if err := listener.AddEvent(event); err != nil {
    94  		return err
    95  	}
    96  	eventCell.AddListener(listenerID, cb)
    97  
    98  	return nil
    99  }
   100  
   101  func (evsw *eventSwitch) RemoveListener(listenerID string) {
   102  	// Get and remove listener.
   103  	evsw.mtx.RLock()
   104  	listener := evsw.listeners[listenerID]
   105  	evsw.mtx.RUnlock()
   106  	if listener == nil {
   107  		return
   108  	}
   109  
   110  	evsw.mtx.Lock()
   111  	delete(evsw.listeners, listenerID)
   112  	evsw.mtx.Unlock()
   113  
   114  	// Remove callback for each event.
   115  	listener.SetRemoved()
   116  	for _, event := range listener.GetEvents() {
   117  		evsw.RemoveListenerForEvent(event, listenerID)
   118  	}
   119  }
   120  
   121  func (evsw *eventSwitch) RemoveListenerForEvent(event string, listenerID string) {
   122  	// Get eventCell
   123  	evsw.mtx.Lock()
   124  	eventCell := evsw.eventCells[event]
   125  	evsw.mtx.Unlock()
   126  
   127  	if eventCell == nil {
   128  		return
   129  	}
   130  
   131  	// Remove listenerID from eventCell
   132  	numListeners := eventCell.RemoveListener(listenerID)
   133  
   134  	// Maybe garbage collect eventCell.
   135  	if numListeners == 0 {
   136  		// Lock again and double check.
   137  		evsw.mtx.Lock()      // OUTER LOCK
   138  		eventCell.mtx.Lock() // INNER LOCK
   139  		if len(eventCell.listeners) == 0 {
   140  			delete(evsw.eventCells, event)
   141  		}
   142  		eventCell.mtx.Unlock() // INNER LOCK
   143  		evsw.mtx.Unlock()      // OUTER LOCK
   144  	}
   145  }
   146  
   147  func (evsw *eventSwitch) FireEvent(event string, data EventData) {
   148  	// Get the eventCell
   149  	evsw.mtx.RLock()
   150  	eventCell := evsw.eventCells[event]
   151  	evsw.mtx.RUnlock()
   152  
   153  	if eventCell == nil {
   154  		return
   155  	}
   156  
   157  	// Fire event for all listeners in eventCell
   158  	eventCell.FireEvent(data)
   159  }
   160  
   161  //-----------------------------------------------------------------------------
   162  
   163  // eventCell handles keeping track of listener callbacks for a given event.
   164  type eventCell struct {
   165  	mtx       tmsync.RWMutex
   166  	listeners map[string]EventCallback
   167  }
   168  
   169  func newEventCell() *eventCell {
   170  	return &eventCell{
   171  		listeners: make(map[string]EventCallback),
   172  	}
   173  }
   174  
   175  func (cell *eventCell) AddListener(listenerID string, cb EventCallback) {
   176  	cell.mtx.Lock()
   177  	cell.listeners[listenerID] = cb
   178  	cell.mtx.Unlock()
   179  }
   180  
   181  func (cell *eventCell) RemoveListener(listenerID string) int {
   182  	cell.mtx.Lock()
   183  	delete(cell.listeners, listenerID)
   184  	numListeners := len(cell.listeners)
   185  	cell.mtx.Unlock()
   186  	return numListeners
   187  }
   188  
   189  func (cell *eventCell) FireEvent(data EventData) {
   190  	cell.mtx.RLock()
   191  	eventCallbacks := make([]EventCallback, 0, len(cell.listeners))
   192  	for _, cb := range cell.listeners {
   193  		eventCallbacks = append(eventCallbacks, cb)
   194  	}
   195  	cell.mtx.RUnlock()
   196  
   197  	for _, cb := range eventCallbacks {
   198  		cb(data)
   199  	}
   200  }
   201  
   202  //-----------------------------------------------------------------------------
   203  
   204  type EventCallback func(data EventData)
   205  
   206  type eventListener struct {
   207  	id string
   208  
   209  	mtx     tmsync.RWMutex
   210  	removed bool
   211  	events  []string
   212  }
   213  
   214  func newEventListener(id string) *eventListener {
   215  	return &eventListener{
   216  		id:      id,
   217  		removed: false,
   218  		events:  nil,
   219  	}
   220  }
   221  
   222  func (evl *eventListener) AddEvent(event string) error {
   223  	evl.mtx.Lock()
   224  
   225  	if evl.removed {
   226  		evl.mtx.Unlock()
   227  		return ErrListenerWasRemoved{listenerID: evl.id}
   228  	}
   229  
   230  	evl.events = append(evl.events, event)
   231  	evl.mtx.Unlock()
   232  	return nil
   233  }
   234  
   235  func (evl *eventListener) GetEvents() []string {
   236  	evl.mtx.RLock()
   237  	events := make([]string, len(evl.events))
   238  	copy(events, evl.events)
   239  	evl.mtx.RUnlock()
   240  	return events
   241  }
   242  
   243  func (evl *eventListener) SetRemoved() {
   244  	evl.mtx.Lock()
   245  	evl.removed = true
   246  	evl.mtx.Unlock()
   247  }