github.com/sberex/go-sberex@v1.8.2-0.20181113200658-ed96ac38f7d7/event/feed.go (about)

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