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