github.com/klaytn/klaytn@v1.12.1/event/event.go (about)

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