| 
									
										
										
										
											2015-07-07 02:54:22 +02:00
										 |  |  | // Copyright 2014 The go-ethereum Authors | 
					
						
							| 
									
										
										
										
											2015-07-22 18:48:40 +02:00
										 |  |  | // This file is part of the go-ethereum library. | 
					
						
							| 
									
										
										
										
											2015-07-07 02:54:22 +02:00
										 |  |  | // | 
					
						
							| 
									
										
										
										
											2015-07-23 18:35:11 +02:00
										 |  |  | // The go-ethereum library is free software: you can redistribute it and/or modify | 
					
						
							| 
									
										
										
										
											2015-07-07 02:54:22 +02:00
										 |  |  | // it under the terms of the GNU Lesser General Public License as published by | 
					
						
							|  |  |  | // the Free Software Foundation, either version 3 of the License, or | 
					
						
							|  |  |  | // (at your option) any later version. | 
					
						
							|  |  |  | // | 
					
						
							| 
									
										
										
										
											2015-07-22 18:48:40 +02:00
										 |  |  | // The go-ethereum library is distributed in the hope that it will be useful, | 
					
						
							| 
									
										
										
										
											2015-07-07 02:54:22 +02:00
										 |  |  | // but WITHOUT ANY WARRANTY; without even the implied warranty of | 
					
						
							| 
									
										
										
										
											2015-07-22 18:48:40 +02:00
										 |  |  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 
					
						
							| 
									
										
										
										
											2015-07-07 02:54:22 +02:00
										 |  |  | // GNU Lesser General Public License for more details. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // You should have received a copy of the GNU Lesser General Public License | 
					
						
							| 
									
										
										
										
											2015-07-22 18:48:40 +02:00
										 |  |  | // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. | 
					
						
							| 
									
										
										
										
											2015-07-07 02:54:22 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-19 01:39:12 +02:00
										 |  |  | // Package event deals with subscriptions to real-time events. | 
					
						
							| 
									
										
										
										
											2014-10-14 01:56:24 +02:00
										 |  |  | package event | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"errors" | 
					
						
							| 
									
										
										
										
											2014-10-16 18:59:28 +02:00
										 |  |  | 	"fmt" | 
					
						
							| 
									
										
										
										
											2014-10-14 01:56:24 +02:00
										 |  |  | 	"reflect" | 
					
						
							|  |  |  | 	"sync" | 
					
						
							| 
									
										
										
										
											2015-10-12 15:04:38 +03:00
										 |  |  | 	"time" | 
					
						
							| 
									
										
										
										
											2014-10-14 01:56:24 +02:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-10 19:02:14 +01:00
										 |  |  | // TypeMuxEvent is a time-tagged notification pushed to subscribers. | 
					
						
							|  |  |  | type TypeMuxEvent struct { | 
					
						
							| 
									
										
										
										
											2015-10-12 15:04:38 +03:00
										 |  |  | 	Time time.Time | 
					
						
							|  |  |  | 	Data interface{} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-10-14 01:56:24 +02:00
										 |  |  | // A TypeMux dispatches events to registered receivers. Receivers can be | 
					
						
							|  |  |  | // registered to handle events of certain type. Any operation | 
					
						
							|  |  |  | // called after mux is stopped will return ErrMuxClosed. | 
					
						
							| 
									
										
										
										
											2014-10-16 18:10:09 +02:00
										 |  |  | // | 
					
						
							|  |  |  | // The zero value is ready to use. | 
					
						
							| 
									
										
										
										
											2016-12-10 19:02:14 +01:00
										 |  |  | // | 
					
						
							|  |  |  | // Deprecated: use Feed | 
					
						
							| 
									
										
										
										
											2014-10-14 01:56:24 +02:00
										 |  |  | type TypeMux struct { | 
					
						
							|  |  |  | 	mutex   sync.RWMutex | 
					
						
							| 
									
										
										
										
											2016-12-10 19:02:14 +01:00
										 |  |  | 	subm    map[reflect.Type][]*TypeMuxSubscription | 
					
						
							| 
									
										
										
										
											2014-10-14 01:56:24 +02:00
										 |  |  | 	stopped bool | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-10-16 18:07:27 +02:00
										 |  |  | // ErrMuxClosed is returned when Posting on a closed TypeMux. | 
					
						
							| 
									
										
										
										
											2014-10-14 01:56:24 +02:00
										 |  |  | var ErrMuxClosed = errors.New("event: mux closed") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Subscribe creates a subscription for events of the given types. The | 
					
						
							|  |  |  | // subscription's channel is closed when it is unsubscribed | 
					
						
							|  |  |  | // or the mux is closed. | 
					
						
							| 
									
										
										
										
											2016-12-10 19:02:14 +01:00
										 |  |  | func (mux *TypeMux) Subscribe(types ...interface{}) *TypeMuxSubscription { | 
					
						
							| 
									
										
										
										
											2014-10-14 01:56:24 +02:00
										 |  |  | 	sub := newsub(mux) | 
					
						
							|  |  |  | 	mux.mutex.Lock() | 
					
						
							| 
									
										
										
										
											2014-10-16 18:59:28 +02:00
										 |  |  | 	defer mux.mutex.Unlock() | 
					
						
							| 
									
										
										
										
											2014-10-14 01:56:24 +02:00
										 |  |  | 	if mux.stopped { | 
					
						
							| 
									
										
										
										
											2016-05-12 17:57:45 +02:00
										 |  |  | 		// set the status to closed so that calling Unsubscribe after this | 
					
						
							|  |  |  | 		// call will short curuit | 
					
						
							|  |  |  | 		sub.closed = true | 
					
						
							| 
									
										
										
										
											2014-10-14 01:56:24 +02:00
										 |  |  | 		close(sub.postC) | 
					
						
							|  |  |  | 	} else { | 
					
						
							| 
									
										
										
										
											2014-10-16 18:10:09 +02:00
										 |  |  | 		if mux.subm == nil { | 
					
						
							| 
									
										
										
										
											2016-12-10 19:02:14 +01:00
										 |  |  | 			mux.subm = make(map[reflect.Type][]*TypeMuxSubscription) | 
					
						
							| 
									
										
										
										
											2014-10-16 18:10:09 +02:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2014-10-14 01:56:24 +02:00
										 |  |  | 		for _, t := range types { | 
					
						
							|  |  |  | 			rtyp := reflect.TypeOf(t) | 
					
						
							|  |  |  | 			oldsubs := mux.subm[rtyp] | 
					
						
							| 
									
										
										
										
											2014-10-16 18:59:28 +02:00
										 |  |  | 			if find(oldsubs, sub) != -1 { | 
					
						
							|  |  |  | 				panic(fmt.Sprintf("event: duplicate type %s in Subscribe", rtyp)) | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2016-12-10 19:02:14 +01:00
										 |  |  | 			subs := make([]*TypeMuxSubscription, len(oldsubs)+1) | 
					
						
							| 
									
										
										
										
											2014-10-14 01:56:24 +02:00
										 |  |  | 			copy(subs, oldsubs) | 
					
						
							|  |  |  | 			subs[len(oldsubs)] = sub | 
					
						
							|  |  |  | 			mux.subm[rtyp] = subs | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return sub | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Post sends an event to all receivers registered for the given type. | 
					
						
							|  |  |  | // It returns ErrMuxClosed if the mux has been stopped. | 
					
						
							|  |  |  | func (mux *TypeMux) Post(ev interface{}) error { | 
					
						
							| 
									
										
										
										
											2016-12-10 19:02:14 +01:00
										 |  |  | 	event := &TypeMuxEvent{ | 
					
						
							| 
									
										
										
										
											2015-10-12 15:04:38 +03:00
										 |  |  | 		Time: time.Now(), | 
					
						
							|  |  |  | 		Data: ev, | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2014-10-14 01:56:24 +02:00
										 |  |  | 	rtyp := reflect.TypeOf(ev) | 
					
						
							|  |  |  | 	mux.mutex.RLock() | 
					
						
							|  |  |  | 	if mux.stopped { | 
					
						
							|  |  |  | 		mux.mutex.RUnlock() | 
					
						
							|  |  |  | 		return ErrMuxClosed | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	subs := mux.subm[rtyp] | 
					
						
							|  |  |  | 	mux.mutex.RUnlock() | 
					
						
							|  |  |  | 	for _, sub := range subs { | 
					
						
							| 
									
										
										
										
											2015-10-12 15:04:38 +03:00
										 |  |  | 		sub.deliver(event) | 
					
						
							| 
									
										
										
										
											2014-10-14 01:56:24 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Stop closes a mux. The mux can no longer be used. | 
					
						
							|  |  |  | // Future Post calls will fail with ErrMuxClosed. | 
					
						
							|  |  |  | // Stop blocks until all current deliveries have finished. | 
					
						
							|  |  |  | func (mux *TypeMux) Stop() { | 
					
						
							|  |  |  | 	mux.mutex.Lock() | 
					
						
							|  |  |  | 	for _, subs := range mux.subm { | 
					
						
							|  |  |  | 		for _, sub := range subs { | 
					
						
							|  |  |  | 			sub.closewait() | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	mux.subm = nil | 
					
						
							|  |  |  | 	mux.stopped = true | 
					
						
							|  |  |  | 	mux.mutex.Unlock() | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-10 19:02:14 +01:00
										 |  |  | func (mux *TypeMux) del(s *TypeMuxSubscription) { | 
					
						
							| 
									
										
										
										
											2014-10-14 01:56:24 +02:00
										 |  |  | 	mux.mutex.Lock() | 
					
						
							|  |  |  | 	for typ, subs := range mux.subm { | 
					
						
							|  |  |  | 		if pos := find(subs, s); pos >= 0 { | 
					
						
							|  |  |  | 			if len(subs) == 1 { | 
					
						
							|  |  |  | 				delete(mux.subm, typ) | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				mux.subm[typ] = posdelete(subs, pos) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	s.mux.mutex.Unlock() | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-10 19:02:14 +01:00
										 |  |  | func find(slice []*TypeMuxSubscription, item *TypeMuxSubscription) int { | 
					
						
							| 
									
										
										
										
											2014-10-14 01:56:24 +02:00
										 |  |  | 	for i, v := range slice { | 
					
						
							|  |  |  | 		if v == item { | 
					
						
							|  |  |  | 			return i | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return -1 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-10 19:02:14 +01:00
										 |  |  | func posdelete(slice []*TypeMuxSubscription, pos int) []*TypeMuxSubscription { | 
					
						
							|  |  |  | 	news := make([]*TypeMuxSubscription, len(slice)-1) | 
					
						
							| 
									
										
										
										
											2014-10-14 01:56:24 +02:00
										 |  |  | 	copy(news[:pos], slice[:pos]) | 
					
						
							|  |  |  | 	copy(news[pos:], slice[pos+1:]) | 
					
						
							|  |  |  | 	return news | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-10 19:02:14 +01:00
										 |  |  | // TypeMuxSubscription is a subscription established through TypeMux. | 
					
						
							|  |  |  | type TypeMuxSubscription struct { | 
					
						
							| 
									
										
										
										
											2014-10-14 01:56:24 +02:00
										 |  |  | 	mux     *TypeMux | 
					
						
							| 
									
										
										
										
											2015-10-12 15:04:38 +03:00
										 |  |  | 	created time.Time | 
					
						
							| 
									
										
										
										
											2014-10-16 18:08:48 +02:00
										 |  |  | 	closeMu sync.Mutex | 
					
						
							| 
									
										
										
										
											2014-10-14 01:56:24 +02:00
										 |  |  | 	closing chan struct{} | 
					
						
							| 
									
										
										
										
											2014-10-16 18:08:48 +02:00
										 |  |  | 	closed  bool | 
					
						
							| 
									
										
										
										
											2014-10-14 01:56:24 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// these two are the same channel. they are stored separately so | 
					
						
							|  |  |  | 	// postC can be set to nil without affecting the return value of | 
					
						
							|  |  |  | 	// Chan. | 
					
						
							| 
									
										
										
										
											2014-10-16 18:08:48 +02:00
										 |  |  | 	postMu sync.RWMutex | 
					
						
							| 
									
										
										
										
											2016-12-10 19:02:14 +01:00
										 |  |  | 	readC  <-chan *TypeMuxEvent | 
					
						
							|  |  |  | 	postC  chan<- *TypeMuxEvent | 
					
						
							| 
									
										
										
										
											2014-10-14 01:56:24 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-10 19:02:14 +01:00
										 |  |  | func newsub(mux *TypeMux) *TypeMuxSubscription { | 
					
						
							|  |  |  | 	c := make(chan *TypeMuxEvent) | 
					
						
							|  |  |  | 	return &TypeMuxSubscription{ | 
					
						
							| 
									
										
										
										
											2014-10-14 01:56:24 +02:00
										 |  |  | 		mux:     mux, | 
					
						
							| 
									
										
										
										
											2015-10-12 15:04:38 +03:00
										 |  |  | 		created: time.Now(), | 
					
						
							| 
									
										
										
										
											2014-10-14 01:56:24 +02:00
										 |  |  | 		readC:   c, | 
					
						
							|  |  |  | 		postC:   c, | 
					
						
							|  |  |  | 		closing: make(chan struct{}), | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-10 19:02:14 +01:00
										 |  |  | func (s *TypeMuxSubscription) Chan() <-chan *TypeMuxEvent { | 
					
						
							| 
									
										
										
										
											2014-10-14 01:56:24 +02:00
										 |  |  | 	return s.readC | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-10 19:02:14 +01:00
										 |  |  | func (s *TypeMuxSubscription) Unsubscribe() { | 
					
						
							| 
									
										
										
										
											2014-10-14 01:56:24 +02:00
										 |  |  | 	s.mux.del(s) | 
					
						
							|  |  |  | 	s.closewait() | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-10 19:02:14 +01:00
										 |  |  | func (s *TypeMuxSubscription) closewait() { | 
					
						
							| 
									
										
										
										
											2014-10-16 18:08:48 +02:00
										 |  |  | 	s.closeMu.Lock() | 
					
						
							|  |  |  | 	defer s.closeMu.Unlock() | 
					
						
							|  |  |  | 	if s.closed { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2014-10-14 01:56:24 +02:00
										 |  |  | 	close(s.closing) | 
					
						
							| 
									
										
										
										
											2014-10-16 18:08:48 +02:00
										 |  |  | 	s.closed = true | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	s.postMu.Lock() | 
					
						
							| 
									
										
										
										
											2014-10-14 01:56:24 +02:00
										 |  |  | 	close(s.postC) | 
					
						
							|  |  |  | 	s.postC = nil | 
					
						
							| 
									
										
										
										
											2014-10-16 18:08:48 +02:00
										 |  |  | 	s.postMu.Unlock() | 
					
						
							| 
									
										
										
										
											2014-10-14 01:56:24 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-10 19:02:14 +01:00
										 |  |  | func (s *TypeMuxSubscription) deliver(event *TypeMuxEvent) { | 
					
						
							| 
									
										
										
										
											2015-10-12 15:04:38 +03:00
										 |  |  | 	// Short circuit delivery if stale event | 
					
						
							|  |  |  | 	if s.created.After(event.Time) { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// Otherwise deliver the event | 
					
						
							| 
									
										
										
										
											2014-10-16 18:08:48 +02:00
										 |  |  | 	s.postMu.RLock() | 
					
						
							| 
									
										
										
										
											2015-10-12 15:04:38 +03:00
										 |  |  | 	defer s.postMu.RUnlock() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-10-14 01:56:24 +02:00
										 |  |  | 	select { | 
					
						
							| 
									
										
										
										
											2015-10-12 15:04:38 +03:00
										 |  |  | 	case s.postC <- event: | 
					
						
							| 
									
										
										
										
											2014-10-14 01:56:24 +02:00
										 |  |  | 	case <-s.closing: | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } |