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 }