github.com/jonasnick/go-ethereum@v0.7.12-0.20150216215225-22176f05d387/event/event.go (about)

     1  // Package event implements an event multiplexer.
     2  package event
     3  
     4  import (
     5  	"errors"
     6  	"fmt"
     7  	"reflect"
     8  	"sync"
     9  )
    10  
    11  // Subscription is implemented by event subscriptions.
    12  type Subscription interface {
    13  	// Chan returns a channel that carries events.
    14  	// Implementations should return the same channel
    15  	// for any subsequent calls to Chan.
    16  	Chan() <-chan interface{}
    17  
    18  	// Unsubscribe stops delivery of events to a subscription.
    19  	// The event channel is closed.
    20  	// Unsubscribe can be called more than once.
    21  	Unsubscribe()
    22  }
    23  
    24  // A TypeMux dispatches events to registered receivers. Receivers can be
    25  // registered to handle events of certain type. Any operation
    26  // called after mux is stopped will return ErrMuxClosed.
    27  //
    28  // The zero value is ready to use.
    29  type TypeMux struct {
    30  	mutex   sync.RWMutex
    31  	subm    map[reflect.Type][]*muxsub
    32  	stopped bool
    33  }
    34  
    35  // ErrMuxClosed is returned when Posting on a closed TypeMux.
    36  var ErrMuxClosed = errors.New("event: mux closed")
    37  
    38  // Subscribe creates a subscription for events of the given types. The
    39  // subscription's channel is closed when it is unsubscribed
    40  // or the mux is closed.
    41  func (mux *TypeMux) Subscribe(types ...interface{}) Subscription {
    42  	sub := newsub(mux)
    43  	mux.mutex.Lock()
    44  	defer mux.mutex.Unlock()
    45  	if mux.stopped {
    46  		close(sub.postC)
    47  	} else {
    48  		if mux.subm == nil {
    49  			mux.subm = make(map[reflect.Type][]*muxsub)
    50  		}
    51  		for _, t := range types {
    52  			rtyp := reflect.TypeOf(t)
    53  			oldsubs := mux.subm[rtyp]
    54  			if find(oldsubs, sub) != -1 {
    55  				panic(fmt.Sprintf("event: duplicate type %s in Subscribe", rtyp))
    56  			}
    57  			subs := make([]*muxsub, len(oldsubs)+1)
    58  			copy(subs, oldsubs)
    59  			subs[len(oldsubs)] = sub
    60  			mux.subm[rtyp] = subs
    61  		}
    62  	}
    63  	return sub
    64  }
    65  
    66  // Post sends an event to all receivers registered for the given type.
    67  // It returns ErrMuxClosed if the mux has been stopped.
    68  func (mux *TypeMux) Post(ev interface{}) error {
    69  	rtyp := reflect.TypeOf(ev)
    70  	mux.mutex.RLock()
    71  	if mux.stopped {
    72  		mux.mutex.RUnlock()
    73  		return ErrMuxClosed
    74  	}
    75  	subs := mux.subm[rtyp]
    76  	mux.mutex.RUnlock()
    77  	for _, sub := range subs {
    78  		sub.deliver(ev)
    79  	}
    80  	return nil
    81  }
    82  
    83  // Stop closes a mux. The mux can no longer be used.
    84  // Future Post calls will fail with ErrMuxClosed.
    85  // Stop blocks until all current deliveries have finished.
    86  func (mux *TypeMux) Stop() {
    87  	mux.mutex.Lock()
    88  	for _, subs := range mux.subm {
    89  		for _, sub := range subs {
    90  			sub.closewait()
    91  		}
    92  	}
    93  	mux.subm = nil
    94  	mux.stopped = true
    95  	mux.mutex.Unlock()
    96  }
    97  
    98  func (mux *TypeMux) del(s *muxsub) {
    99  	mux.mutex.Lock()
   100  	for typ, subs := range mux.subm {
   101  		if pos := find(subs, s); pos >= 0 {
   102  			if len(subs) == 1 {
   103  				delete(mux.subm, typ)
   104  			} else {
   105  				mux.subm[typ] = posdelete(subs, pos)
   106  			}
   107  		}
   108  	}
   109  	s.mux.mutex.Unlock()
   110  }
   111  
   112  func find(slice []*muxsub, item *muxsub) int {
   113  	for i, v := range slice {
   114  		if v == item {
   115  			return i
   116  		}
   117  	}
   118  	return -1
   119  }
   120  
   121  func posdelete(slice []*muxsub, pos int) []*muxsub {
   122  	news := make([]*muxsub, len(slice)-1)
   123  	copy(news[:pos], slice[:pos])
   124  	copy(news[pos:], slice[pos+1:])
   125  	return news
   126  }
   127  
   128  type muxsub struct {
   129  	mux     *TypeMux
   130  	closeMu sync.Mutex
   131  	closing chan struct{}
   132  	closed  bool
   133  
   134  	// these two are the same channel. they are stored separately so
   135  	// postC can be set to nil without affecting the return value of
   136  	// Chan.
   137  	postMu sync.RWMutex
   138  	readC  <-chan interface{}
   139  	postC  chan<- interface{}
   140  }
   141  
   142  func newsub(mux *TypeMux) *muxsub {
   143  	c := make(chan interface{})
   144  	return &muxsub{
   145  		mux:     mux,
   146  		readC:   c,
   147  		postC:   c,
   148  		closing: make(chan struct{}),
   149  	}
   150  }
   151  
   152  func (s *muxsub) Chan() <-chan interface{} {
   153  	return s.readC
   154  }
   155  
   156  func (s *muxsub) Unsubscribe() {
   157  	s.mux.del(s)
   158  	s.closewait()
   159  }
   160  
   161  func (s *muxsub) closewait() {
   162  	s.closeMu.Lock()
   163  	defer s.closeMu.Unlock()
   164  	if s.closed {
   165  		return
   166  	}
   167  	close(s.closing)
   168  	s.closed = true
   169  
   170  	s.postMu.Lock()
   171  	close(s.postC)
   172  	s.postC = nil
   173  	s.postMu.Unlock()
   174  }
   175  
   176  func (s *muxsub) deliver(ev interface{}) {
   177  	s.postMu.RLock()
   178  	select {
   179  	case s.postC <- ev:
   180  	case <-s.closing:
   181  	}
   182  	s.postMu.RUnlock()
   183  }