github.com/angenalZZZ/gofunc@v0.0.0-20210507121333-48ff1be3917b/rpc/event/event_bus.go (about)

     1  package event
     2  
     3  import (
     4  	"fmt"
     5  	"reflect"
     6  	"sync"
     7  )
     8  
     9  // BusSubscriber defines subscription-related bus behavior
    10  type BusSubscriber interface {
    11  	Subscribe(topic string, fn interface{}) error
    12  	SubscribeAsync(topic string, fn interface{}, transactional bool) error
    13  	SubscribeOnce(topic string, fn interface{}) error
    14  	SubscribeOnceAsync(topic string, fn interface{}) error
    15  	Unsubscribe(topic string, handler interface{}) error
    16  }
    17  
    18  // BusPublisher defines publishing-related bus behavior
    19  type BusPublisher interface {
    20  	Publish(topic string, args ...interface{})
    21  }
    22  
    23  // BusController defines bus control behavior (checking handler's presence, synchronization)
    24  type BusController interface {
    25  	HasCallback(topic string) bool
    26  	WaitAsync()
    27  }
    28  
    29  // IBus a global interface(subscribe, publish, control) bus behavior
    30  type IBus interface {
    31  	BusController
    32  	BusSubscriber
    33  	BusPublisher
    34  }
    35  
    36  // EventBus - box for handlers and callbacks.
    37  type Bus struct {
    38  	handlers map[string][]*eventHandler
    39  	lock     sync.Mutex // a lock for the map
    40  	wg       sync.WaitGroup
    41  }
    42  
    43  type eventHandler struct {
    44  	callBack      reflect.Value
    45  	flagOnce      bool
    46  	async         bool
    47  	transactional bool
    48  	sync.Mutex    // lock for an event handler - useful for running async callbacks serially
    49  }
    50  
    51  // New returns new EventBus with empty handlers.
    52  func New() IBus {
    53  	b := &Bus{
    54  		make(map[string][]*eventHandler),
    55  		sync.Mutex{},
    56  		sync.WaitGroup{},
    57  	}
    58  	return IBus(b)
    59  }
    60  
    61  // doSubscribe handles the subscription logic and is utilized by the public Subscribe functions
    62  func (bus *Bus) doSubscribe(topic string, fn interface{}, handler *eventHandler) error {
    63  	bus.lock.Lock()
    64  	defer bus.lock.Unlock()
    65  	if !(reflect.TypeOf(fn).Kind() == reflect.Func) {
    66  		return fmt.Errorf("%s is not of type reflect.Func", reflect.TypeOf(fn).Kind())
    67  	}
    68  	bus.handlers[topic] = append(bus.handlers[topic], handler)
    69  	return nil
    70  }
    71  
    72  // Subscribe subscribes to a topic.
    73  // Returns error if `fn` is not a function.
    74  func (bus *Bus) Subscribe(topic string, fn interface{}) error {
    75  	return bus.doSubscribe(topic, fn, &eventHandler{
    76  		reflect.ValueOf(fn), false, false, false, sync.Mutex{},
    77  	})
    78  }
    79  
    80  // SubscribeAsync subscribes to a topic with an asynchronous callback
    81  // Transactional determines whether subsequent callbacks for a topic are
    82  // run serially (true) or concurrently (false)
    83  // Returns error if `fn` is not a function.
    84  func (bus *Bus) SubscribeAsync(topic string, fn interface{}, transactional bool) error {
    85  	return bus.doSubscribe(topic, fn, &eventHandler{
    86  		reflect.ValueOf(fn), false, true, transactional, sync.Mutex{},
    87  	})
    88  }
    89  
    90  // SubscribeOnce subscribes to a topic once. Handler will be removed after executing.
    91  // Returns error if `fn` is not a function.
    92  func (bus *Bus) SubscribeOnce(topic string, fn interface{}) error {
    93  	return bus.doSubscribe(topic, fn, &eventHandler{
    94  		reflect.ValueOf(fn), true, false, false, sync.Mutex{},
    95  	})
    96  }
    97  
    98  // SubscribeOnceAsync subscribes to a topic once with an asynchronous callback
    99  // Handler will be removed after executing.
   100  // Returns error if `fn` is not a function.
   101  func (bus *Bus) SubscribeOnceAsync(topic string, fn interface{}) error {
   102  	return bus.doSubscribe(topic, fn, &eventHandler{
   103  		reflect.ValueOf(fn), true, true, false, sync.Mutex{},
   104  	})
   105  }
   106  
   107  // HasCallback returns true if exists any callback subscribed to the topic.
   108  func (bus *Bus) HasCallback(topic string) bool {
   109  	bus.lock.Lock()
   110  	defer bus.lock.Unlock()
   111  	_, ok := bus.handlers[topic]
   112  	if ok {
   113  		return len(bus.handlers[topic]) > 0
   114  	}
   115  	return false
   116  }
   117  
   118  // Unsubscribe removes callback defined for a topic.
   119  // Returns error if there are no callbacks subscribed to the topic.
   120  func (bus *Bus) Unsubscribe(topic string, handler interface{}) error {
   121  	bus.lock.Lock()
   122  	defer bus.lock.Unlock()
   123  	if _, ok := bus.handlers[topic]; ok && len(bus.handlers[topic]) > 0 {
   124  		bus.removeHandler(topic, bus.findHandlerIdx(topic, reflect.ValueOf(handler)))
   125  		return nil
   126  	}
   127  	return fmt.Errorf("topic %s doesn't exist", topic)
   128  }
   129  
   130  // Publish executes callback defined for a topic. Any additional argument will be transferred to the callback.
   131  func (bus *Bus) Publish(topic string, args ...interface{}) {
   132  	bus.lock.Lock() // will unlock if handler is not found or always after setUpPublish
   133  	defer bus.lock.Unlock()
   134  	if handlers, ok := bus.handlers[topic]; ok && 0 < len(handlers) {
   135  		// Handlers slice may be changed by removeHandler and Unsubscribe during iteration,
   136  		// so make a copy and iterate the copied slice.
   137  		copyHandlers := make([]*eventHandler, len(handlers))
   138  		copy(copyHandlers, handlers)
   139  		for i, handler := range copyHandlers {
   140  			if handler.flagOnce {
   141  				bus.removeHandler(topic, i)
   142  			}
   143  			if !handler.async {
   144  				bus.doPublish(handler, args...)
   145  			} else {
   146  				bus.wg.Add(1)
   147  				if handler.transactional {
   148  					bus.lock.Unlock()
   149  					handler.Lock()
   150  					bus.lock.Lock()
   151  				}
   152  				go bus.doPublishAsync(handler, args...)
   153  			}
   154  		}
   155  	}
   156  }
   157  
   158  func (bus *Bus) doPublish(handler *eventHandler, args ...interface{}) {
   159  	passedArguments := bus.setUpPublish(handler, args...)
   160  	handler.callBack.Call(passedArguments)
   161  }
   162  
   163  func (bus *Bus) doPublishAsync(handler *eventHandler, args ...interface{}) {
   164  	defer bus.wg.Done()
   165  	if handler.transactional {
   166  		defer handler.Unlock()
   167  	}
   168  	bus.doPublish(handler, args...)
   169  }
   170  
   171  func (bus *Bus) removeHandler(topic string, idx int) {
   172  	if _, ok := bus.handlers[topic]; !ok {
   173  		return
   174  	}
   175  	l := len(bus.handlers[topic])
   176  
   177  	if !(0 <= idx && idx < l) {
   178  		return
   179  	}
   180  
   181  	copy(bus.handlers[topic][idx:], bus.handlers[topic][idx+1:])
   182  	bus.handlers[topic][l-1] = nil // or the zero value of T
   183  	bus.handlers[topic] = bus.handlers[topic][:l-1]
   184  }
   185  
   186  func (bus *Bus) findHandlerIdx(topic string, callback reflect.Value) int {
   187  	if _, ok := bus.handlers[topic]; ok {
   188  		for idx, handler := range bus.handlers[topic] {
   189  			if handler.callBack.Type() == callback.Type() &&
   190  				handler.callBack.Pointer() == callback.Pointer() {
   191  				return idx
   192  			}
   193  		}
   194  	}
   195  	return -1
   196  }
   197  
   198  func (bus *Bus) setUpPublish(callback *eventHandler, args ...interface{}) []reflect.Value {
   199  	funcType := callback.callBack.Type()
   200  	passedArguments := make([]reflect.Value, len(args))
   201  	for i, v := range args {
   202  		if v == nil {
   203  			passedArguments[i] = reflect.New(funcType.In(i)).Elem()
   204  		} else {
   205  			passedArguments[i] = reflect.ValueOf(v)
   206  		}
   207  	}
   208  
   209  	return passedArguments
   210  }
   211  
   212  // WaitAsync waits for all async callbacks to complete
   213  func (bus *Bus) WaitAsync() {
   214  	bus.wg.Wait()
   215  }