vitess.io/vitess@v0.16.2/go/event/event.go (about) 1 /* 2 Copyright 2019 The Vitess Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 /* 18 Package event provides a reflect-based framework for low-frequency global 19 dispatching of events, which are values of any arbitrary type, to a set of 20 listener functions, which are usually registered by plugin packages during init(). 21 22 Listeners should do work in a separate goroutine if it might block. Dispatch 23 should be called synchronously to make sure work enters the listener's work 24 queue before moving on. After Dispatch returns, the listener is responsible for 25 arranging to flush its work queue before program termination if desired. 26 27 For example, any package can define an event type: 28 29 package mypackage 30 31 type MyEvent struct { 32 field1, field2 string 33 } 34 35 Then, any other package (e.g. a plugin) can listen for those events: 36 37 package myplugin 38 39 import ( 40 "event" 41 "mypackage" 42 ) 43 44 func onMyEvent(ev mypackage.MyEvent) { 45 // do something with ev 46 } 47 48 func init() { 49 event.AddListener(onMyEvent) 50 } 51 52 Any registered listeners that accept a single argument of type MyEvent will 53 be called when a value of type MyEvent is dispatched: 54 55 package myotherpackage 56 57 import ( 58 "event" 59 "mypackage" 60 ) 61 62 func InMediasRes() { 63 ev := mypackage.MyEvent{ 64 field1: "foo", 65 field2: "bar", 66 } 67 68 event.Dispatch(ev) 69 } 70 71 In addition, listener functions that accept an interface type will be called 72 for any dispatched value that implements the specified interface. A listener 73 that accepts `any` will be called for every event type. Listeners can also 74 accept pointer types, but they will only be called if the dispatch site calls 75 Dispatch() on a pointer. 76 */ 77 package event 78 79 import ( 80 "fmt" 81 "reflect" 82 "sync" 83 ) 84 85 var ( 86 listenersMutex sync.RWMutex // protects listeners and interfaces 87 listeners = make(map[reflect.Type][]any) 88 interfaces = make([]reflect.Type, 0) 89 ) 90 91 // BadListenerError is raised via panic() when AddListener is called with an 92 // invalid listener function. 93 type BadListenerError string 94 95 func (why BadListenerError) Error() string { 96 return fmt.Sprintf("bad listener func: %s", string(why)) 97 } 98 99 // AddListener registers a listener function that will be called when a matching 100 // event is dispatched. The type of the function's first (and only) argument 101 // declares the event type (or interface) to listen for. 102 func AddListener(fn any) { 103 listenersMutex.Lock() 104 defer listenersMutex.Unlock() 105 106 fnType := reflect.TypeOf(fn) 107 108 // check that the function type is what we think: # of inputs/outputs, etc. 109 // panic if conditions not met (because it's a programming error to have that happen) 110 switch { 111 case fnType.Kind() != reflect.Func: 112 panic(BadListenerError("listener must be a function")) 113 case fnType.NumIn() != 1: 114 panic(BadListenerError("listener must take exactly one input argument")) 115 } 116 117 // the first input parameter is the event 118 evType := fnType.In(0) 119 120 // keep a list of listeners for each event type 121 listeners[evType] = append(listeners[evType], fn) 122 123 // if eventType is an interface, store it in a separate list 124 // so we can check non-interface objects against all interfaces 125 if evType.Kind() == reflect.Interface { 126 interfaces = append(interfaces, evType) 127 } 128 } 129 130 // Dispatch sends an event to all registered listeners that were declared 131 // to accept values of the event's type, or interfaces that the value implements. 132 func Dispatch(ev any) { 133 listenersMutex.RLock() 134 defer listenersMutex.RUnlock() 135 136 evType := reflect.TypeOf(ev) 137 vals := []reflect.Value{reflect.ValueOf(ev)} 138 139 // call listeners for the actual static type 140 callListeners(evType, vals) 141 142 // also check if the type implements any of the registered interfaces 143 for _, in := range interfaces { 144 if evType.Implements(in) { 145 callListeners(in, vals) 146 } 147 } 148 } 149 150 func callListeners(t reflect.Type, vals []reflect.Value) { 151 for _, fn := range listeners[t] { 152 reflect.ValueOf(fn).Call(vals) 153 } 154 } 155 156 // Updater is an interface that events can implement to combine updating and 157 // dispatching into one call. 158 type Updater interface { 159 // Update is called by DispatchUpdate() before the event is dispatched. 160 Update(update any) 161 } 162 163 // DispatchUpdate calls Update() on the event and then dispatches it. This is a 164 // shortcut for combining updates and dispatches into a single call. 165 func DispatchUpdate(ev Updater, update any) { 166 ev.Update(update) 167 Dispatch(ev) 168 }