github.com/kivutar/go-ethereum@v1.7.4-0.20180117074026-6fdb126e9630/event/event.go (about)

     1  // Copyright 2014 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-ethereum library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  // Package event deals with subscriptions to real-time events.
    18  package event
    19  
    20  import (
    21  	"errors"
    22  	"fmt"
    23  	"reflect"
    24  	"sync"
    25  	"time"
    26  )
    27  
    28  // TypeMuxEvent is a time-tagged notification pushed to subscribers.
    29  type TypeMuxEvent struct {
    30  	Time time.Time
    31  	Data interface{}
    32  }
    33  
    34  // A TypeMux dispatches events to registered receivers. Receivers can be
    35  // registered to handle events of certain type. Any operation
    36  // called after mux is stopped will return ErrMuxClosed.
    37  //
    38  // The zero value is ready to use.
    39  //
    40  // Deprecated: use Feed
    41  type TypeMux struct {
    42  	mutex   sync.RWMutex
    43  	subm    map[reflect.Type][]*TypeMuxSubscription
    44  	stopped bool
    45  }
    46  
    47  // ErrMuxClosed is returned when Posting on a closed TypeMux.
    48  var ErrMuxClosed = errors.New("event: mux closed")
    49  
    50  // Subscribe creates a subscription for events of the given types. The
    51  // subscription's channel is closed when it is unsubscribed
    52  // or the mux is closed.
    53  func (mux *TypeMux) Subscribe(types ...interface{}) *TypeMuxSubscription {
    54  	sub := newsub(mux)
    55  	mux.mutex.Lock()
    56  	defer mux.mutex.Unlock()
    57  	if mux.stopped {
    58  		// set the status to closed so that calling Unsubscribe after this
    59  		// call will short circuit.
    60  		sub.closed = true
    61  		close(sub.postC)
    62  	} else {
    63  		if mux.subm == nil {
    64  			mux.subm = make(map[reflect.Type][]*TypeMuxSubscription)
    65  		}
    66  		for _, t := range types {
    67  			rtyp := reflect.TypeOf(t)
    68  			oldsubs := mux.subm[rtyp]
    69  			if find(oldsubs, sub) != -1 {
    70  				panic(fmt.Sprintf("event: duplicate type %s in Subscribe", rtyp))
    71  			}
    72  			subs := make([]*TypeMuxSubscription, len(oldsubs)+1)
    73  			copy(subs, oldsubs)
    74  			subs[len(oldsubs)] = sub
    75  			mux.subm[rtyp] = subs
    76  		}
    77  	}
    78  	return sub
    79  }
    80  
    81  // Post sends an event to all receivers registered for the given type.
    82  // It returns ErrMuxClosed if the mux has been stopped.
    83  func (mux *TypeMux) Post(ev interface{}) error {
    84  	event := &TypeMuxEvent{
    85  		Time: time.Now(),
    86  		Data: ev,
    87  	}
    88  	rtyp := reflect.TypeOf(ev)
    89  	mux.mutex.RLock()
    90  	if mux.stopped {
    91  		mux.mutex.RUnlock()
    92  		return ErrMuxClosed
    93  	}
    94  	subs := mux.subm[rtyp]
    95  	mux.mutex.RUnlock()
    96  	for _, sub := range subs {
    97  		sub.deliver(event)
    98  	}
    99  	return nil
   100  }
   101  
   102  // Stop closes a mux. The mux can no longer be used.
   103  // Future Post calls will fail with ErrMuxClosed.
   104  // Stop blocks until all current deliveries have finished.
   105  func (mux *TypeMux) Stop() {
   106  	mux.mutex.Lock()
   107  	for _, subs := range mux.subm {
   108  		for _, sub := range subs {
   109  			sub.closewait()
   110  		}
   111  	}
   112  	mux.subm = nil
   113  	mux.stopped = true
   114  	mux.mutex.Unlock()
   115  }
   116  
   117  func (mux *TypeMux) del(s *TypeMuxSubscription) {
   118  	mux.mutex.Lock()
   119  	for typ, subs := range mux.subm {
   120  		if pos := find(subs, s); pos >= 0 {
   121  			if len(subs) == 1 {
   122  				delete(mux.subm, typ)
   123  			} else {
   124  				mux.subm[typ] = posdelete(subs, pos)
   125  			}
   126  		}
   127  	}
   128  	s.mux.mutex.Unlock()
   129  }
   130  
   131  func find(slice []*TypeMuxSubscription, item *TypeMuxSubscription) int {
   132  	for i, v := range slice {
   133  		if v == item {
   134  			return i
   135  		}
   136  	}
   137  	return -1
   138  }
   139  
   140  func posdelete(slice []*TypeMuxSubscription, pos int) []*TypeMuxSubscription {
   141  	news := make([]*TypeMuxSubscription, len(slice)-1)
   142  	copy(news[:pos], slice[:pos])
   143  	copy(news[pos:], slice[pos+1:])
   144  	return news
   145  }
   146  
   147  // TypeMuxSubscription is a subscription established through TypeMux.
   148  type TypeMuxSubscription struct {
   149  	mux     *TypeMux
   150  	created time.Time
   151  	closeMu sync.Mutex
   152  	closing chan struct{}
   153  	closed  bool
   154  
   155  	// these two are the same channel. they are stored separately so
   156  	// postC can be set to nil without affecting the return value of
   157  	// Chan.
   158  	postMu sync.RWMutex
   159  	readC  <-chan *TypeMuxEvent
   160  	postC  chan<- *TypeMuxEvent
   161  }
   162  
   163  func newsub(mux *TypeMux) *TypeMuxSubscription {
   164  	c := make(chan *TypeMuxEvent)
   165  	return &TypeMuxSubscription{
   166  		mux:     mux,
   167  		created: time.Now(),
   168  		readC:   c,
   169  		postC:   c,
   170  		closing: make(chan struct{}),
   171  	}
   172  }
   173  
   174  func (s *TypeMuxSubscription) Chan() <-chan *TypeMuxEvent {
   175  	return s.readC
   176  }
   177  
   178  func (s *TypeMuxSubscription) Unsubscribe() {
   179  	s.mux.del(s)
   180  	s.closewait()
   181  }
   182  
   183  func (s *TypeMuxSubscription) closewait() {
   184  	s.closeMu.Lock()
   185  	defer s.closeMu.Unlock()
   186  	if s.closed {
   187  		return
   188  	}
   189  	close(s.closing)
   190  	s.closed = true
   191  
   192  	s.postMu.Lock()
   193  	close(s.postC)
   194  	s.postC = nil
   195  	s.postMu.Unlock()
   196  }
   197  
   198  func (s *TypeMuxSubscription) deliver(event *TypeMuxEvent) {
   199  	// Short circuit delivery if stale event
   200  	if s.created.After(event.Time) {
   201  		return
   202  	}
   203  	// Otherwise deliver the event
   204  	s.postMu.RLock()
   205  	defer s.postMu.RUnlock()
   206  
   207  	select {
   208  	case s.postC <- event:
   209  	case <-s.closing:
   210  	}
   211  }