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

     1  // Modifications Copyright 2018 The klaytn Authors
     2  // Copyright 2015 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/feed.go (2018/06/04).
    19  // Modified and improved for the klaytn development.
    20  
    21  package event
    22  
    23  import (
    24  	"errors"
    25  	"reflect"
    26  	"sync"
    27  )
    28  
    29  var errBadChannel = errors.New("event: Subscribe argument does not have sendable channel type")
    30  
    31  // Feed implements one-to-many subscriptions where the carrier of events is a channel.
    32  // Values sent to a Feed are delivered to all subscribed channels simultaneously.
    33  //
    34  // Feeds can only be used with a single type. The type is determined by the first Send or
    35  // Subscribe operation. Subsequent calls to these methods panic if the type does not
    36  // match.
    37  //
    38  // The zero value is ready to use.
    39  type Feed struct {
    40  	once      sync.Once        // ensures that init only runs once
    41  	sendLock  chan struct{}    // sendLock has a one-element buffer and is empty when held.It protects sendCases.
    42  	removeSub chan interface{} // interrupts Send
    43  	sendCases caseList         // the active set of select cases used by Send
    44  
    45  	// The inbox holds newly subscribed channels until they are added to sendCases.
    46  	mu     sync.Mutex
    47  	inbox  caseList
    48  	etype  reflect.Type
    49  	closed bool
    50  }
    51  
    52  // This is the index of the first actual subscription channel in sendCases.
    53  // sendCases[0] is a SelectRecv case for the removeSub channel.
    54  const firstSubSendCase = 1
    55  
    56  type feedTypeError struct {
    57  	got, want reflect.Type
    58  	op        string
    59  }
    60  
    61  func (e feedTypeError) Error() string {
    62  	return "event: wrong type in " + e.op + " got " + e.got.String() + ", want " + e.want.String()
    63  }
    64  
    65  func (f *Feed) init() {
    66  	f.removeSub = make(chan interface{})
    67  	f.sendLock = make(chan struct{}, 1)
    68  	f.sendLock <- struct{}{}
    69  	f.sendCases = caseList{{Chan: reflect.ValueOf(f.removeSub), Dir: reflect.SelectRecv}}
    70  }
    71  
    72  // Subscribe adds a channel to the feed. Future sends will be delivered on the channel
    73  // until the subscription is canceled. All channels added must have the same element type.
    74  //
    75  // The channel should have ample buffer space to avoid blocking other subscribers.
    76  // Slow subscribers are not dropped.
    77  func (f *Feed) Subscribe(channel interface{}) Subscription {
    78  	f.once.Do(f.init)
    79  
    80  	chanval := reflect.ValueOf(channel)
    81  	chantyp := chanval.Type()
    82  	if chantyp.Kind() != reflect.Chan || chantyp.ChanDir()&reflect.SendDir == 0 {
    83  		panic(errBadChannel)
    84  	}
    85  	sub := &feedSub{feed: f, channel: chanval, err: make(chan error, 1)}
    86  
    87  	f.mu.Lock()
    88  	defer f.mu.Unlock()
    89  	if !f.typecheck(chantyp.Elem()) {
    90  		panic(feedTypeError{op: "Subscribe", got: chantyp, want: reflect.ChanOf(reflect.SendDir, f.etype)})
    91  	}
    92  	// Add the select case to the inbox.
    93  	// The next Send will add it to f.sendCases.
    94  	cas := reflect.SelectCase{Dir: reflect.SelectSend, Chan: chanval}
    95  	f.inbox = append(f.inbox, cas)
    96  	return sub
    97  }
    98  
    99  // note: callers must hold f.mu
   100  func (f *Feed) typecheck(typ reflect.Type) bool {
   101  	if f.etype == nil {
   102  		f.etype = typ
   103  		return true
   104  	}
   105  	return f.etype == typ
   106  }
   107  
   108  func (f *Feed) remove(sub *feedSub) {
   109  	// Delete from inbox first, which covers channels
   110  	// that have not been added to f.sendCases yet.
   111  	ch := sub.channel.Interface()
   112  	f.mu.Lock()
   113  	index := f.inbox.find(ch)
   114  	if index != -1 {
   115  		f.inbox = f.inbox.delete(index)
   116  		f.mu.Unlock()
   117  		return
   118  	}
   119  	f.mu.Unlock()
   120  
   121  	select {
   122  	case f.removeSub <- ch:
   123  		// Send will remove the channel from f.sendCases.
   124  	case <-f.sendLock:
   125  		// No Send is in progress, delete the channel now that we have the send lock.
   126  		f.sendCases = f.sendCases.delete(f.sendCases.find(ch))
   127  		f.sendLock <- struct{}{}
   128  	}
   129  }
   130  
   131  // Send delivers to all subscribed channels simultaneously.
   132  // It returns the number of subscribers that the value was sent to.
   133  func (f *Feed) Send(value interface{}) (nsent int) {
   134  	rvalue := reflect.ValueOf(value)
   135  
   136  	f.once.Do(f.init)
   137  	<-f.sendLock
   138  
   139  	// Add new cases from the inbox after taking the send lock.
   140  	f.mu.Lock()
   141  	f.sendCases = append(f.sendCases, f.inbox...)
   142  	f.inbox = nil
   143  
   144  	if !f.typecheck(rvalue.Type()) {
   145  		f.sendLock <- struct{}{}
   146  		panic(feedTypeError{op: "Send", got: rvalue.Type(), want: f.etype})
   147  	}
   148  	f.mu.Unlock()
   149  
   150  	// Set the sent value on all channels.
   151  	for i := firstSubSendCase; i < len(f.sendCases); i++ {
   152  		f.sendCases[i].Send = rvalue
   153  	}
   154  
   155  	// Send until all channels except removeSub have been chosen.
   156  	cases := f.sendCases
   157  	for {
   158  		// Fast path: try sending without blocking before adding to the select set.
   159  		// This should usually succeed if subscribers are fast enough and have free
   160  		// buffer space.
   161  		for i := firstSubSendCase; i < len(cases); i++ {
   162  			if cases[i].Chan.TrySend(rvalue) {
   163  				nsent++
   164  				cases = cases.deactivate(i)
   165  				i--
   166  			}
   167  		}
   168  		if len(cases) == firstSubSendCase {
   169  			break
   170  		}
   171  		// Select on all the receivers, waiting for them to unblock.
   172  		chosen, recv, _ := reflect.Select(cases)
   173  		if chosen == 0 /* <-f.removeSub */ {
   174  			index := f.sendCases.find(recv.Interface())
   175  			f.sendCases = f.sendCases.delete(index)
   176  			if index >= 0 && index < len(cases) {
   177  				cases = f.sendCases[:len(cases)-1]
   178  			}
   179  		} else {
   180  			cases = cases.deactivate(chosen)
   181  			nsent++
   182  		}
   183  	}
   184  
   185  	// Forget about the sent value and hand off the send lock.
   186  	for i := firstSubSendCase; i < len(f.sendCases); i++ {
   187  		f.sendCases[i].Send = reflect.Value{}
   188  	}
   189  	f.sendLock <- struct{}{}
   190  	return nsent
   191  }
   192  
   193  type feedSub struct {
   194  	feed    *Feed
   195  	channel reflect.Value
   196  	errOnce sync.Once
   197  	err     chan error
   198  }
   199  
   200  func (sub *feedSub) Unsubscribe() {
   201  	sub.errOnce.Do(func() {
   202  		sub.feed.remove(sub)
   203  		close(sub.err)
   204  	})
   205  }
   206  
   207  func (sub *feedSub) Err() <-chan error {
   208  	return sub.err
   209  }
   210  
   211  type caseList []reflect.SelectCase
   212  
   213  // find returns the index of a case containing the given channel.
   214  func (cs caseList) find(channel interface{}) int {
   215  	for i, cas := range cs {
   216  		if cas.Chan.Interface() == channel {
   217  			return i
   218  		}
   219  	}
   220  	return -1
   221  }
   222  
   223  // delete removes the given case from cs.
   224  func (cs caseList) delete(index int) caseList {
   225  	return append(cs[:index], cs[index+1:]...)
   226  }
   227  
   228  // deactivate moves the case at index into the non-accessible portion of the cs slice.
   229  func (cs caseList) deactivate(index int) caseList {
   230  	last := len(cs) - 1
   231  	cs[index], cs[last] = cs[last], cs[index]
   232  	return cs[:last]
   233  }