github.com/sixexorg/magnetic-ring@v0.0.0-20191119090307-31705a21e419/consense/dpoa/event.go (about) 1 package dpoa 2 3 4 import ( 5 "errors" 6 "reflect" 7 "sync" 8 ) 9 10 type Subscription interface { 11 Err() <-chan error 12 Unsubscribe() 13 } 14 15 var errBadChannel = errors.New("event: Subscribe argument does not have sendable channel type") 16 17 type Feed struct { 18 once sync.Once 19 sendLock chan struct{} 20 removeSub chan interface{} 21 sendCases caseList 22 23 mu sync.Mutex 24 inbox caseList 25 etype reflect.Type 26 closed bool 27 } 28 29 const firstSubSendCase = 1 30 31 type feedTypeError struct { 32 got, want reflect.Type 33 op string 34 } 35 36 func (e feedTypeError) Error() string { 37 return "event: wrong type in " + e.op + " got " + e.got.String() + ", want " + e.want.String() 38 } 39 40 func (f *Feed) init() { 41 f.removeSub = make(chan interface{}) 42 f.sendLock = make(chan struct{}, 1) 43 f.sendLock <- struct{}{} 44 f.sendCases = caseList{{Chan: reflect.ValueOf(f.removeSub), Dir: reflect.SelectRecv}} 45 } 46 47 func (f *Feed) Subscribe(channel interface{}) Subscription { 48 f.once.Do(f.init) 49 50 chanval := reflect.ValueOf(channel) 51 chantyp := chanval.Type() 52 if chantyp.Kind() != reflect.Chan || chantyp.ChanDir()&reflect.SendDir == 0 { 53 panic(errBadChannel) 54 } 55 sub := &feedSub{feed: f, channel: chanval, err: make(chan error, 1)} 56 57 f.mu.Lock() 58 defer f.mu.Unlock() 59 if !f.typecheck(chantyp.Elem()) { 60 panic(feedTypeError{op: "Subscribe", got: chantyp, want: reflect.ChanOf(reflect.SendDir, f.etype)}) 61 } 62 63 cas := reflect.SelectCase{Dir: reflect.SelectSend, Chan: chanval} 64 f.inbox = append(f.inbox, cas) 65 return sub 66 } 67 68 func (f *Feed) typecheck(typ reflect.Type) bool { 69 if f.etype == nil { 70 f.etype = typ 71 return true 72 } 73 return f.etype == typ 74 } 75 76 func (f *Feed) remove(sub *feedSub) { 77 ch := sub.channel.Interface() 78 f.mu.Lock() 79 index := f.inbox.find(ch) 80 if index != -1 { 81 f.inbox = f.inbox.delete(index) 82 f.mu.Unlock() 83 return 84 } 85 f.mu.Unlock() 86 87 select { 88 case f.removeSub <- ch: 89 case <-f.sendLock: 90 f.sendCases = f.sendCases.delete(f.sendCases.find(ch)) 91 f.sendLock <- struct{}{} 92 } 93 } 94 95 func (f *Feed) Send(value interface{}) (nsent int) { 96 rvalue := reflect.ValueOf(value) 97 98 f.once.Do(f.init) 99 <-f.sendLock 100 101 f.mu.Lock() 102 f.sendCases = append(f.sendCases, f.inbox...) 103 f.inbox = nil 104 105 if !f.typecheck(rvalue.Type()) { 106 f.sendLock <- struct{}{} 107 panic(feedTypeError{op: "Send", got: rvalue.Type(), want: f.etype}) 108 } 109 f.mu.Unlock() 110 111 for i := firstSubSendCase; i < len(f.sendCases); i++ { 112 f.sendCases[i].Send = rvalue 113 } 114 115 cases := f.sendCases 116 for { 117 for i := firstSubSendCase; i < len(cases); i++ { 118 if cases[i].Chan.TrySend(rvalue) { 119 nsent++ 120 cases = cases.deactivate(i) 121 i-- 122 } 123 } 124 if len(cases) == firstSubSendCase { 125 break 126 } 127 chosen, recv, _ := reflect.Select(cases) 128 if chosen == 0 { 129 index := f.sendCases.find(recv.Interface()) 130 f.sendCases = f.sendCases.delete(index) 131 if index >= 0 && index < len(cases) { 132 cases = f.sendCases[:len(cases)-1] 133 } 134 } else { 135 cases = cases.deactivate(chosen) 136 nsent++ 137 } 138 } 139 140 for i := firstSubSendCase; i < len(f.sendCases); i++ { 141 f.sendCases[i].Send = reflect.Value{} 142 } 143 f.sendLock <- struct{}{} 144 return nsent 145 } 146 147 type feedSub struct { 148 feed *Feed 149 channel reflect.Value 150 errOnce sync.Once 151 err chan error 152 } 153 154 func (sub *feedSub) Unsubscribe() { 155 sub.errOnce.Do(func() { 156 sub.feed.remove(sub) 157 close(sub.err) 158 }) 159 } 160 161 func (sub *feedSub) Err() <-chan error { 162 return sub.err 163 } 164 165 type caseList []reflect.SelectCase 166 167 func (cs caseList) find(channel interface{}) int { 168 for i, cas := range cs { 169 if cas.Chan.Interface() == channel { 170 return i 171 } 172 } 173 return -1 174 } 175 176 func (cs caseList) delete(index int) caseList { 177 return append(cs[:index], cs[index+1:]...) 178 } 179 180 func (cs caseList) deactivate(index int) caseList { 181 last := len(cs) - 1 182 cs[index], cs[last] = cs[last], cs[index] 183 return cs[:last] 184 }