go.nanomsg.org/mangos/v3@v3.4.3-0.20240217232803-46464076f1f5/protocol/xsurveyor/xsurveyor.go (about) 1 // Copyright 2019 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 xsurveyor implements the SURVEYOR protocol. This sends messages 16 // out to RESPONDENT partners, and receives their responses. 17 package xsurveyor 18 19 import ( 20 "sync" 21 "time" 22 23 "go.nanomsg.org/mangos/v3/protocol" 24 ) 25 26 // Protocol identity information. 27 const ( 28 Self = protocol.ProtoSurveyor 29 Peer = protocol.ProtoRespondent 30 SelfName = "surveyor" 31 PeerName = "respondent" 32 ) 33 34 type pipe struct { 35 p protocol.Pipe 36 s *socket 37 closeQ chan struct{} 38 sendQ chan *protocol.Message 39 } 40 41 type socket struct { 42 closed bool 43 pipes map[uint32]*pipe 44 recvQLen int 45 sendQLen int 46 recvExpire time.Duration 47 recvQ chan *protocol.Message 48 closeQ chan struct{} 49 sizeQ chan struct{} 50 sync.Mutex 51 } 52 53 var ( 54 nilQ <-chan time.Time 55 ) 56 57 const defaultQLen = 128 58 59 func (s *socket) SendMsg(m *protocol.Message) error { 60 s.Lock() 61 if s.closed { 62 s.Unlock() 63 return protocol.ErrClosed 64 } 65 66 // This could benefit from optimization to avoid useless duplicates. 67 for _, p := range s.pipes { 68 m.Clone() 69 select { 70 case p.sendQ <- m: 71 default: 72 m.Free() 73 } 74 } 75 s.Unlock() 76 m.Free() 77 return nil 78 } 79 80 func (s *socket) RecvMsg() (*protocol.Message, error) { 81 // For now this uses a simple unified queue for the entire 82 // socket. Later we can look at moving this to priority queues 83 // based on socket pipes. 84 85 for { 86 s.Lock() 87 timeQ := nilQ 88 if s.recvExpire > 0 { 89 timeQ = time.After(s.recvExpire) 90 } 91 recvQ := s.recvQ 92 sizeQ := s.sizeQ 93 closeQ := s.closeQ 94 s.Unlock() 95 96 select { 97 case m := <-recvQ: 98 return m, nil 99 case <-closeQ: 100 return nil, protocol.ErrClosed 101 case <-timeQ: 102 return nil, protocol.ErrRecvTimeout 103 case <-sizeQ: 104 continue 105 } 106 } 107 } 108 109 func (s *socket) SetOption(name string, value interface{}) error { 110 switch name { 111 112 case protocol.OptionRecvDeadline: 113 if v, ok := value.(time.Duration); ok { 114 s.Lock() 115 s.recvExpire = v 116 s.Unlock() 117 return nil 118 } 119 return protocol.ErrBadValue 120 121 case protocol.OptionWriteQLen: 122 if v, ok := value.(int); ok && v >= 0 { 123 s.Lock() 124 s.sendQLen = v 125 s.Unlock() 126 return nil 127 } 128 return protocol.ErrBadValue 129 130 case protocol.OptionReadQLen: 131 if v, ok := value.(int); ok && v >= 0 { 132 recvQ := make(chan *protocol.Message, v) 133 sizeQ := make(chan struct{}) 134 s.Lock() 135 s.recvQ = recvQ 136 sizeQ, s.sizeQ = s.sizeQ, sizeQ 137 s.recvQLen = v 138 s.Unlock() 139 close(sizeQ) 140 // Messages in the old channel "leak", but should be 141 // picked up by garbage collection. This is not 142 // a big deal for us. 143 return nil 144 } 145 return protocol.ErrBadValue 146 } 147 148 return protocol.ErrBadOption 149 } 150 151 func (s *socket) GetOption(option string) (interface{}, error) { 152 switch option { 153 case protocol.OptionRaw: 154 return true, nil 155 case protocol.OptionRecvDeadline: 156 s.Lock() 157 v := s.recvExpire 158 s.Unlock() 159 return v, nil 160 case protocol.OptionWriteQLen: 161 s.Lock() 162 v := s.sendQLen 163 s.Unlock() 164 return v, nil 165 case protocol.OptionReadQLen: 166 s.Lock() 167 v := s.recvQLen 168 s.Unlock() 169 return v, nil 170 } 171 172 return nil, protocol.ErrBadOption 173 } 174 175 func (s *socket) AddPipe(pp protocol.Pipe) error { 176 p := &pipe{ 177 p: pp, 178 s: s, 179 closeQ: make(chan struct{}), 180 sendQ: make(chan *protocol.Message, s.sendQLen), 181 } 182 pp.SetPrivate(p) 183 s.Lock() 184 defer s.Unlock() 185 if s.closed { 186 return protocol.ErrClosed 187 } 188 s.pipes[pp.ID()] = p 189 190 go p.sender() 191 go p.receiver() 192 return nil 193 } 194 195 func (s *socket) RemovePipe(pp protocol.Pipe) { 196 p := pp.GetPrivate().(*pipe) 197 close(p.closeQ) 198 s.Lock() 199 delete(p.s.pipes, p.p.ID()) 200 s.Unlock() 201 } 202 203 func (s *socket) OpenContext() (protocol.Context, error) { 204 return nil, protocol.ErrProtoOp 205 } 206 207 func (*socket) Info() protocol.Info { 208 return protocol.Info{ 209 Self: Self, 210 Peer: Peer, 211 SelfName: SelfName, 212 PeerName: PeerName, 213 } 214 } 215 216 func (s *socket) Close() error { 217 s.Lock() 218 219 if s.closed { 220 s.Unlock() 221 return protocol.ErrClosed 222 } 223 s.closed = true 224 s.Unlock() 225 226 close(s.closeQ) 227 return nil 228 229 } 230 231 func (p *pipe) sender() { 232 outer: 233 for { 234 var m *protocol.Message 235 select { 236 case <-p.closeQ: 237 break outer 238 case m = <-p.sendQ: 239 } 240 241 if err := p.p.SendMsg(m); err != nil { 242 m.Free() 243 break 244 } 245 } 246 p.close() 247 } 248 249 func (p *pipe) receiver() { 250 s := p.s 251 outer: 252 for { 253 m := p.p.RecvMsg() 254 if m == nil { 255 break 256 } 257 258 if len(m.Body) < 4 { 259 m.Free() 260 continue 261 } 262 263 m.Header = m.Body[:4] 264 m.Body = m.Body[4:] 265 266 s.Lock() 267 recvQ := s.recvQ 268 sizeQ := s.sizeQ 269 s.Unlock() 270 271 select { 272 case recvQ <- m: 273 case <-p.closeQ: 274 m.Free() 275 break outer 276 case <-sizeQ: 277 m.Free() 278 } 279 } 280 p.close() 281 } 282 283 func (p *pipe) close() { 284 _ = p.p.Close() 285 } 286 287 // NewProtocol returns a new protocol implementation. 288 func NewProtocol() protocol.Protocol { 289 s := &socket{ 290 pipes: make(map[uint32]*pipe), 291 closeQ: make(chan struct{}), 292 sizeQ: make(chan struct{}), 293 recvQ: make(chan *protocol.Message, defaultQLen), 294 sendQLen: defaultQLen, 295 recvQLen: defaultQLen, 296 } 297 return s 298 } 299 300 // NewSocket allocates a new Socket using the RESPONDENT protocol. 301 func NewSocket() (protocol.Socket, error) { 302 return protocol.MakeSocket(NewProtocol()), nil 303 }