pkg.re/essentialkaos/ek.10@v12.41.0+incompatible/events/events.go (about) 1 // Package events provides methods and structs for creating event-driven systems 2 package events 3 4 // ////////////////////////////////////////////////////////////////////////////////// // 5 // // 6 // Copyright (c) 2022 ESSENTIAL KAOS // 7 // Apache License, Version 2.0 <https://www.apache.org/licenses/LICENSE-2.0> // 8 // // 9 // ////////////////////////////////////////////////////////////////////////////////// // 10 11 import ( 12 "fmt" 13 "reflect" 14 "sync" 15 ) 16 17 // ////////////////////////////////////////////////////////////////////////////////// // 18 19 // Dispatcher is event dispatcher 20 type Dispatcher struct { 21 handlers map[string]Handlers 22 mx *sync.RWMutex 23 } 24 25 // Handlers is a slice with handlers 26 type Handlers []Handler 27 28 // Handler is a function that handles an event 29 type Handler func(payload interface{}) 30 31 // ////////////////////////////////////////////////////////////////////////////////// // 32 33 var ( 34 ErrNilDispatcher = fmt.Errorf("Dispatcher wasn't created properly") 35 ErrEmptyType = fmt.Errorf("Event type is empty") 36 ErrNilHandler = fmt.Errorf("Handler must not be nil") 37 ) 38 39 // ////////////////////////////////////////////////////////////////////////////////// // 40 41 // NewDispatcher creates new event dispatcher instance 42 func NewDispatcher() *Dispatcher { 43 return &Dispatcher{ 44 handlers: make(map[string]Handlers), 45 mx: &sync.RWMutex{}, 46 } 47 } 48 49 // ////////////////////////////////////////////////////////////////////////////////// // 50 51 // AddHandler registers handler for events with given event type 52 func (d *Dispatcher) AddHandler(typ string, handler Handler) error { 53 err := validateArguments(d, typ, handler, true) 54 55 if err != nil { 56 return err 57 } 58 59 d.mx.Lock() 60 61 if d.getHandlerIndex(typ, handler) != -1 { 62 d.mx.Unlock() 63 return fmt.Errorf("Handler already registered for given event type (%s)", typ) 64 } 65 66 d.handlers[typ] = append(d.handlers[typ], handler) 67 68 d.mx.Unlock() 69 return nil 70 } 71 72 // RemoveHandler removes handler for given event type 73 func (d *Dispatcher) RemoveHandler(typ string, handler Handler) error { 74 err := validateArguments(d, typ, handler, true) 75 76 if err != nil { 77 return err 78 } 79 80 d.mx.Lock() 81 82 i := d.getHandlerIndex(typ, handler) 83 84 if i == -1 { 85 d.mx.Unlock() 86 return fmt.Errorf("Handler is not registered for given event type (%s)", typ) 87 } 88 89 copy(d.handlers[typ][i:], d.handlers[typ][i+1:]) 90 d.handlers[typ][len(d.handlers[typ])-1] = nil 91 d.handlers[typ] = d.handlers[typ][:len(d.handlers[typ])-1] 92 93 d.mx.Unlock() 94 return nil 95 } 96 97 // HasHandler returns true if given handler is registered for given event type 98 func (d *Dispatcher) HasHandler(typ string, handler Handler) bool { 99 err := validateArguments(d, typ, handler, true) 100 101 if err != nil { 102 return false 103 } 104 105 d.mx.RLock() 106 defer d.mx.RUnlock() 107 108 return d.getHandlerIndex(typ, handler) != -1 109 } 110 111 // Dispatch dispatches event with given type and payload 112 func (d *Dispatcher) Dispatch(typ string, payload interface{}) error { 113 err := validateArguments(d, typ, nil, false) 114 115 if err != nil { 116 return err 117 } 118 119 d.mx.RLock() 120 121 if d.handlers[typ] == nil { 122 d.mx.RUnlock() 123 return fmt.Errorf("No handlers for event \"%s\"", typ) 124 } 125 126 d.mx.RUnlock() 127 d.mx.RLock() 128 129 for _, h := range d.handlers[typ] { 130 go h(payload) 131 } 132 133 d.mx.RUnlock() 134 return nil 135 } 136 137 // DispatchAndWait dispatches event with given type and payload and waits 138 // until all handlers will be executed 139 func (d *Dispatcher) DispatchAndWait(typ string, payload interface{}) error { 140 err := validateArguments(d, typ, nil, false) 141 142 if err != nil { 143 return err 144 } 145 146 d.mx.RLock() 147 148 if d.handlers[typ] == nil { 149 d.mx.RUnlock() 150 return fmt.Errorf("No handlers for event \"%s\"", typ) 151 } 152 153 d.mx.RUnlock() 154 d.mx.RLock() 155 156 cur, tot := 0, len(d.handlers[typ]) 157 ch := make(chan bool, tot) 158 159 for _, h := range d.handlers[typ] { 160 go execWrapper(ch, h, payload) 161 } 162 163 d.mx.RUnlock() 164 165 MAIN: 166 for { 167 select { 168 case <-ch: 169 cur++ 170 if cur == tot { 171 break MAIN 172 } 173 } 174 } 175 176 return nil 177 } 178 179 // ////////////////////////////////////////////////////////////////////////////////// // 180 181 // getHandlerIndex returns index of handler in the slice 182 func (d *Dispatcher) getHandlerIndex(typ string, handler Handler) int { 183 hp := reflect.ValueOf(handler).Pointer() 184 185 for i, h := range d.handlers[typ] { 186 if reflect.ValueOf(h).Pointer() == hp { 187 return i 188 } 189 } 190 191 return -1 192 } 193 194 // ////////////////////////////////////////////////////////////////////////////////// // 195 196 // validateArguments validates arguments 197 func validateArguments(d *Dispatcher, typ string, handler Handler, isHandlerRequired bool) error { 198 if d == nil || d.handlers == nil { 199 return ErrNilDispatcher 200 } 201 202 if typ == "" { 203 return ErrEmptyType 204 } 205 206 if isHandlerRequired && handler == nil { 207 return ErrNilHandler 208 } 209 210 return nil 211 } 212 213 // execWrapper exec wrapper runs given handler function and sends true to the channel 214 // when function is successfully executed 215 func execWrapper(ch chan bool, handler Handler, payload interface{}) { 216 handler(payload) 217 ch <- true 218 }