github.com/gdamore/mangos@v1.4.0/protocol/surveyor/surveyor.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 surveyor implements the SURVEYOR protocol. This sends messages 16 // out to RESPONDENT partners, and receives their responses. 17 package surveyor 18 19 import ( 20 "encoding/binary" 21 "sync" 22 "time" 23 24 "nanomsg.org/go-mangos" 25 ) 26 27 const defaultSurveyTime = time.Second 28 29 type surveyor struct { 30 sock mangos.ProtocolSocket 31 peers map[uint32]*surveyorP 32 raw bool 33 nextID uint32 34 surveyID uint32 35 duration time.Duration 36 timeout time.Time 37 timer *time.Timer 38 w mangos.Waiter 39 init sync.Once 40 ttl int 41 42 sync.Mutex 43 } 44 45 type surveyorP struct { 46 q chan *mangos.Message 47 ep mangos.Endpoint 48 x *surveyor 49 } 50 51 func (x *surveyor) Init(sock mangos.ProtocolSocket) { 52 x.sock = sock 53 x.peers = make(map[uint32]*surveyorP) 54 x.sock.SetRecvError(mangos.ErrProtoState) 55 x.timer = time.AfterFunc(x.duration, 56 func() { x.sock.SetRecvError(mangos.ErrProtoState) }) 57 x.timer.Stop() 58 x.w.Init() 59 x.w.Add() 60 go x.sender() 61 } 62 63 func (x *surveyor) Shutdown(expire time.Time) { 64 65 x.w.WaitAbsTimeout(expire) 66 x.Lock() 67 peers := x.peers 68 x.peers = make(map[uint32]*surveyorP) 69 x.Unlock() 70 71 for id, peer := range peers { 72 delete(peers, id) 73 mangos.DrainChannel(peer.q, expire) 74 close(peer.q) 75 } 76 } 77 78 func (x *surveyor) sender() { 79 defer x.w.Done() 80 cq := x.sock.CloseChannel() 81 sq := x.sock.SendChannel() 82 for { 83 var m *mangos.Message 84 select { 85 case m = <-sq: 86 if m == nil { 87 sq = x.sock.SendChannel() 88 continue 89 } 90 case <-cq: 91 return 92 } 93 94 x.Lock() 95 for _, pe := range x.peers { 96 m := m.Dup() 97 select { 98 case pe.q <- m: 99 default: 100 m.Free() 101 } 102 } 103 x.Unlock() 104 } 105 } 106 107 // When sending, we should have the survey ID in the header. 108 func (peer *surveyorP) sender() { 109 for { 110 if m := <-peer.q; m == nil { 111 break 112 } else { 113 if peer.ep.SendMsg(m) != nil { 114 m.Free() 115 return 116 } 117 } 118 } 119 } 120 121 func (peer *surveyorP) receiver() { 122 123 rq := peer.x.sock.RecvChannel() 124 cq := peer.x.sock.CloseChannel() 125 126 for { 127 m := peer.ep.RecvMsg() 128 if m == nil { 129 return 130 } 131 if len(m.Body) < 4 { 132 m.Free() 133 continue 134 } 135 136 // Get survery ID -- this will be passed in the header up 137 // to the application. It should include that in the response. 138 m.Header = append(m.Header, m.Body[:4]...) 139 m.Body = m.Body[4:] 140 141 select { 142 case rq <- m: 143 case <-cq: 144 return 145 } 146 } 147 } 148 149 func (x *surveyor) AddEndpoint(ep mangos.Endpoint) { 150 peer := &surveyorP{ep: ep, x: x, q: make(chan *mangos.Message, 1)} 151 x.Lock() 152 x.peers[ep.GetID()] = peer 153 go peer.receiver() 154 go peer.sender() 155 x.Unlock() 156 } 157 158 func (x *surveyor) RemoveEndpoint(ep mangos.Endpoint) { 159 id := ep.GetID() 160 161 x.Lock() 162 peer := x.peers[id] 163 delete(x.peers, id) 164 x.Unlock() 165 166 if peer != nil { 167 close(peer.q) 168 } 169 } 170 171 func (*surveyor) Number() uint16 { 172 return mangos.ProtoSurveyor 173 } 174 175 func (*surveyor) PeerNumber() uint16 { 176 return mangos.ProtoRespondent 177 } 178 179 func (*surveyor) Name() string { 180 return "surveyor" 181 } 182 183 func (*surveyor) PeerName() string { 184 return "respondent" 185 } 186 187 func (x *surveyor) SendHook(m *mangos.Message) bool { 188 189 if x.raw { 190 return true 191 } 192 193 x.Lock() 194 x.surveyID = x.nextID | 0x80000000 195 x.nextID++ 196 x.sock.SetRecvError(nil) 197 v := x.surveyID 198 m.Header = append(m.Header, 199 byte(v>>24), byte(v>>16), byte(v>>8), byte(v)) 200 201 if x.duration > 0 { 202 x.timer.Reset(x.duration) 203 } 204 x.Unlock() 205 206 return true 207 } 208 209 func (x *surveyor) RecvHook(m *mangos.Message) bool { 210 if x.raw { 211 return true 212 } 213 214 x.Lock() 215 defer x.Unlock() 216 217 if len(m.Header) < 4 { 218 return false 219 } 220 if binary.BigEndian.Uint32(m.Header) != x.surveyID { 221 return false 222 } 223 m.Header = m.Header[4:] 224 return true 225 } 226 227 func (x *surveyor) SetOption(name string, val interface{}) error { 228 var ok bool 229 switch name { 230 case mangos.OptionRaw: 231 if x.raw, ok = val.(bool); !ok { 232 return mangos.ErrBadValue 233 } 234 if x.raw { 235 x.timer.Stop() 236 x.sock.SetRecvError(nil) 237 } else { 238 x.sock.SetRecvError(mangos.ErrProtoState) 239 } 240 return nil 241 case mangos.OptionSurveyTime: 242 x.Lock() 243 x.duration, ok = val.(time.Duration) 244 x.Unlock() 245 if !ok { 246 return mangos.ErrBadValue 247 } 248 return nil 249 case mangos.OptionTTL: 250 // We don't do anything with this, but support it for 251 // symmetry with the respondent socket. 252 if ttl, ok := val.(int); !ok { 253 return mangos.ErrBadValue 254 } else if ttl < 1 || ttl > 255 { 255 return mangos.ErrBadValue 256 } else { 257 x.ttl = ttl 258 } 259 return nil 260 default: 261 return mangos.ErrBadOption 262 } 263 } 264 265 func (x *surveyor) GetOption(name string) (interface{}, error) { 266 switch name { 267 case mangos.OptionRaw: 268 return x.raw, nil 269 case mangos.OptionSurveyTime: 270 x.Lock() 271 d := x.duration 272 x.Unlock() 273 return d, nil 274 case mangos.OptionTTL: 275 return x.ttl, nil 276 default: 277 return nil, mangos.ErrBadOption 278 } 279 } 280 281 // NewSocket allocates a new Socket using the SURVEYOR protocol. 282 func NewSocket() (mangos.Socket, error) { 283 return mangos.MakeSocket(&surveyor{duration: defaultSurveyTime}), nil 284 }