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  }