github.com/chain5j/chain5j-pkg@v1.0.7/eventbus/event_bus.go (about)

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