github.com/fff-chain/go-fff@v0.0.0-20220726032732-1c84420b8a99/event/feed.go (about)

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