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