github.com/mit-dci/lit@v0.0.0-20221102210550-8c3d3b49f2ce/eventbus/bus.go (about)

     1  package eventbus
     2  
     3  import (
     4  	"fmt"
     5  	"github.com/mit-dci/lit/logging"
     6  	"sync"
     7  )
     8  
     9  // An EventBus takes events and forwards them to event handlers matched by name.
    10  type EventBus struct {
    11  	handlers     map[string][]*eventhandler
    12  	eventMutexes map[string]*sync.Mutex
    13  	mutex        *sync.Mutex
    14  }
    15  
    16  // NewEventBus creates a new event bus without any event handlers.
    17  func NewEventBus() EventBus {
    18  	return EventBus{
    19  		handlers:     map[string][]*eventhandler{},
    20  		eventMutexes: map[string]*sync.Mutex{}, // Make triggering events safe.
    21  		mutex:        &sync.Mutex{},            // Make adding events/handlers safe.
    22  	}
    23  }
    24  
    25  const (
    26  
    27  	// EHANDLE_OK means that the event should not be cancelled.
    28  	EHANDLE_OK = 0
    29  
    30  	// EHANDLE_CANCEL means that the event should be cancelled.
    31  	EHANDLE_CANCEL = 1
    32  )
    33  
    34  // EventHandleResult is a flag field to represent certain things.
    35  type EventHandleResult uint8
    36  
    37  type eventhandler struct {
    38  	handleFunc func(Event) EventHandleResult
    39  	mutex      *sync.Mutex // Make sure we don't race a handler against itself.
    40  }
    41  
    42  // RegisterHandler registers an event handler function by name
    43  func (b *EventBus) RegisterHandler(eventName string, hFunc func(Event) EventHandleResult) {
    44  
    45  	b.mutex.Lock()
    46  
    47  	h := &eventhandler{
    48  		handleFunc: hFunc,
    49  		mutex:      &sync.Mutex{},
    50  	}
    51  
    52  	// We might need to make a new array of handlers.
    53  	if _, ok := b.handlers[eventName]; !ok {
    54  		b.handlers[eventName] = make([]*eventhandler, 0)
    55  		b.eventMutexes[eventName] = &sync.Mutex{}
    56  	}
    57  
    58  	b.handlers[eventName] = append(b.handlers[eventName], h)
    59  	logging.Infof("Registered handler for %s\n", eventName)
    60  
    61  	b.mutex.Unlock()
    62  
    63  }
    64  
    65  // CountHandlers is a convenience function.
    66  func (b *EventBus) CountHandlers(name string) int {
    67  	if _, ok := b.handlers[name]; !ok {
    68  		return 0
    69  	}
    70  	return len(b.handlers[name])
    71  }
    72  
    73  // Publish sends an event to the relevant event handlers.
    74  func (b *EventBus) Publish(event Event) (bool, error) {
    75  
    76  	logging.Debugf("eventbus: Published event: %s\n", event.Name())
    77  
    78  	ck := checkEventSanity(event)
    79  	if ck != nil {
    80  		return true, ck
    81  	}
    82  
    83  	name := event.Name()
    84  
    85  	// Make a copy of the handler list so we don't block for longer than we need to.
    86  	b.mutex.Lock()
    87  	eventMutex, present := b.eventMutexes[name]
    88  	if !present {
    89  		b.mutex.Unlock() // unlock it early
    90  		return true, nil
    91  	}
    92  	eventMutex.Lock()
    93  	src := b.handlers[name]
    94  	hs := make([]*eventhandler, len(src))
    95  	copy(hs, src)
    96  	b.mutex.Unlock()
    97  
    98  	// Figure out the flags.
    99  	f := event.Flags()
   100  	async := (f & EFLAG_ASYNC_UNSAFE) != 0
   101  	uncan := (f & EFLAG_UNCANCELLABLE) != 0
   102  
   103  	// Actually iterate over all the handlers and make them run.
   104  	ok := true
   105  	for _, h := range hs {
   106  
   107  		if async {
   108  
   109  			// If it's an async event, spawn a goroutine for it.  Ignore results.
   110  			go callEventHandler(h, event)
   111  
   112  		} else {
   113  
   114  			// Since it's not async we might cancel it.
   115  			res, err := callEventHandler(h, event)
   116  			if err != nil {
   117  				logging.Warnf("Error in event handler for %s: %s", name, err.Error())
   118  			}
   119  
   120  			if res == EHANDLE_CANCEL && !uncan {
   121  				ok = false
   122  			}
   123  
   124  		}
   125  
   126  	}
   127  
   128  	eventMutex.Unlock()
   129  	return ok, nil
   130  
   131  }
   132  
   133  // PublishNonblocking sends async events off to the relevant handlers witout blocking.
   134  func (b *EventBus) PublishNonblocking(event Event) error {
   135  
   136  	// Make sure it's async, if it is then we can't do it nonblockingly.
   137  	async := (event.Flags() & EFLAG_ASYNC_UNSAFE) != 0
   138  	if !async {
   139  		return fmt.Errorf("event %s not async but called on function that needs async", event.Name())
   140  	}
   141  
   142  	// This is the lazy way of spawning it.
   143  	go b.Publish(event)
   144  	return nil
   145  
   146  }
   147  
   148  func callEventHandler(handler *eventhandler, event Event) (EventHandleResult, error) {
   149  	handler.mutex.Lock()
   150  	r := handler.handleFunc(event)
   151  	handler.mutex.Unlock()
   152  	return r, nil // TODO Catch panics.
   153  }
   154  
   155  func checkEventSanity(e Event) error {
   156  	f := e.Flags()
   157  
   158  	// If it's async then the caller will return before the event handler can know if it wants to cancel the event.
   159  	if (f&EFLAG_ASYNC_UNSAFE) != 0 && (f&EFLAG_UNCANCELLABLE) == 0 {
   160  		return fmt.Errorf("event of type %s flagged as async but isn't cancellable, is it using EFLAG_ASYNC_UNSAFE instead of EFLAG_ASYNC?", e.Name())
   161  	}
   162  	return nil
   163  }