github.com/gdamore/mangos@v1.4.0/protocol/star/star.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 star implements a new, experimental protocol called "STAR". 16 // This is like the BUS protocol, except that each member of the network 17 // automatically forwards any message it receives to any other peers. 18 // In a star network, this means that all members should receive all messages, 19 // assuming that there is a central server. Its important to ensure that 20 // the topology is free from cycles, as there is no protection against 21 // that, and cycles can lead to infinite message storms. (TODO: Add a TTL, 22 // and basic message ID / anti-replay protection.) 23 package star 24 25 import ( 26 "sync" 27 "time" 28 29 "nanomsg.org/go-mangos" 30 ) 31 32 type starEp struct { 33 ep mangos.Endpoint 34 q chan *mangos.Message 35 x *star 36 } 37 38 type star struct { 39 sock mangos.ProtocolSocket 40 eps map[uint32]*starEp 41 raw bool 42 w mangos.Waiter 43 ttl int 44 45 sync.Mutex 46 } 47 48 func (x *star) Init(sock mangos.ProtocolSocket) { 49 x.sock = sock 50 x.eps = make(map[uint32]*starEp) 51 x.ttl = 8 52 x.w.Init() 53 x.w.Add() 54 go x.sender() 55 } 56 57 func (x *star) Shutdown(expire time.Time) { 58 59 x.w.WaitAbsTimeout(expire) 60 61 x.Lock() 62 peers := x.eps 63 x.eps = make(map[uint32]*starEp) 64 x.Unlock() 65 66 for id, peer := range peers { 67 delete(peers, id) 68 mangos.DrainChannel(peer.q, expire) 69 close(peer.q) 70 } 71 } 72 73 // Bottom sender. 74 func (pe *starEp) sender() { 75 for { 76 m := <-pe.q 77 if m == nil { 78 break 79 } 80 81 if pe.ep.SendMsg(m) != nil { 82 m.Free() 83 break 84 } 85 } 86 } 87 88 func (x *star) broadcast(m *mangos.Message, sender *starEp) { 89 90 x.Lock() 91 if sender == nil || !x.raw { 92 for _, pe := range x.eps { 93 if sender == pe { 94 continue 95 } 96 m = m.Dup() 97 select { 98 case pe.q <- m: 99 default: 100 // No room on outbound queue, drop it. 101 if m != nil { 102 m.Free() 103 } 104 } 105 } 106 } 107 x.Unlock() 108 109 // Grab a local copy and send it up if we aren't originator 110 if sender != nil { 111 select { 112 case x.sock.RecvChannel() <- m: 113 case <-x.sock.CloseChannel(): 114 m.Free() 115 return 116 default: 117 // No room, so we just drop it. 118 m.Free() 119 } 120 } else { 121 // Not sending it up, so we need to release it. 122 m.Free() 123 } 124 } 125 126 func (x *star) sender() { 127 defer x.w.Done() 128 cq := x.sock.CloseChannel() 129 sq := x.sock.SendChannel() 130 131 for { 132 select { 133 case <-cq: 134 return 135 case m := <-sq: 136 if m == nil { 137 sq = x.sock.SendChannel() 138 continue 139 } 140 x.broadcast(m, nil) 141 } 142 } 143 } 144 145 func (pe *starEp) receiver() { 146 for { 147 m := pe.ep.RecvMsg() 148 if m == nil { 149 return 150 } 151 152 if len(m.Body) < 4 { 153 m.Free() 154 continue 155 } 156 if m.Body[0] != 0 || m.Body[1] != 0 || m.Body[2] != 0 { 157 // non-zero reserved fields are illegal 158 m.Free() 159 continue 160 } 161 if int(m.Body[3]) >= pe.x.ttl { // TTL expired? 162 // XXX: bump a stat 163 m.Free() 164 continue 165 } 166 m.Header = append(m.Header, 0, 0, 0, m.Body[3]+1) 167 m.Body = m.Body[4:] 168 169 // if we're in raw mode, this does only a sendup, otherwise 170 // it does both a retransmit + sendup 171 pe.x.broadcast(m, pe) 172 } 173 } 174 175 func (x *star) AddEndpoint(ep mangos.Endpoint) { 176 depth := 16 177 if i, err := x.sock.GetOption(mangos.OptionWriteQLen); err == nil { 178 depth = i.(int) 179 } 180 pe := &starEp{ep: ep, x: x, q: make(chan *mangos.Message, depth)} 181 x.Lock() 182 x.eps[ep.GetID()] = pe 183 x.Unlock() 184 go pe.sender() 185 go pe.receiver() 186 } 187 188 func (x *star) RemoveEndpoint(ep mangos.Endpoint) { 189 x.Lock() 190 if peer := x.eps[ep.GetID()]; peer != nil { 191 delete(x.eps, ep.GetID()) 192 close(peer.q) 193 } 194 x.Unlock() 195 } 196 197 func (*star) Number() uint16 { 198 return mangos.ProtoStar 199 } 200 201 func (*star) PeerNumber() uint16 { 202 return mangos.ProtoStar 203 } 204 205 func (*star) Name() string { 206 return "star" 207 } 208 209 func (*star) PeerName() string { 210 return "star" 211 } 212 213 func (x *star) SetOption(name string, v interface{}) error { 214 var ok bool 215 switch name { 216 case mangos.OptionRaw: 217 if x.raw, ok = v.(bool); !ok { 218 return mangos.ErrBadValue 219 } 220 return nil 221 case mangos.OptionTTL: 222 if ttl, ok := v.(int); !ok { 223 return mangos.ErrBadValue 224 } else if ttl < 1 || ttl > 255 { 225 return mangos.ErrBadValue 226 } else { 227 x.ttl = ttl 228 } 229 return nil 230 default: 231 return mangos.ErrBadOption 232 } 233 } 234 235 func (x *star) GetOption(name string) (interface{}, error) { 236 switch name { 237 case mangos.OptionRaw: 238 return x.raw, nil 239 case mangos.OptionTTL: 240 return x.ttl, nil 241 default: 242 return nil, mangos.ErrBadOption 243 } 244 } 245 246 func (x *star) SendHook(m *mangos.Message) bool { 247 248 if x.raw { 249 // TTL header must be present. 250 return true 251 } 252 // new message has a zero hop count 253 m.Header = append(m.Header, 0, 0, 0, 0) 254 return true 255 } 256 257 // NewSocket allocates a new Socket using the STAR protocol. 258 func NewSocket() (mangos.Socket, error) { 259 return mangos.MakeSocket(&star{}), nil 260 }