github.com/gdamore/mangos@v1.4.0/protocol/bus/bus.go (about) 1 // Copyright 2018 The Mangos Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use file except in compliance with the License. 5 // You may obtain a copy of the license at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Package bus implements the BUS protocol. In this protocol, participants 16 // send a message to each of their peers. 17 package bus 18 19 import ( 20 "encoding/binary" 21 "sync" 22 "time" 23 24 "nanomsg.org/go-mangos" 25 ) 26 27 type busEp struct { 28 ep mangos.Endpoint 29 q chan *mangos.Message 30 x *bus 31 } 32 33 type bus struct { 34 sock mangos.ProtocolSocket 35 peers map[uint32]*busEp 36 raw bool 37 w mangos.Waiter 38 init sync.Once 39 40 sync.Mutex 41 } 42 43 // Init implements the Protocol Init method. 44 func (x *bus) Init(sock mangos.ProtocolSocket) { 45 x.sock = sock 46 x.peers = make(map[uint32]*busEp) 47 x.w.Init() 48 x.w.Add() 49 go x.sender() 50 } 51 52 func (x *bus) Shutdown(expire time.Time) { 53 54 x.w.WaitAbsTimeout(expire) 55 56 x.Lock() 57 peers := x.peers 58 x.peers = make(map[uint32]*busEp) 59 x.Unlock() 60 61 for id, peer := range peers { 62 mangos.DrainChannel(peer.q, expire) 63 close(peer.q) 64 delete(peers, id) 65 } 66 } 67 68 // Bottom sender. 69 func (pe *busEp) peerSender() { 70 for { 71 m := <-pe.q 72 if m == nil { 73 return 74 } 75 if pe.ep.SendMsg(m) != nil { 76 m.Free() 77 return 78 } 79 } 80 } 81 82 func (x *bus) broadcast(m *mangos.Message, sender uint32) { 83 84 x.Lock() 85 for id, pe := range x.peers { 86 if sender == id { 87 continue 88 } 89 m = m.Dup() 90 91 select { 92 case pe.q <- m: 93 default: 94 // No room on outbound queue, drop it. 95 // Note that if we are passing on a linger/shutdown 96 // notification and we can't deliver due to queue 97 // full, it means we will wind up waiting the full 98 // linger time in the lower sender. Its correct, if 99 // suboptimal, behavior. 100 m.Free() 101 } 102 } 103 x.Unlock() 104 } 105 106 func (x *bus) sender() { 107 cq := x.sock.CloseChannel() 108 sq := x.sock.SendChannel() 109 defer x.w.Done() 110 for { 111 var id uint32 112 select { 113 case <-cq: 114 return 115 case m := <-sq: 116 if m == nil { 117 sq = x.sock.SendChannel() 118 continue 119 } 120 // If a header was present, it means this message is 121 // being rebroadcast. It should be a pipe ID. 122 if len(m.Header) >= 4 { 123 id = binary.BigEndian.Uint32(m.Header) 124 m.Header = m.Header[4:] 125 } 126 x.broadcast(m, id) 127 m.Free() 128 } 129 } 130 } 131 132 func (pe *busEp) receiver() { 133 134 rq := pe.x.sock.RecvChannel() 135 cq := pe.x.sock.CloseChannel() 136 137 for { 138 m := pe.ep.RecvMsg() 139 if m == nil { 140 return 141 } 142 v := pe.ep.GetID() 143 m.Header = append(m.Header, 144 byte(v>>24), byte(v>>16), byte(v>>8), byte(v)) 145 146 select { 147 case rq <- m: 148 case <-cq: 149 m.Free() 150 return 151 default: 152 // No room, so we just drop it. 153 m.Free() 154 } 155 } 156 } 157 158 func (x *bus) AddEndpoint(ep mangos.Endpoint) { 159 // Set our broadcast depth to match upper depth -- this should 160 // help avoid dropping when bursting, if we burst before we 161 // context switch. 162 depth := 16 163 if i, err := x.sock.GetOption(mangos.OptionWriteQLen); err == nil { 164 depth = i.(int) 165 } 166 pe := &busEp{ep: ep, x: x, q: make(chan *mangos.Message, depth)} 167 x.Lock() 168 x.peers[ep.GetID()] = pe 169 x.Unlock() 170 go pe.peerSender() 171 go pe.receiver() 172 } 173 174 func (x *bus) RemoveEndpoint(ep mangos.Endpoint) { 175 x.Lock() 176 if peer := x.peers[ep.GetID()]; peer != nil { 177 close(peer.q) 178 delete(x.peers, ep.GetID()) 179 } 180 x.Unlock() 181 } 182 183 func (*bus) Number() uint16 { 184 return mangos.ProtoBus 185 } 186 187 func (*bus) Name() string { 188 return "bus" 189 } 190 191 func (*bus) PeerNumber() uint16 { 192 return mangos.ProtoBus 193 } 194 195 func (*bus) PeerName() string { 196 return "bus" 197 } 198 199 func (x *bus) RecvHook(m *mangos.Message) bool { 200 if !x.raw && len(m.Header) >= 4 { 201 m.Header = m.Header[4:] 202 } 203 return true 204 } 205 206 func (x *bus) SetOption(name string, v interface{}) error { 207 var ok bool 208 switch name { 209 case mangos.OptionRaw: 210 if x.raw, ok = v.(bool); !ok { 211 return mangos.ErrBadValue 212 } 213 return nil 214 default: 215 return mangos.ErrBadOption 216 } 217 } 218 219 func (x *bus) GetOption(name string) (interface{}, error) { 220 switch name { 221 case mangos.OptionRaw: 222 return x.raw, nil 223 default: 224 return nil, mangos.ErrBadOption 225 } 226 } 227 228 // NewSocket allocates a new Socket using the BUS protocol. 229 func NewSocket() (mangos.Socket, error) { 230 return mangos.MakeSocket(&bus{}), nil 231 }