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