go.nanomsg.org/mangos/v3@v3.4.3-0.20240217232803-46464076f1f5/protocol/req/req.go (about) 1 // Copyright 2022 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 req implements the REQ protocol, which is the request side of 16 // the request/response pattern. (REP is the response.) 17 package req 18 19 import ( 20 "encoding/binary" 21 "sync" 22 "sync/atomic" 23 "time" 24 25 "go.nanomsg.org/mangos/v3/protocol" 26 ) 27 28 // Protocol identity information. 29 const ( 30 Self = protocol.ProtoReq 31 Peer = protocol.ProtoRep 32 SelfName = "req" 33 PeerName = "rep" 34 ) 35 36 type pipe struct { 37 p protocol.Pipe 38 s *socket 39 closed bool 40 } 41 42 type context struct { 43 s *socket 44 cond *sync.Cond 45 resendTime time.Duration // tunable resend time 46 sendExpire time.Duration // how long to wait in send 47 receiveExpire time.Duration // how long to wait in receive 48 sendTimer *time.Timer // send timer 49 receiveTimer *time.Timer // receive timer 50 resendTimer *time.Timer // resend timeout 51 reqMsg *protocol.Message // message for transmit 52 repMsg *protocol.Message // received reply 53 sendMsg *protocol.Message // messaging waiting for send 54 lastPipe *pipe // last pipe used for transmit 55 reqID uint32 // request ID 56 receiveWait bool // true if a thread is blocked receiving 57 bestEffort bool // if true, don't block waiting in send 58 failNoPeers bool // fast fail if no peers present 59 queued bool // true if we need to send a message 60 closed bool // true if we are closed 61 } 62 63 type socket struct { 64 sync.Mutex 65 defCtx *context // default context 66 contexts map[*context]struct{} // all contexts (set) 67 ctxByID map[uint32]*context // contexts by request ID 68 nextID uint32 // next request ID 69 closed bool // true if we are closed 70 sendQ []*context // contexts waiting to send 71 readyQ []*pipe // pipes available for sending 72 pipes map[uint32]*pipe // all pipes 73 } 74 75 func (s *socket) send() { 76 for len(s.sendQ) != 0 && len(s.readyQ) != 0 { 77 c := s.sendQ[0] 78 s.sendQ = s.sendQ[1:] 79 c.queued = false 80 81 var m *protocol.Message 82 if m = c.sendMsg; m != nil { 83 c.reqMsg = m 84 c.sendMsg = nil 85 s.ctxByID[c.reqID] = c 86 c.cond.Broadcast() 87 } else { 88 m = c.reqMsg 89 } 90 m.Clone() 91 p := s.readyQ[0] 92 s.readyQ = s.readyQ[1:] 93 94 // Schedule retransmission for the future. 95 c.lastPipe = p 96 if c.resendTime > 0 { 97 id := c.reqID 98 c.resendTimer = time.AfterFunc(c.resendTime, func() { 99 c.resendMessage(id) 100 }) 101 } 102 go p.sendCtx(c, m) 103 } 104 } 105 106 func (p *pipe) sendCtx(_ *context, m *protocol.Message) { 107 s := p.s 108 109 // Send this message. If an error occurs, we examine the 110 // error. If it is ErrClosed, we don't schedule our self. 111 if err := p.p.SendMsg(m); err != nil { 112 m.Free() 113 if err == protocol.ErrClosed { 114 return 115 } 116 } 117 s.Lock() 118 if !s.closed && !p.closed { 119 s.readyQ = append(s.readyQ, p) 120 s.send() 121 } 122 s.Unlock() 123 } 124 125 func (p *pipe) receiver() { 126 s := p.s 127 for { 128 m := p.p.RecvMsg() 129 if m == nil { 130 break 131 } 132 if len(m.Body) < 4 { 133 m.Free() 134 continue 135 } 136 m.Header = append(m.Header, m.Body[:4]...) 137 m.Body = m.Body[4:] 138 139 id := binary.BigEndian.Uint32(m.Header) 140 141 s.Lock() 142 // Since we just received a reply, stick our send at the 143 // head of the list, since that's a good indication that 144 // we're ready for another request. 145 for i, rp := range s.readyQ { 146 if p == rp { 147 s.readyQ[0], s.readyQ[i] = s.readyQ[i], s.readyQ[0] 148 break 149 } 150 } 151 152 if c, ok := s.ctxByID[id]; ok { 153 c.cancelSend() 154 c.reqMsg.Free() 155 c.reqMsg = nil 156 c.repMsg = m 157 delete(s.ctxByID, id) 158 if c.resendTimer != nil { 159 c.resendTimer.Stop() 160 c.resendTimer = nil 161 } 162 if c.receiveTimer != nil { 163 c.receiveTimer.Stop() 164 c.receiveTimer = nil 165 } 166 c.cond.Broadcast() 167 } else { 168 // No matching receiver so just drop it. 169 m.Free() 170 } 171 s.Unlock() 172 } 173 174 go p.Close() 175 } 176 177 func (p *pipe) Close() { 178 _ = p.p.Close() 179 } 180 181 func (c *context) resendMessage(id uint32) { 182 s := c.s 183 s.Lock() 184 defer s.Unlock() 185 if c.reqID == id && c.reqMsg != nil { 186 if !c.queued { 187 c.queued = true 188 s.sendQ = append(s.sendQ, c) 189 s.send() 190 } 191 } 192 } 193 194 func (c *context) cancelSend() { 195 s := c.s 196 if c.queued { 197 c.queued = false 198 for i, c2 := range s.sendQ { 199 if c2 == c { 200 s.sendQ = append(s.sendQ[:i], s.sendQ[i+1:]...) 201 return 202 } 203 } 204 } 205 } 206 207 func (c *context) cancel() { 208 s := c.s 209 c.cancelSend() 210 if c.reqID != 0 { 211 delete(s.ctxByID, c.reqID) 212 c.reqID = 0 213 } 214 if c.repMsg != nil { 215 c.repMsg.Free() 216 c.repMsg = nil 217 } 218 if c.reqMsg != nil { 219 c.reqMsg.Free() 220 c.reqMsg = nil 221 } 222 if c.resendTimer != nil { 223 c.resendTimer.Stop() 224 c.resendTimer = nil 225 } 226 if c.sendTimer != nil { 227 c.sendTimer.Stop() 228 c.sendTimer = nil 229 } 230 if c.receiveTimer != nil { 231 c.receiveTimer.Stop() 232 c.receiveTimer = nil 233 } 234 c.cond.Broadcast() 235 } 236 237 func (c *context) SendMsg(m *protocol.Message) error { 238 239 s := c.s 240 241 id := atomic.AddUint32(&s.nextID, 1) 242 id |= 0x80000000 243 244 // cooked mode, we stash the header 245 m.Header = append([]byte{}, 246 byte(id>>24), byte(id>>16), byte(id>>8), byte(id)) 247 248 s.Lock() 249 defer s.Unlock() 250 if s.closed || c.closed { 251 return protocol.ErrClosed 252 } 253 254 if c.failNoPeers && len(s.pipes) == 0 { 255 return protocol.ErrNoPeers 256 } 257 c.cancel() // this cancels any pending send or receive calls 258 c.cancelSend() 259 260 c.reqID = id 261 c.queued = true 262 c.sendMsg = m 263 264 s.sendQ = append(s.sendQ, c) 265 266 if c.bestEffort { 267 // for best effort case, we just immediately go the 268 // reqMsg, and schedule sending. No waiting. 269 // This means that if the message cannot be delivered 270 // immediately, it will still get a chance later. 271 s.send() 272 return nil 273 } 274 275 expired := false 276 if c.sendExpire > 0 { 277 c.sendTimer = time.AfterFunc(c.sendExpire, func() { 278 s.Lock() 279 if c.sendMsg == m { 280 expired = true 281 c.cancel() // also does a wake-up 282 } 283 s.Unlock() 284 }) 285 } 286 287 s.send() 288 289 // This sleeps until we are picked for scheduling. 290 // It is responsible for providing the blocking semantic and 291 // ultimately back-pressure. Note that we will "continue" if 292 // sending is canceled by a subsequent send. 293 for c.sendMsg == m && !expired && !c.closed && !(c.failNoPeers && len(s.pipes) == 0) { 294 c.cond.Wait() 295 } 296 if c.sendMsg == m { 297 c.cancelSend() 298 c.sendMsg = nil 299 c.reqID = 0 300 if c.closed { 301 return protocol.ErrClosed 302 } 303 if c.failNoPeers && len(s.pipes) == 0 { 304 return protocol.ErrNoPeers 305 } 306 return protocol.ErrSendTimeout 307 } 308 return nil 309 } 310 311 func (c *context) RecvMsg() (*protocol.Message, error) { 312 s := c.s 313 s.Lock() 314 defer s.Unlock() 315 if s.closed || c.closed { 316 return nil, protocol.ErrClosed 317 } 318 if c.failNoPeers && len(s.pipes) == 0 { 319 return nil, protocol.ErrNoPeers 320 } 321 if c.receiveWait || c.reqID == 0 { 322 return nil, protocol.ErrProtoState 323 } 324 c.receiveWait = true 325 id := c.reqID 326 expired := false 327 328 if c.receiveExpire > 0 { 329 c.receiveTimer = time.AfterFunc(c.receiveExpire, func() { 330 s.Lock() 331 if c.reqID == id { 332 expired = true 333 c.cancel() 334 } 335 s.Unlock() 336 }) 337 } 338 339 for id == c.reqID && c.repMsg == nil { 340 c.cond.Wait() 341 } 342 343 m := c.repMsg 344 c.reqID = 0 345 c.repMsg = nil 346 c.receiveWait = false 347 c.cond.Broadcast() 348 349 if m == nil { 350 if c.closed { 351 return nil, protocol.ErrClosed 352 } 353 if expired { 354 return nil, protocol.ErrRecvTimeout 355 } 356 if c.failNoPeers && len(s.pipes) == 0 { 357 return nil, protocol.ErrNoPeers 358 } 359 return nil, protocol.ErrCanceled 360 } 361 return m, nil 362 } 363 364 func (c *context) SetOption(name string, value interface{}) error { 365 switch name { 366 case protocol.OptionRetryTime: 367 if v, ok := value.(time.Duration); ok { 368 c.s.Lock() 369 c.resendTime = v 370 c.s.Unlock() 371 return nil 372 } 373 return protocol.ErrBadValue 374 375 case protocol.OptionRecvDeadline: 376 if v, ok := value.(time.Duration); ok { 377 c.s.Lock() 378 c.receiveExpire = v 379 c.s.Unlock() 380 return nil 381 } 382 return protocol.ErrBadValue 383 384 case protocol.OptionSendDeadline: 385 if v, ok := value.(time.Duration); ok { 386 c.s.Lock() 387 c.sendExpire = v 388 c.s.Unlock() 389 return nil 390 } 391 return protocol.ErrBadValue 392 393 case protocol.OptionBestEffort: 394 if v, ok := value.(bool); ok { 395 c.s.Lock() 396 c.bestEffort = v 397 c.s.Unlock() 398 return nil 399 } 400 return protocol.ErrBadValue 401 402 case protocol.OptionFailNoPeers: 403 if v, ok := value.(bool); ok { 404 c.s.Lock() 405 c.failNoPeers = v 406 c.s.Unlock() 407 return nil 408 } 409 return protocol.ErrBadValue 410 411 } 412 413 return protocol.ErrBadOption 414 } 415 416 func (c *context) GetOption(option string) (interface{}, error) { 417 switch option { 418 case protocol.OptionRetryTime: 419 c.s.Lock() 420 v := c.resendTime 421 c.s.Unlock() 422 return v, nil 423 case protocol.OptionRecvDeadline: 424 c.s.Lock() 425 v := c.receiveExpire 426 c.s.Unlock() 427 return v, nil 428 case protocol.OptionSendDeadline: 429 c.s.Lock() 430 v := c.sendExpire 431 c.s.Unlock() 432 return v, nil 433 case protocol.OptionBestEffort: 434 c.s.Lock() 435 v := c.bestEffort 436 c.s.Unlock() 437 return v, nil 438 case protocol.OptionFailNoPeers: 439 c.s.Lock() 440 v := c.failNoPeers 441 c.s.Unlock() 442 return v, nil 443 } 444 445 return nil, protocol.ErrBadOption 446 } 447 448 func (c *context) Close() error { 449 s := c.s 450 c.s.Lock() 451 defer c.s.Unlock() 452 if c.closed { 453 return protocol.ErrClosed 454 } 455 c.closed = true 456 c.cancel() 457 delete(s.contexts, c) 458 return nil 459 } 460 461 func (s *socket) GetOption(option string) (interface{}, error) { 462 switch option { 463 case protocol.OptionRaw: 464 return false, nil 465 default: 466 return s.defCtx.GetOption(option) 467 } 468 } 469 func (s *socket) SetOption(option string, value interface{}) error { 470 return s.defCtx.SetOption(option, value) 471 } 472 473 func (s *socket) SendMsg(m *protocol.Message) error { 474 return s.defCtx.SendMsg(m) 475 } 476 477 func (s *socket) RecvMsg() (*protocol.Message, error) { 478 return s.defCtx.RecvMsg() 479 } 480 481 func (s *socket) Close() error { 482 s.Lock() 483 484 if s.closed { 485 s.Unlock() 486 return protocol.ErrClosed 487 } 488 s.closed = true 489 for c := range s.contexts { 490 c.closed = true 491 c.cancel() 492 delete(s.contexts, c) 493 } 494 s.Unlock() 495 return nil 496 } 497 498 func (s *socket) OpenContext() (protocol.Context, error) { 499 s.Lock() 500 defer s.Unlock() 501 if s.closed { 502 return nil, protocol.ErrClosed 503 } 504 c := &context{ 505 s: s, 506 cond: sync.NewCond(s), 507 bestEffort: s.defCtx.bestEffort, 508 resendTime: s.defCtx.resendTime, 509 sendExpire: s.defCtx.sendExpire, 510 receiveExpire: s.defCtx.receiveExpire, 511 failNoPeers: s.defCtx.failNoPeers, 512 } 513 s.contexts[c] = struct{}{} 514 return c, nil 515 } 516 517 func (s *socket) AddPipe(pp protocol.Pipe) error { 518 p := &pipe{ 519 p: pp, 520 s: s, 521 } 522 pp.SetPrivate(p) 523 s.Lock() 524 defer s.Unlock() 525 if s.closed { 526 return protocol.ErrClosed 527 } 528 s.readyQ = append(s.readyQ, p) 529 s.send() 530 s.pipes[pp.ID()] = p 531 go p.receiver() 532 return nil 533 } 534 535 func (s *socket) RemovePipe(pp protocol.Pipe) { 536 p := pp.GetPrivate().(*pipe) 537 s.Lock() 538 p.closed = true 539 for i, rp := range s.readyQ { 540 if p == rp { 541 s.readyQ = append(s.readyQ[:i], s.readyQ[i+1:]...) 542 } 543 } 544 delete(s.pipes, pp.ID()) 545 for c := range s.contexts { 546 if c.failNoPeers && len(s.pipes) == 0 { 547 c.cancel() 548 } else if c.lastPipe == p && c.reqMsg != nil { 549 // We are closing this pipe, so we need to 550 // immediately reschedule it. 551 c.lastPipe = nil 552 id := c.reqID 553 // If there is no resend time, then we need to simply 554 // discard the message, because it's not necessarily idempotent. 555 if c.resendTime == 0 { 556 c.cancel() 557 } else { 558 c.cancelSend() 559 go c.resendMessage(id) 560 } 561 } 562 } 563 s.Unlock() 564 } 565 566 func (*socket) Info() protocol.Info { 567 return protocol.Info{ 568 Self: Self, 569 Peer: Peer, 570 SelfName: SelfName, 571 PeerName: PeerName, 572 } 573 } 574 575 // NewProtocol allocates a new protocol implementation. 576 func NewProtocol() protocol.Protocol { 577 s := &socket{ 578 nextID: uint32(time.Now().UnixNano()), // quasi-random 579 contexts: make(map[*context]struct{}), 580 ctxByID: make(map[uint32]*context), 581 pipes: make(map[uint32]*pipe), 582 } 583 s.defCtx = &context{ 584 s: s, 585 cond: sync.NewCond(s), 586 resendTime: time.Minute, 587 } 588 s.contexts[s.defCtx] = struct{}{} 589 return s 590 } 591 592 // NewSocket allocates a new Socket using the REQ protocol. 593 func NewSocket() (protocol.Socket, error) { 594 return protocol.MakeSocket(NewProtocol()), nil 595 }