bitbucket.org/number571/tendermint@v0.8.14/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  	tmsync "bitbucket.org/number571/tendermint/internal/libs/sync"
     8  	"bitbucket.org/number571/tendermint/libs/service"
     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(eventValue 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, eventValue 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, eventValue string, cb EventCallback) error {
    78  	// Get/Create eventCell and listener.
    79  	evsw.mtx.Lock()
    80  
    81  	eventCell := evsw.eventCells[eventValue]
    82  	if eventCell == nil {
    83  		eventCell = newEventCell()
    84  		evsw.eventCells[eventValue] = eventCell
    85  	}
    86  
    87  	listener := evsw.listeners[listenerID]
    88  	if listener == nil {
    89  		listener = newEventListener(listenerID)
    90  		evsw.listeners[listenerID] = listener
    91  	}
    92  
    93  	evsw.mtx.Unlock()
    94  
    95  	if err := listener.AddEvent(eventValue); err != nil {
    96  		return err
    97  	}
    98  
    99  	eventCell.AddListener(listenerID, cb)
   100  	return nil
   101  }
   102  
   103  func (evsw *eventSwitch) RemoveListener(listenerID string) {
   104  	// Get and remove listener.
   105  	evsw.mtx.RLock()
   106  	listener := evsw.listeners[listenerID]
   107  	evsw.mtx.RUnlock()
   108  	if listener == nil {
   109  		return
   110  	}
   111  
   112  	evsw.mtx.Lock()
   113  	delete(evsw.listeners, listenerID)
   114  	evsw.mtx.Unlock()
   115  
   116  	// Remove callback for each event.
   117  	listener.SetRemoved()
   118  	for _, event := range listener.GetEvents() {
   119  		evsw.RemoveListenerForEvent(event, listenerID)
   120  	}
   121  }
   122  
   123  func (evsw *eventSwitch) RemoveListenerForEvent(event string, listenerID string) {
   124  	// Get eventCell
   125  	evsw.mtx.Lock()
   126  	eventCell := evsw.eventCells[event]
   127  	evsw.mtx.Unlock()
   128  
   129  	if eventCell == nil {
   130  		return
   131  	}
   132  
   133  	// Remove listenerID from eventCell
   134  	numListeners := eventCell.RemoveListener(listenerID)
   135  
   136  	// Maybe garbage collect eventCell.
   137  	if numListeners == 0 {
   138  		// Lock again and double check.
   139  		evsw.mtx.Lock()      // OUTER LOCK
   140  		eventCell.mtx.Lock() // INNER LOCK
   141  		if len(eventCell.listeners) == 0 {
   142  			delete(evsw.eventCells, event)
   143  		}
   144  		eventCell.mtx.Unlock() // INNER LOCK
   145  		evsw.mtx.Unlock()      // OUTER LOCK
   146  	}
   147  }
   148  
   149  func (evsw *eventSwitch) FireEvent(event string, data EventData) {
   150  	// Get the eventCell
   151  	evsw.mtx.RLock()
   152  	eventCell := evsw.eventCells[event]
   153  	evsw.mtx.RUnlock()
   154  
   155  	if eventCell == nil {
   156  		return
   157  	}
   158  
   159  	// Fire event for all listeners in eventCell
   160  	eventCell.FireEvent(data)
   161  }
   162  
   163  //-----------------------------------------------------------------------------
   164  
   165  // eventCell handles keeping track of listener callbacks for a given event.
   166  type eventCell struct {
   167  	mtx       tmsync.RWMutex
   168  	listeners map[string]EventCallback
   169  }
   170  
   171  func newEventCell() *eventCell {
   172  	return &eventCell{
   173  		listeners: make(map[string]EventCallback),
   174  	}
   175  }
   176  
   177  func (cell *eventCell) AddListener(listenerID string, cb EventCallback) {
   178  	cell.mtx.Lock()
   179  	cell.listeners[listenerID] = cb
   180  	cell.mtx.Unlock()
   181  }
   182  
   183  func (cell *eventCell) RemoveListener(listenerID string) int {
   184  	cell.mtx.Lock()
   185  	delete(cell.listeners, listenerID)
   186  	numListeners := len(cell.listeners)
   187  	cell.mtx.Unlock()
   188  	return numListeners
   189  }
   190  
   191  func (cell *eventCell) FireEvent(data EventData) {
   192  	cell.mtx.RLock()
   193  	eventCallbacks := make([]EventCallback, 0, len(cell.listeners))
   194  	for _, cb := range cell.listeners {
   195  		eventCallbacks = append(eventCallbacks, cb)
   196  	}
   197  	cell.mtx.RUnlock()
   198  
   199  	for _, cb := range eventCallbacks {
   200  		cb(data)
   201  	}
   202  }
   203  
   204  //-----------------------------------------------------------------------------
   205  
   206  type EventCallback func(data EventData)
   207  
   208  type eventListener struct {
   209  	id string
   210  
   211  	mtx     tmsync.RWMutex
   212  	removed bool
   213  	events  []string
   214  }
   215  
   216  func newEventListener(id string) *eventListener {
   217  	return &eventListener{
   218  		id:      id,
   219  		removed: false,
   220  		events:  nil,
   221  	}
   222  }
   223  
   224  func (evl *eventListener) AddEvent(event string) error {
   225  	evl.mtx.Lock()
   226  
   227  	if evl.removed {
   228  		evl.mtx.Unlock()
   229  		return ErrListenerWasRemoved{listenerID: evl.id}
   230  	}
   231  
   232  	evl.events = append(evl.events, event)
   233  	evl.mtx.Unlock()
   234  	return nil
   235  }
   236  
   237  func (evl *eventListener) GetEvents() []string {
   238  	evl.mtx.RLock()
   239  	events := make([]string, len(evl.events))
   240  	copy(events, evl.events)
   241  	evl.mtx.RUnlock()
   242  	return events
   243  }
   244  
   245  func (evl *eventListener) SetRemoved() {
   246  	evl.mtx.Lock()
   247  	evl.removed = true
   248  	evl.mtx.Unlock()
   249  }