github.com/line/ostracon@v1.0.10-0.20230328032236-7f20145f065d/p2p/conn/connection.go (about) 1 package conn 2 3 import ( 4 "bufio" 5 "errors" 6 "fmt" 7 "io" 8 "math" 9 "net" 10 "reflect" 11 "runtime/debug" 12 "sync/atomic" 13 "time" 14 15 "github.com/gogo/protobuf/proto" 16 17 tmp2p "github.com/tendermint/tendermint/proto/tendermint/p2p" 18 19 flow "github.com/line/ostracon/libs/flowrate" 20 "github.com/line/ostracon/libs/log" 21 tmmath "github.com/line/ostracon/libs/math" 22 "github.com/line/ostracon/libs/protoio" 23 "github.com/line/ostracon/libs/service" 24 tmsync "github.com/line/ostracon/libs/sync" 25 "github.com/line/ostracon/libs/timer" 26 ) 27 28 const ( 29 defaultMaxPacketMsgPayloadSize = 1024 30 31 numBatchPacketMsgs = 10 32 minReadBufferSize = 1024 33 minWriteBufferSize = 65536 34 updateStats = 2 * time.Second 35 36 // some of these defaults are written in the user config 37 // flushThrottle, sendRate, recvRate 38 // TODO: remove values present in config 39 defaultFlushThrottle = 100 * time.Millisecond 40 41 defaultSendQueueCapacity = 1 42 defaultRecvBufferCapacity = 4096 43 defaultRecvMessageCapacity = 22020096 // 21MB 44 defaultSendRate = int64(512000) // 500KB/s 45 defaultRecvRate = int64(512000) // 500KB/s 46 defaultSendTimeout = 10 * time.Second 47 defaultPingInterval = 60 * time.Second 48 defaultPongTimeout = 45 * time.Second 49 defaultRecvAsync = true 50 ) 51 52 type receiveCbFunc func(chID byte, msgBytes []byte) 53 type errorCbFunc func(interface{}) 54 55 /* 56 Each peer has one `MConnection` (multiplex connection) instance. 57 58 __multiplex__ *noun* a system or signal involving simultaneous transmission of 59 several messages along a single channel of communication. 60 61 Each `MConnection` handles message transmission on multiple abstract communication 62 `Channel`s. Each channel has a globally unique byte id. 63 The byte id and the relative priorities of each `Channel` are configured upon 64 initialization of the connection. 65 66 There are two methods for sending messages: 67 68 func (m MConnection) Send(chID byte, msgBytes []byte) bool {} 69 func (m MConnection) TrySend(chID byte, msgBytes []byte}) bool {} 70 71 `Send(chID, msgBytes)` is a blocking call that waits until `msg` is 72 successfully queued for the channel with the given id byte `chID`, or until the 73 request times out. The message `msg` is serialized using Protobuf. 74 75 `TrySend(chID, msgBytes)` is a nonblocking call that returns false if the 76 channel's queue is full. 77 78 Inbound message bytes are handled with an onReceive callback function. 79 */ 80 type MConnection struct { 81 service.BaseService 82 83 conn net.Conn 84 bufConnReader *bufio.Reader 85 bufConnWriter *bufio.Writer 86 sendMonitor *flow.Monitor 87 recvMonitor *flow.Monitor 88 send chan struct{} 89 pong chan struct{} 90 channels []*Channel 91 channelsIdx map[byte]*Channel 92 onReceive receiveCbFunc 93 onError errorCbFunc 94 errored uint32 95 config MConnConfig 96 97 // Closing quitSendRoutine will cause the sendRoutine to eventually quit. 98 // doneSendRoutine is closed when the sendRoutine actually quits. 99 quitSendRoutine chan struct{} 100 doneSendRoutine chan struct{} 101 102 // Closing quitRecvRouting will cause the recvRouting to eventually quit. 103 quitRecvRoutine chan struct{} 104 105 // used to ensure FlushStop and OnStop 106 // are safe to call concurrently. 107 stopMtx tmsync.Mutex 108 109 flushTimer *timer.ThrottleTimer // flush writes as necessary but throttled. 110 pingTimer *time.Ticker // send pings periodically 111 112 // close conn if pong is not received in pongTimeout 113 pongTimer *time.Timer 114 pongTimeoutCh chan bool // true - timeout, false - peer sent pong 115 116 chStatsTimer *time.Ticker // update channel stats periodically 117 118 created time.Time // time of creation 119 120 _maxPacketMsgSize int 121 } 122 123 // MConnConfig is a MConnection configuration. 124 type MConnConfig struct { 125 SendRate int64 `mapstructure:"send_rate"` 126 RecvRate int64 `mapstructure:"recv_rate"` 127 128 // Maximum payload size 129 MaxPacketMsgPayloadSize int `mapstructure:"max_packet_msg_payload_size"` 130 131 // Interval to flush writes (throttled) 132 FlushThrottle time.Duration `mapstructure:"flush_throttle"` 133 134 // Interval to send pings 135 PingInterval time.Duration `mapstructure:"ping_interval"` 136 137 // Maximum wait time for pongs 138 PongTimeout time.Duration `mapstructure:"pong_timeout"` 139 140 // Action method of reactor's receive function 141 RecvAsync bool `mapstructure:"recv_async"` 142 } 143 144 // DefaultMConnConfig returns the default config. 145 func DefaultMConnConfig() MConnConfig { 146 return MConnConfig{ 147 SendRate: defaultSendRate, 148 RecvRate: defaultRecvRate, 149 MaxPacketMsgPayloadSize: defaultMaxPacketMsgPayloadSize, 150 FlushThrottle: defaultFlushThrottle, 151 PingInterval: defaultPingInterval, 152 PongTimeout: defaultPongTimeout, 153 RecvAsync: defaultRecvAsync, 154 } 155 } 156 157 // NewMConnection wraps net.Conn and creates multiplex connection 158 func NewMConnection( 159 conn net.Conn, 160 chDescs []*ChannelDescriptor, 161 onReceive receiveCbFunc, 162 onError errorCbFunc, 163 ) *MConnection { 164 return NewMConnectionWithConfig( 165 conn, 166 chDescs, 167 onReceive, 168 onError, 169 DefaultMConnConfig()) 170 } 171 172 // NewMConnectionWithConfig wraps net.Conn and creates multiplex connection with a config 173 func NewMConnectionWithConfig( 174 conn net.Conn, 175 chDescs []*ChannelDescriptor, 176 onReceive receiveCbFunc, 177 onError errorCbFunc, 178 config MConnConfig, 179 ) *MConnection { 180 if config.PongTimeout >= config.PingInterval { 181 panic("pongTimeout must be less than pingInterval (otherwise, next ping will reset pong timer)") 182 } 183 184 mconn := &MConnection{ 185 conn: conn, 186 bufConnReader: bufio.NewReaderSize(conn, minReadBufferSize), 187 bufConnWriter: bufio.NewWriterSize(conn, minWriteBufferSize), 188 sendMonitor: flow.New(0, 0), 189 recvMonitor: flow.New(0, 0), 190 send: make(chan struct{}, 1), 191 pong: make(chan struct{}, 1), 192 onReceive: onReceive, 193 onError: onError, 194 config: config, 195 created: time.Now(), 196 } 197 198 // Create channels 199 var channelsIdx = map[byte]*Channel{} 200 var channels = []*Channel{} 201 202 for _, desc := range chDescs { 203 channel := newChannel(mconn, *desc) 204 channelsIdx[channel.desc.ID] = channel 205 channels = append(channels, channel) 206 } 207 mconn.channels = channels 208 mconn.channelsIdx = channelsIdx 209 210 mconn.BaseService = *service.NewBaseService(nil, "MConnection", mconn) 211 212 // maxPacketMsgSize() is a bit heavy, so call just once 213 mconn._maxPacketMsgSize = mconn.maxPacketMsgSize() 214 215 return mconn 216 } 217 218 func (c *MConnection) SetLogger(l log.Logger) { 219 c.BaseService.SetLogger(l) 220 for _, ch := range c.channels { 221 ch.SetLogger(l) 222 } 223 } 224 225 // OnStart implements BaseService 226 func (c *MConnection) OnStart() error { 227 if err := c.BaseService.OnStart(); err != nil { 228 return err 229 } 230 c.flushTimer = timer.NewThrottleTimer("flush", c.config.FlushThrottle) 231 c.pingTimer = time.NewTicker(c.config.PingInterval) 232 c.pongTimeoutCh = make(chan bool, 1) 233 c.chStatsTimer = time.NewTicker(updateStats) 234 c.quitSendRoutine = make(chan struct{}) 235 c.doneSendRoutine = make(chan struct{}) 236 c.quitRecvRoutine = make(chan struct{}) 237 go c.sendRoutine() 238 go c.recvRoutine() 239 return nil 240 } 241 242 // stopServices stops the BaseService and timers and closes the quitSendRoutine. 243 // if the quitSendRoutine was already closed, it returns true, otherwise it returns false. 244 // It uses the stopMtx to ensure only one of FlushStop and OnStop can do this at a time. 245 func (c *MConnection) stopServices() (alreadyStopped bool) { 246 c.stopMtx.Lock() 247 defer c.stopMtx.Unlock() 248 249 select { 250 case <-c.quitSendRoutine: 251 // already quit 252 return true 253 default: 254 } 255 256 select { 257 case <-c.quitRecvRoutine: 258 // already quit 259 return true 260 default: 261 } 262 263 c.BaseService.OnStop() 264 c.flushTimer.Stop() 265 c.pingTimer.Stop() 266 c.chStatsTimer.Stop() 267 268 // inform the recvRouting that we are shutting down 269 close(c.quitRecvRoutine) 270 close(c.quitSendRoutine) 271 return false 272 } 273 274 // FlushStop replicates the logic of OnStop. 275 // It additionally ensures that all successful 276 // .Send() calls will get flushed before closing 277 // the connection. 278 func (c *MConnection) FlushStop() { 279 if c.stopServices() { 280 return 281 } 282 283 // this block is unique to FlushStop 284 { 285 // wait until the sendRoutine exits 286 // so we dont race on calling sendSomePacketMsgs 287 <-c.doneSendRoutine 288 289 // Send and flush all pending msgs. 290 // Since sendRoutine has exited, we can call this 291 // safely 292 eof := c.sendSomePacketMsgs() 293 for !eof { 294 eof = c.sendSomePacketMsgs() 295 } 296 c.flush() 297 298 // Now we can close the connection 299 } 300 301 c.conn.Close() 302 303 // We can't close pong safely here because 304 // recvRoutine may write to it after we've stopped. 305 // Though it doesn't need to get closed at all, 306 // we close it @ recvRoutine. 307 308 // c.Stop() 309 } 310 311 // OnStop implements BaseService 312 func (c *MConnection) OnStop() { 313 if c.stopServices() { 314 return 315 } 316 317 c.conn.Close() 318 319 // We can't close pong safely here because 320 // recvRoutine may write to it after we've stopped. 321 // Though it doesn't need to get closed at all, 322 // we close it @ recvRoutine. 323 } 324 325 func (c *MConnection) String() string { 326 return fmt.Sprintf("MConn{%v}", c.conn.RemoteAddr()) 327 } 328 329 func (c *MConnection) flush() { 330 c.Logger.Debug("Flush", "conn", c) 331 err := c.bufConnWriter.Flush() 332 if err != nil { 333 c.Logger.Debug("MConnection flush failed", "err", err) 334 } 335 } 336 337 // Catch panics, usually caused by remote disconnects. 338 func (c *MConnection) _recover() { 339 if r := recover(); r != nil { 340 c.Logger.Error("MConnection panicked", "err", r, "stack", string(debug.Stack())) 341 c.stopForError(fmt.Errorf("recovered from panic: %v", r)) 342 } 343 } 344 345 func (c *MConnection) stopForError(r interface{}) { 346 if err := c.Stop(); err != nil { 347 c.Logger.Error("Error stopping connection", "err", err) 348 } 349 if atomic.CompareAndSwapUint32(&c.errored, 0, 1) { 350 if c.onError != nil { 351 c.onError(r) 352 } 353 } 354 } 355 356 // Queues a message to be sent to channel. 357 func (c *MConnection) Send(chID byte, msgBytes []byte) bool { 358 if !c.IsRunning() { 359 return false 360 } 361 362 c.Logger.Debug("Send", "channel", chID, "conn", c, "msgBytes", fmt.Sprintf("%X", msgBytes)) 363 364 // Send message to channel. 365 channel, ok := c.channelsIdx[chID] 366 if !ok { 367 c.Logger.Error(fmt.Sprintf("Cannot send bytes, unknown channel %X", chID)) 368 return false 369 } 370 371 success := channel.sendBytes(msgBytes) 372 if success { 373 // Wake up sendRoutine if necessary 374 select { 375 case c.send <- struct{}{}: 376 default: 377 } 378 } else { 379 c.Logger.Debug("Send failed", "channel", chID, "conn", c, "msgBytes", fmt.Sprintf("%X", msgBytes)) 380 } 381 return success 382 } 383 384 // Queues a message to be sent to channel. 385 // Nonblocking, returns true if successful. 386 func (c *MConnection) TrySend(chID byte, msgBytes []byte) bool { 387 if !c.IsRunning() { 388 return false 389 } 390 391 c.Logger.Debug("TrySend", "channel", chID, "conn", c, "msgBytes", fmt.Sprintf("%X", msgBytes)) 392 393 // Send message to channel. 394 channel, ok := c.channelsIdx[chID] 395 if !ok { 396 c.Logger.Error(fmt.Sprintf("Cannot send bytes, unknown channel %X", chID)) 397 return false 398 } 399 400 ok = channel.trySendBytes(msgBytes) 401 if ok { 402 // Wake up sendRoutine if necessary 403 select { 404 case c.send <- struct{}{}: 405 default: 406 } 407 } 408 409 return ok 410 } 411 412 // CanSend returns true if you can send more data onto the chID, false 413 // otherwise. Use only as a heuristic. 414 func (c *MConnection) CanSend(chID byte) bool { 415 if !c.IsRunning() { 416 return false 417 } 418 419 channel, ok := c.channelsIdx[chID] 420 if !ok { 421 c.Logger.Error(fmt.Sprintf("Unknown channel %X", chID)) 422 return false 423 } 424 return channel.canSend() 425 } 426 427 // sendRoutine polls for packets to send from channels. 428 func (c *MConnection) sendRoutine() { 429 defer c._recover() 430 431 protoWriter := protoio.NewDelimitedWriter(c.bufConnWriter) 432 433 FOR_LOOP: 434 for { 435 var _n int 436 var err error 437 SELECTION: 438 select { 439 case <-c.flushTimer.Ch: 440 // NOTE: flushTimer.Set() must be called every time 441 // something is written to .bufConnWriter. 442 c.flush() 443 case <-c.chStatsTimer.C: 444 for _, channel := range c.channels { 445 channel.updateStats() 446 } 447 case <-c.pingTimer.C: 448 c.Logger.Debug("Send Ping") 449 _n, err = protoWriter.WriteMsg(mustWrapPacket(&tmp2p.PacketPing{})) 450 if err != nil { 451 c.Logger.Error("Failed to send PacketPing", "err", err) 452 break SELECTION 453 } 454 c.sendMonitor.Update(_n) 455 c.Logger.Debug("Starting pong timer", "dur", c.config.PongTimeout) 456 c.pongTimer = time.AfterFunc(c.config.PongTimeout, func() { 457 select { 458 case c.pongTimeoutCh <- true: 459 default: 460 } 461 }) 462 c.flush() 463 case timeout := <-c.pongTimeoutCh: 464 if timeout { 465 c.Logger.Debug("Pong timeout") 466 err = errors.New("pong timeout") 467 } else { 468 c.stopPongTimer() 469 } 470 case <-c.pong: 471 c.Logger.Debug("Send Pong") 472 _n, err = protoWriter.WriteMsg(mustWrapPacket(&tmp2p.PacketPong{})) 473 if err != nil { 474 c.Logger.Error("Failed to send PacketPong", "err", err) 475 break SELECTION 476 } 477 c.sendMonitor.Update(_n) 478 c.flush() 479 case <-c.quitSendRoutine: 480 break FOR_LOOP 481 case <-c.send: 482 // Send some PacketMsgs 483 eof := c.sendSomePacketMsgs() 484 if !eof { 485 // Keep sendRoutine awake. 486 select { 487 case c.send <- struct{}{}: 488 default: 489 } 490 } 491 } 492 493 if !c.IsRunning() { 494 break FOR_LOOP 495 } 496 if err != nil { 497 c.Logger.Error("Connection failed @ sendRoutine", "conn", c, "err", err) 498 c.stopForError(err) 499 break FOR_LOOP 500 } 501 } 502 503 // Cleanup 504 c.stopPongTimer() 505 close(c.doneSendRoutine) 506 } 507 508 // Returns true if messages from channels were exhausted. 509 // Blocks in accordance to .sendMonitor throttling. 510 func (c *MConnection) sendSomePacketMsgs() bool { 511 // Block until .sendMonitor says we can write. 512 // Once we're ready we send more than we asked for, 513 // but amortized it should even out. 514 c.sendMonitor.Limit(c._maxPacketMsgSize, atomic.LoadInt64(&c.config.SendRate), true) 515 516 // Now send some PacketMsgs. 517 for i := 0; i < numBatchPacketMsgs; i++ { 518 if c.sendPacketMsg() { 519 return true 520 } 521 } 522 return false 523 } 524 525 // Returns true if messages from channels were exhausted. 526 func (c *MConnection) sendPacketMsg() bool { 527 // Choose a channel to create a PacketMsg from. 528 // The chosen channel will be the one whose recentlySent/priority is the least. 529 var leastRatio float32 = math.MaxFloat32 530 var leastChannel *Channel 531 for _, channel := range c.channels { 532 // If nothing to send, skip this channel 533 if !channel.isSendPending() { 534 continue 535 } 536 // Get ratio, and keep track of lowest ratio. 537 ratio := float32(channel.recentlySent) / float32(channel.desc.Priority) 538 if ratio < leastRatio { 539 leastRatio = ratio 540 leastChannel = channel 541 } 542 } 543 544 // Nothing to send? 545 if leastChannel == nil { 546 return true 547 } 548 // c.Logger.Info("Found a msgPacket to send") 549 550 // Make & send a PacketMsg from this channel 551 _n, err := leastChannel.writePacketMsgTo(c.bufConnWriter) 552 if err != nil { 553 c.Logger.Error("Failed to write PacketMsg", "err", err) 554 c.stopForError(err) 555 return true 556 } 557 c.sendMonitor.Update(_n) 558 c.flushTimer.Set() 559 return false 560 } 561 562 // recvRoutine reads PacketMsgs and reconstructs the message using the channels' "recving" buffer. 563 // After a whole message has been assembled, it's pushed to onReceive(). 564 // Blocks depending on how the connection is throttled. 565 // Otherwise, it never blocks. 566 func (c *MConnection) recvRoutine() { 567 defer c._recover() 568 569 protoReader := protoio.NewDelimitedReader(c.bufConnReader, c._maxPacketMsgSize) 570 571 FOR_LOOP: 572 for { 573 // Block until .recvMonitor says we can read. 574 c.recvMonitor.Limit(c._maxPacketMsgSize, atomic.LoadInt64(&c.config.RecvRate), true) 575 576 // Peek into bufConnReader for debugging 577 /* 578 if numBytes := c.bufConnReader.Buffered(); numBytes > 0 { 579 bz, err := c.bufConnReader.Peek(tmmath.MinInt(numBytes, 100)) 580 if err == nil { 581 // return 582 } else { 583 c.Logger.Debug("Error peeking connection buffer", "err", err) 584 // return nil 585 } 586 c.Logger.Info("Peek connection buffer", "numBytes", numBytes, "bz", bz) 587 } 588 */ 589 590 // Read packet type 591 var packet tmp2p.Packet 592 593 _n, err := protoReader.ReadMsg(&packet) 594 c.recvMonitor.Update(_n) 595 if err != nil { 596 // stopServices was invoked and we are shutting down 597 // receiving is excpected to fail since we will close the connection 598 select { 599 case <-c.quitRecvRoutine: 600 break FOR_LOOP 601 default: 602 } 603 604 if c.IsRunning() { 605 if err == io.EOF { 606 c.Logger.Info("Connection is closed @ recvRoutine (likely by the other side)", "conn", c) 607 } else { 608 c.Logger.Debug("Connection failed @ recvRoutine (reading byte)", "conn", c, "err", err) 609 } 610 c.stopForError(err) 611 } 612 break FOR_LOOP 613 } 614 615 // Read more depending on packet type. 616 switch pkt := packet.Sum.(type) { 617 case *tmp2p.Packet_PacketPing: 618 // TODO: prevent abuse, as they cause flush()'s. 619 // https://github.com/tendermint/tendermint/issues/1190 620 c.Logger.Debug("Receive Ping") 621 select { 622 case c.pong <- struct{}{}: 623 default: 624 // never block 625 } 626 case *tmp2p.Packet_PacketPong: 627 c.Logger.Debug("Receive Pong") 628 select { 629 case c.pongTimeoutCh <- false: 630 default: 631 // never block 632 } 633 case *tmp2p.Packet_PacketMsg: 634 channelID := byte(pkt.PacketMsg.ChannelID) 635 channel, ok := c.channelsIdx[channelID] 636 if pkt.PacketMsg.ChannelID < 0 || pkt.PacketMsg.ChannelID > math.MaxUint8 || !ok || channel == nil { 637 err := fmt.Errorf("unknown channel %X", pkt.PacketMsg.ChannelID) 638 c.Logger.Debug("Connection failed @ recvRoutine", "conn", c, "err", err) 639 c.stopForError(err) 640 break FOR_LOOP 641 } 642 643 msgBytes, err := channel.recvPacketMsg(*pkt.PacketMsg) 644 if err != nil { 645 if c.IsRunning() { 646 c.Logger.Debug("Connection failed @ recvRoutine", "conn", c, "err", err) 647 c.stopForError(err) 648 } 649 break FOR_LOOP 650 } 651 if msgBytes != nil { 652 c.Logger.Debug("Received bytes", "chID", channelID, "msgBytes", msgBytes) 653 // NOTE: This means the reactor.Receive runs in the same thread as the p2p recv routine 654 c.onReceive(channelID, msgBytes) 655 } 656 default: 657 err := fmt.Errorf("unknown message type %v", reflect.TypeOf(packet)) 658 c.Logger.Error("Connection failed @ recvRoutine", "conn", c, "err", err) 659 c.stopForError(err) 660 break FOR_LOOP 661 } 662 } 663 664 // Cleanup 665 close(c.pong) 666 for range c.pong { 667 // Drain 668 } 669 } 670 671 // not goroutine-safe 672 func (c *MConnection) stopPongTimer() { 673 if c.pongTimer != nil { 674 _ = c.pongTimer.Stop() 675 c.pongTimer = nil 676 } 677 } 678 679 // maxPacketMsgSize returns a maximum size of PacketMsg 680 func (c *MConnection) maxPacketMsgSize() int { 681 bz, err := proto.Marshal(mustWrapPacket(&tmp2p.PacketMsg{ 682 ChannelID: 0x01, 683 EOF: true, 684 Data: make([]byte, c.config.MaxPacketMsgPayloadSize), 685 })) 686 if err != nil { 687 panic(err) 688 } 689 return len(bz) 690 } 691 692 type ConnectionStatus struct { 693 Duration time.Duration 694 SendMonitor flow.Status 695 RecvMonitor flow.Status 696 Channels []ChannelStatus 697 } 698 699 type ChannelStatus struct { 700 ID byte 701 SendQueueCapacity int 702 SendQueueSize int 703 Priority int 704 RecentlySent int64 705 } 706 707 func (c *MConnection) Status() ConnectionStatus { 708 var status ConnectionStatus 709 status.Duration = time.Since(c.created) 710 status.SendMonitor = c.sendMonitor.Status() 711 status.RecvMonitor = c.recvMonitor.Status() 712 status.Channels = make([]ChannelStatus, len(c.channels)) 713 for i, channel := range c.channels { 714 status.Channels[i] = ChannelStatus{ 715 ID: channel.desc.ID, 716 SendQueueCapacity: cap(channel.sendQueue), 717 SendQueueSize: int(atomic.LoadInt32(&channel.sendQueueSize)), 718 Priority: channel.desc.Priority, 719 RecentlySent: atomic.LoadInt64(&channel.recentlySent), 720 } 721 } 722 return status 723 } 724 725 //----------------------------------------------------------------------------- 726 727 type ChannelDescriptor struct { 728 ID byte 729 Priority int 730 SendQueueCapacity int 731 RecvBufferCapacity int 732 RecvMessageCapacity int 733 } 734 735 func (chDesc ChannelDescriptor) FillDefaults() (filled ChannelDescriptor) { 736 if chDesc.SendQueueCapacity == 0 { 737 chDesc.SendQueueCapacity = defaultSendQueueCapacity 738 } 739 if chDesc.RecvBufferCapacity == 0 { 740 chDesc.RecvBufferCapacity = defaultRecvBufferCapacity 741 } 742 if chDesc.RecvMessageCapacity == 0 { 743 chDesc.RecvMessageCapacity = defaultRecvMessageCapacity 744 } 745 filled = chDesc 746 return 747 } 748 749 // TODO: lowercase. 750 // NOTE: not goroutine-safe. 751 type Channel struct { 752 conn *MConnection 753 desc ChannelDescriptor 754 sendQueue chan []byte 755 sendQueueSize int32 // atomic. 756 recving []byte 757 sending []byte 758 recentlySent int64 // exponential moving average 759 760 maxPacketMsgPayloadSize int 761 762 Logger log.Logger 763 } 764 765 func newChannel(conn *MConnection, desc ChannelDescriptor) *Channel { 766 desc = desc.FillDefaults() 767 if desc.Priority <= 0 { 768 panic("Channel default priority must be a positive integer") 769 } 770 return &Channel{ 771 conn: conn, 772 desc: desc, 773 sendQueue: make(chan []byte, desc.SendQueueCapacity), 774 recving: make([]byte, 0, desc.RecvBufferCapacity), 775 maxPacketMsgPayloadSize: conn.config.MaxPacketMsgPayloadSize, 776 } 777 } 778 779 func (ch *Channel) SetLogger(l log.Logger) { 780 ch.Logger = l 781 } 782 783 // Queues message to send to this channel. 784 // Goroutine-safe 785 // Times out (and returns false) after defaultSendTimeout 786 func (ch *Channel) sendBytes(bytes []byte) bool { 787 select { 788 case ch.sendQueue <- bytes: 789 atomic.AddInt32(&ch.sendQueueSize, 1) 790 return true 791 case <-time.After(defaultSendTimeout): 792 return false 793 } 794 } 795 796 // Queues message to send to this channel. 797 // Nonblocking, returns true if successful. 798 // Goroutine-safe 799 func (ch *Channel) trySendBytes(bytes []byte) bool { 800 select { 801 case ch.sendQueue <- bytes: 802 atomic.AddInt32(&ch.sendQueueSize, 1) 803 return true 804 default: 805 return false 806 } 807 } 808 809 // Goroutine-safe 810 func (ch *Channel) loadSendQueueSize() (size int) { 811 return int(atomic.LoadInt32(&ch.sendQueueSize)) 812 } 813 814 // Goroutine-safe 815 // Use only as a heuristic. 816 func (ch *Channel) canSend() bool { 817 return ch.loadSendQueueSize() < defaultSendQueueCapacity 818 } 819 820 // Returns true if any PacketMsgs are pending to be sent. 821 // Call before calling nextPacketMsg() 822 // Goroutine-safe 823 func (ch *Channel) isSendPending() bool { 824 if len(ch.sending) == 0 { 825 if len(ch.sendQueue) == 0 { 826 return false 827 } 828 ch.sending = <-ch.sendQueue 829 } 830 return true 831 } 832 833 // Creates a new PacketMsg to send. 834 // Not goroutine-safe 835 func (ch *Channel) nextPacketMsg() tmp2p.PacketMsg { 836 packet := tmp2p.PacketMsg{ChannelID: int32(ch.desc.ID)} 837 maxSize := ch.maxPacketMsgPayloadSize 838 packet.Data = ch.sending[:tmmath.MinInt(maxSize, len(ch.sending))] 839 if len(ch.sending) <= maxSize { 840 packet.EOF = true 841 ch.sending = nil 842 atomic.AddInt32(&ch.sendQueueSize, -1) // decrement sendQueueSize 843 } else { 844 packet.EOF = false 845 ch.sending = ch.sending[tmmath.MinInt(maxSize, len(ch.sending)):] 846 } 847 return packet 848 } 849 850 // Writes next PacketMsg to w and updates c.recentlySent. 851 // Not goroutine-safe 852 func (ch *Channel) writePacketMsgTo(w io.Writer) (n int, err error) { 853 packet := ch.nextPacketMsg() 854 n, err = protoio.NewDelimitedWriter(w).WriteMsg(mustWrapPacket(&packet)) 855 atomic.AddInt64(&ch.recentlySent, int64(n)) 856 return 857 } 858 859 // Handles incoming PacketMsgs. It returns a message bytes if message is 860 // complete. NOTE message bytes may change on next call to recvPacketMsg. 861 // Not goroutine-safe 862 func (ch *Channel) recvPacketMsg(packet tmp2p.PacketMsg) ([]byte, error) { 863 ch.Logger.Debug("Read PacketMsg", "conn", ch.conn, "packet", packet) 864 var recvCap, recvReceived = ch.desc.RecvMessageCapacity, len(ch.recving) + len(packet.Data) 865 if recvCap < recvReceived { 866 return nil, fmt.Errorf("received message exceeds available capacity: %v < %v", recvCap, recvReceived) 867 } 868 ch.recving = append(ch.recving, packet.Data...) 869 if packet.EOF { 870 msgBytes := ch.recving 871 872 // clear the slice without re-allocating. 873 // http://stackoverflow.com/questions/16971741/how-do-you-clear-a-slice-in-go 874 // suggests this could be a memory leak, but we might as well keep the memory for the channel until it closes, 875 // at which point the recving slice stops being used and should be garbage collected 876 ch.recving = ch.recving[:0] // make([]byte, 0, ch.desc.RecvBufferCapacity) 877 return msgBytes, nil 878 } 879 return nil, nil 880 } 881 882 // Call this periodically to update stats for throttling purposes. 883 // Not goroutine-safe 884 func (ch *Channel) updateStats() { 885 // Exponential decay of stats. 886 // TODO: optimize. 887 atomic.StoreInt64(&ch.recentlySent, int64(float64(atomic.LoadInt64(&ch.recentlySent))*0.8)) 888 } 889 890 //---------------------------------------- 891 // Packet 892 893 // mustWrapPacket takes a packet kind (oneof) and wraps it in a tmp2p.Packet message. 894 func mustWrapPacket(pb proto.Message) *tmp2p.Packet { 895 var msg tmp2p.Packet 896 897 switch pb := pb.(type) { 898 case *tmp2p.Packet: // already a packet 899 msg = *pb 900 case *tmp2p.PacketPing: 901 msg = tmp2p.Packet{ 902 Sum: &tmp2p.Packet_PacketPing{ 903 PacketPing: pb, 904 }, 905 } 906 case *tmp2p.PacketPong: 907 msg = tmp2p.Packet{ 908 Sum: &tmp2p.Packet_PacketPong{ 909 PacketPong: pb, 910 }, 911 } 912 case *tmp2p.PacketMsg: 913 msg = tmp2p.Packet{ 914 Sum: &tmp2p.Packet_PacketMsg{ 915 PacketMsg: pb, 916 }, 917 } 918 default: 919 panic(fmt.Errorf("unknown packet type %T", pb)) 920 } 921 922 return &msg 923 }