Connection -> MConnection, huge refactor. True multiplexing.

This commit is contained in:
Jae Kwon
2014-07-28 01:41:25 -07:00
parent 197c8328c9
commit 34fe442514
19 changed files with 825 additions and 654 deletions

View File

@ -35,21 +35,35 @@ func (self ByteSlice) WriteTo(w io.Writer) (n int64, err error) {
return int64(n_ + 4), err return int64(n_ + 4), err
} }
func ReadByteSliceSafe(r io.Reader) (ByteSlice, error) { func (self ByteSlice) Reader() io.Reader {
length, err := ReadUInt32Safe(r) return bytes.NewReader([]byte(self))
if err != nil {
return nil, err
}
bytes := make([]byte, int(length))
_, err = io.ReadFull(r, bytes)
if err != nil {
return nil, err
}
return bytes, nil
} }
func ReadByteSlice(r io.Reader) ByteSlice { func ReadByteSliceSafe(r io.Reader) (bytes ByteSlice, n int64, err error) {
bytes, err := ReadByteSliceSafe(r) length, n_, err := ReadUInt32Safe(r)
n += n_
if err != nil {
return nil, n, err
}
bytes = make([]byte, int(length))
n__, err := io.ReadFull(r, bytes)
n += int64(n__)
if err != nil {
return nil, n, err
}
return bytes, n, nil
}
func ReadByteSliceN(r io.Reader) (bytes ByteSlice, n int64) {
bytes, n, err := ReadByteSliceSafe(r)
if err != nil {
panic(err)
}
return bytes, n
}
func ReadByteSlice(r io.Reader) (bytes ByteSlice) {
bytes, _, err := ReadByteSliceSafe(r)
if err != nil { if err != nil {
panic(err) panic(err)
} }

View File

@ -5,21 +5,19 @@ import (
) )
const ( const (
TYPE_NIL = Byte(0x00) TYPE_NIL = Byte(0x00)
TYPE_BYTE = Byte(0x01) TYPE_BYTE = Byte(0x01)
TYPE_INT8 = Byte(0x02) TYPE_INT8 = Byte(0x02)
TYPE_UINT8 = Byte(0x03) TYPE_UINT8 = Byte(0x03)
TYPE_INT16 = Byte(0x04) TYPE_INT16 = Byte(0x04)
TYPE_UINT16 = Byte(0x05) TYPE_UINT16 = Byte(0x05)
TYPE_INT32 = Byte(0x06) TYPE_INT32 = Byte(0x06)
TYPE_UINT32 = Byte(0x07) TYPE_UINT32 = Byte(0x07)
TYPE_INT64 = Byte(0x08) TYPE_INT64 = Byte(0x08)
TYPE_UINT64 = Byte(0x09) TYPE_UINT64 = Byte(0x09)
TYPE_STRING = Byte(0x10) TYPE_STRING = Byte(0x10)
TYPE_BYTESLICE = Byte(0x11) TYPE_BYTESLICE = Byte(0x11)
TYPE_TIME = Byte(0x20)
TYPE_TIME = Byte(0x20)
) )
func GetBinaryType(o Binary) Byte { func GetBinaryType(o Binary) Byte {
@ -44,57 +42,50 @@ func GetBinaryType(o Binary) Byte {
return TYPE_INT64 return TYPE_INT64
case UInt64: case UInt64:
return TYPE_UINT64 return TYPE_UINT64
case Int:
panic("Int not supported")
case UInt:
panic("UInt not supported")
case String: case String:
return TYPE_STRING return TYPE_STRING
case ByteSlice: case ByteSlice:
return TYPE_BYTESLICE return TYPE_BYTESLICE
case Time: case Time:
return TYPE_TIME return TYPE_TIME
default: default:
panic("Unsupported type") panic("Unsupported type")
} }
} }
func ReadBinary(r io.Reader) Binary { func ReadBinaryN(r io.Reader) (o Binary, n int64) {
type_ := ReadByte(r) type_, n_ := ReadByteN(r)
n += n_
switch type_ { switch type_ {
case TYPE_NIL: case TYPE_NIL:
return nil o, n_ = nil, 0
case TYPE_BYTE: case TYPE_BYTE:
return ReadByte(r) o, n_ = ReadByteN(r)
case TYPE_INT8: case TYPE_INT8:
return ReadInt8(r) o, n_ = ReadInt8N(r)
case TYPE_UINT8: case TYPE_UINT8:
return ReadUInt8(r) o, n_ = ReadUInt8N(r)
case TYPE_INT16: case TYPE_INT16:
return ReadInt16(r) o, n_ = ReadInt16N(r)
case TYPE_UINT16: case TYPE_UINT16:
return ReadUInt16(r) o, n_ = ReadUInt16N(r)
case TYPE_INT32: case TYPE_INT32:
return ReadInt32(r) o, n_ = ReadInt32N(r)
case TYPE_UINT32: case TYPE_UINT32:
return ReadUInt32(r) o, n_ = ReadUInt32N(r)
case TYPE_INT64: case TYPE_INT64:
return ReadInt64(r) o, n_ = ReadInt64N(r)
case TYPE_UINT64: case TYPE_UINT64:
return ReadUInt64(r) o, n_ = ReadUInt64N(r)
case TYPE_STRING: case TYPE_STRING:
return ReadString(r) o, n_ = ReadStringN(r)
case TYPE_BYTESLICE: case TYPE_BYTESLICE:
return ReadByteSlice(r) o, n_ = ReadByteSliceN(r)
case TYPE_TIME: case TYPE_TIME:
return ReadTime(r) o, n_ = ReadTimeN(r)
default: default:
panic("Unsupported type") panic("Unsupported type")
} }
n += n_
return o, n
} }

View File

@ -40,17 +40,25 @@ func (self Byte) WriteTo(w io.Writer) (int64, error) {
return int64(n), err return int64(n), err
} }
func ReadByteSafe(r io.Reader) (Byte, error) { func ReadByteSafe(r io.Reader) (Byte, int64, error) {
buf := [1]byte{0} buf := [1]byte{0}
_, err := io.ReadFull(r, buf[:]) n, err := io.ReadFull(r, buf[:])
if err != nil { if err != nil {
return 0, err return 0, int64(n), err
} }
return Byte(buf[0]), nil return Byte(buf[0]), int64(n), nil
}
func ReadByteN(r io.Reader) (Byte, int64) {
b, n, err := ReadByteSafe(r)
if err != nil {
panic(err)
}
return b, n
} }
func ReadByte(r io.Reader) Byte { func ReadByte(r io.Reader) Byte {
b, err := ReadByteSafe(r) b, _, err := ReadByteSafe(r)
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -80,17 +88,25 @@ func (self Int8) WriteTo(w io.Writer) (int64, error) {
return int64(n), err return int64(n), err
} }
func ReadInt8Safe(r io.Reader) (Int8, error) { func ReadInt8Safe(r io.Reader) (Int8, int64, error) {
buf := [1]byte{0} buf := [1]byte{0}
_, err := io.ReadFull(r, buf[:]) n, err := io.ReadFull(r, buf[:])
if err != nil { if err != nil {
return Int8(0), err return Int8(0), int64(n), err
} }
return Int8(buf[0]), nil return Int8(buf[0]), int64(n), nil
}
func ReadInt8N(r io.Reader) (Int8, int64) {
b, n, err := ReadInt8Safe(r)
if err != nil {
panic(err)
}
return b, n
} }
func ReadInt8(r io.Reader) Int8 { func ReadInt8(r io.Reader) Int8 {
b, err := ReadInt8Safe(r) b, _, err := ReadInt8Safe(r)
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -120,17 +136,25 @@ func (self UInt8) WriteTo(w io.Writer) (int64, error) {
return int64(n), err return int64(n), err
} }
func ReadUInt8Safe(r io.Reader) (UInt8, error) { func ReadUInt8Safe(r io.Reader) (UInt8, int64, error) {
buf := [1]byte{0} buf := [1]byte{0}
_, err := io.ReadFull(r, buf[:]) n, err := io.ReadFull(r, buf[:])
if err != nil { if err != nil {
return UInt8(0), err return UInt8(0), int64(n), err
} }
return UInt8(buf[0]), nil return UInt8(buf[0]), int64(n), nil
}
func ReadUInt8N(r io.Reader) (UInt8, int64) {
b, n, err := ReadUInt8Safe(r)
if err != nil {
panic(err)
}
return b, n
} }
func ReadUInt8(r io.Reader) UInt8 { func ReadUInt8(r io.Reader) UInt8 {
b, err := ReadUInt8Safe(r) b, _, err := ReadUInt8Safe(r)
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -162,17 +186,25 @@ func (self Int16) WriteTo(w io.Writer) (int64, error) {
return int64(n), err return int64(n), err
} }
func ReadInt16Safe(r io.Reader) (Int16, error) { func ReadInt16Safe(r io.Reader) (Int16, int64, error) {
buf := [2]byte{0} buf := [2]byte{0}
_, err := io.ReadFull(r, buf[:]) n, err := io.ReadFull(r, buf[:])
if err != nil { if err != nil {
return Int16(0), err return Int16(0), int64(n), err
} }
return Int16(binary.LittleEndian.Uint16(buf[:])), nil return Int16(binary.LittleEndian.Uint16(buf[:])), int64(n), nil
}
func ReadInt16N(r io.Reader) (Int16, int64) {
b, n, err := ReadInt16Safe(r)
if err != nil {
panic(err)
}
return b, n
} }
func ReadInt16(r io.Reader) Int16 { func ReadInt16(r io.Reader) Int16 {
b, err := ReadInt16Safe(r) b, _, err := ReadInt16Safe(r)
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -204,17 +236,25 @@ func (self UInt16) WriteTo(w io.Writer) (int64, error) {
return int64(n), err return int64(n), err
} }
func ReadUInt16Safe(r io.Reader) (UInt16, error) { func ReadUInt16Safe(r io.Reader) (UInt16, int64, error) {
buf := [2]byte{0} buf := [2]byte{0}
_, err := io.ReadFull(r, buf[:]) n, err := io.ReadFull(r, buf[:])
if err != nil { if err != nil {
return UInt16(0), err return UInt16(0), int64(n), err
} }
return UInt16(binary.LittleEndian.Uint16(buf[:])), nil return UInt16(binary.LittleEndian.Uint16(buf[:])), int64(n), nil
}
func ReadUInt16N(r io.Reader) (UInt16, int64) {
b, n, err := ReadUInt16Safe(r)
if err != nil {
panic(err)
}
return b, n
} }
func ReadUInt16(r io.Reader) UInt16 { func ReadUInt16(r io.Reader) UInt16 {
b, err := ReadUInt16Safe(r) b, _, err := ReadUInt16Safe(r)
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -246,17 +286,25 @@ func (self Int32) WriteTo(w io.Writer) (int64, error) {
return int64(n), err return int64(n), err
} }
func ReadInt32Safe(r io.Reader) (Int32, error) { func ReadInt32Safe(r io.Reader) (Int32, int64, error) {
buf := [4]byte{0} buf := [4]byte{0}
_, err := io.ReadFull(r, buf[:]) n, err := io.ReadFull(r, buf[:])
if err != nil { if err != nil {
return Int32(0), err return Int32(0), int64(n), err
} }
return Int32(binary.LittleEndian.Uint32(buf[:])), nil return Int32(binary.LittleEndian.Uint32(buf[:])), int64(n), nil
}
func ReadInt32N(r io.Reader) (Int32, int64) {
b, n, err := ReadInt32Safe(r)
if err != nil {
panic(err)
}
return b, n
} }
func ReadInt32(r io.Reader) Int32 { func ReadInt32(r io.Reader) Int32 {
b, err := ReadInt32Safe(r) b, _, err := ReadInt32Safe(r)
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -288,17 +336,25 @@ func (self UInt32) WriteTo(w io.Writer) (int64, error) {
return int64(n), err return int64(n), err
} }
func ReadUInt32Safe(r io.Reader) (UInt32, error) { func ReadUInt32Safe(r io.Reader) (UInt32, int64, error) {
buf := [4]byte{0} buf := [4]byte{0}
_, err := io.ReadFull(r, buf[:]) n, err := io.ReadFull(r, buf[:])
if err != nil { if err != nil {
return UInt32(0), err return UInt32(0), int64(n), err
} }
return UInt32(binary.LittleEndian.Uint32(buf[:])), nil return UInt32(binary.LittleEndian.Uint32(buf[:])), int64(n), nil
}
func ReadUInt32N(r io.Reader) (UInt32, int64) {
b, n, err := ReadUInt32Safe(r)
if err != nil {
panic(err)
}
return b, n
} }
func ReadUInt32(r io.Reader) UInt32 { func ReadUInt32(r io.Reader) UInt32 {
b, err := ReadUInt32Safe(r) b, _, err := ReadUInt32Safe(r)
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -330,17 +386,25 @@ func (self Int64) WriteTo(w io.Writer) (int64, error) {
return int64(n), err return int64(n), err
} }
func ReadInt64Safe(r io.Reader) (Int64, error) { func ReadInt64Safe(r io.Reader) (Int64, int64, error) {
buf := [8]byte{0} buf := [8]byte{0}
_, err := io.ReadFull(r, buf[:]) n, err := io.ReadFull(r, buf[:])
if err != nil { if err != nil {
return Int64(0), err return Int64(0), int64(n), err
} }
return Int64(binary.LittleEndian.Uint64(buf[:])), nil return Int64(binary.LittleEndian.Uint64(buf[:])), int64(n), nil
}
func ReadInt64N(r io.Reader) (Int64, int64) {
b, n, err := ReadInt64Safe(r)
if err != nil {
panic(err)
}
return b, n
} }
func ReadInt64(r io.Reader) Int64 { func ReadInt64(r io.Reader) Int64 {
b, err := ReadInt64Safe(r) b, _, err := ReadInt64Safe(r)
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -372,87 +436,27 @@ func (self UInt64) WriteTo(w io.Writer) (int64, error) {
return int64(n), err return int64(n), err
} }
func ReadUInt64Safe(r io.Reader) (UInt64, error) { func ReadUInt64Safe(r io.Reader) (UInt64, int64, error) {
buf := [8]byte{0} buf := [8]byte{0}
_, err := io.ReadFull(r, buf[:]) n, err := io.ReadFull(r, buf[:])
if err != nil { if err != nil {
return UInt64(0), err return UInt64(0), int64(n), err
} }
return UInt64(binary.LittleEndian.Uint64(buf[:])), nil return UInt64(binary.LittleEndian.Uint64(buf[:])), int64(n), nil
}
func ReadUInt64N(r io.Reader) (UInt64, int64) {
b, n, err := ReadUInt64Safe(r)
if err != nil {
panic(err)
}
return b, n
} }
func ReadUInt64(r io.Reader) UInt64 { func ReadUInt64(r io.Reader) UInt64 {
b, err := ReadUInt64Safe(r) b, _, err := ReadUInt64Safe(r)
if err != nil { if err != nil {
panic(err) panic(err)
} }
return b return b
} }
// Int
func (self Int) Equals(other Binary) bool {
return self == other
}
func (self Int) Less(other Binary) bool {
if o, ok := other.(Int); ok {
return self < o
} else {
panic("Cannot compare unequal types")
}
}
func (self Int) ByteSize() int {
return 8
}
func (self Int) WriteTo(w io.Writer) (int64, error) {
buf := []byte{0, 0, 0, 0, 0, 0, 0, 0}
binary.LittleEndian.PutUint64(buf, uint64(self))
n, err := w.Write(buf)
return int64(n), err
}
func ReadInt(r io.Reader) Int {
buf := [8]byte{0}
_, err := io.ReadFull(r, buf[:])
if err != nil {
panic(err)
}
return Int(binary.LittleEndian.Uint64(buf[:]))
}
// UInt
func (self UInt) Equals(other Binary) bool {
return self == other
}
func (self UInt) Less(other Binary) bool {
if o, ok := other.(UInt); ok {
return self < o
} else {
panic("Cannot compare unequal types")
}
}
func (self UInt) ByteSize() int {
return 8
}
func (self UInt) WriteTo(w io.Writer) (int64, error) {
buf := []byte{0, 0, 0, 0, 0, 0, 0, 0}
binary.LittleEndian.PutUint64(buf, uint64(self))
n, err := w.Write(buf)
return int64(n), err
}
func ReadUInt(r io.Reader) UInt {
buf := [8]byte{0}
_, err := io.ReadFull(r, buf[:])
if err != nil {
panic(err)
}
return UInt(binary.LittleEndian.Uint64(buf[:]))
}

View File

@ -32,21 +32,31 @@ func (self String) WriteTo(w io.Writer) (n int64, err error) {
return int64(n_ + 4), err return int64(n_ + 4), err
} }
func ReadStringSafe(r io.Reader) (String, error) { func ReadStringSafe(r io.Reader) (str String, n int64, err error) {
length, err := ReadUInt32Safe(r) length, n_, err := ReadUInt32Safe(r)
n += n_
if err != nil { if err != nil {
return "", err return "", n, err
} }
bytes := make([]byte, int(length)) bytes := make([]byte, int(length))
_, err = io.ReadFull(r, bytes) n__, err := io.ReadFull(r, bytes)
n += int64(n__)
if err != nil { if err != nil {
return "", err return "", n, err
} }
return String(bytes), nil return String(bytes), n, nil
} }
func ReadString(r io.Reader) String { func ReadStringN(r io.Reader) (str String, n int64) {
str, err := ReadStringSafe(r) str, n, err := ReadStringSafe(r)
if err != nil {
panic(err)
}
return str, n
}
func ReadString(r io.Reader) (str String) {
str, _, err := ReadStringSafe(r)
if err != nil { if err != nil {
panic(err) panic(err)
} }

View File

@ -33,6 +33,26 @@ func (self Time) WriteTo(w io.Writer) (int64, error) {
return Int64(self.Unix()).WriteTo(w) return Int64(self.Unix()).WriteTo(w)
} }
func ReadTime(r io.Reader) Time { func ReadTimeSafe(r io.Reader) (Time, int64, error) {
return Time{time.Unix(int64(ReadInt64(r)), 0)} t, n, err := ReadInt64Safe(r)
if err != nil {
return Time{}, n, err
}
return Time{time.Unix(int64(t), 0)}, n, nil
}
func ReadTimeN(r io.Reader) (Time, int64) {
t, n, err := ReadTimeSafe(r)
if err != nil {
panic(err)
}
return t, n
}
func ReadTime(r io.Reader) Time {
t, _, err := ReadTimeSafe(r)
if err != nil {
panic(err)
}
return t
} }

View File

@ -1,6 +1,16 @@
package blocks package blocks
import ( import (
"bytes"
"encoding/json"
"fmt"
"io"
"sync"
"sync/atomic"
"time"
. "github.com/tendermint/tendermint/binary"
. "github.com/tendermint/tendermint/common"
db_ "github.com/tendermint/tendermint/db" db_ "github.com/tendermint/tendermint/db"
"github.com/tendermint/tendermint/p2p" "github.com/tendermint/tendermint/p2p"
) )
@ -26,7 +36,7 @@ const (
dataTypeTxs = byte(0x03) dataTypeTxs = byte(0x03)
) )
func _dataKey(dataType byte, height int) { func _dataKey(dataType byte, height int) string {
switch dataType { switch dataType {
case dataTypeHeader: case dataTypeHeader:
return fmt.Sprintf("H%v", height) return fmt.Sprintf("H%v", height)
@ -35,11 +45,12 @@ func _dataKey(dataType byte, height int) {
case dataTypeTxs: case dataTypeTxs:
return fmt.Sprintf("T%v", height) return fmt.Sprintf("T%v", height)
default: default:
panic("Unknown datatype %X", dataType) Panicf("Unknown datatype %X", dataType)
return "" // should not happen
} }
} }
func dataTypeFromObj(data interface{}) { func dataTypeFromObj(data interface{}) byte {
switch data.(type) { switch data.(type) {
case *Header: case *Header:
return dataTypeHeader return dataTypeHeader
@ -48,7 +59,8 @@ func dataTypeFromObj(data interface{}) {
case *Txs: case *Txs:
return dataTypeTxs return dataTypeTxs
default: default:
panic("Unexpected datatype: %v", data) Panicf("Unexpected datatype: %v", data)
return byte(0x00) // should not happen
} }
} }
@ -99,8 +111,8 @@ func (bm *BlockManager) Stop() {
// NOTE: assumes that data is already validated. // NOTE: assumes that data is already validated.
func (bm *BlockManager) StoreData(dataObj interface{}) { func (bm *BlockManager) StoreData(dataObj interface{}) {
bm.mtx.Lock() //bm.mtx.Lock()
defer bm.mtx.Unlock() //defer bm.mtx.Unlock()
dataType := dataTypeForObj(dataObj) dataType := dataTypeForObj(dataObj)
dataKey := _dataKey(dataType, dataObj) dataKey := _dataKey(dataType, dataObj)
// Update state // Update state

View File

@ -91,13 +91,13 @@ func TestBlock(t *testing.T) {
Time: randVar(), Time: randVar(),
PrevHash: randBytes(32), PrevHash: randBytes(32),
ValidationHash: randBytes(32), ValidationHash: randBytes(32),
DataHash: randBytes(32), TxsHash: randBytes(32),
}, },
Validation{ Validation{
Signatures: []Signature{randSig(), randSig()}, Signatures: []Signature{randSig(), randSig()},
Adjustments: []Adjustment{bond, unbond, timeout, dupeout}, Adjustments: []Adjustment{bond, unbond, timeout, dupeout},
}, },
Data{ Txs{
Txs: []Tx{sendTx, nameTx}, Txs: []Tx{sendTx, nameTx},
}, },
} }

15
blocks/log.go Normal file
View File

@ -0,0 +1,15 @@
package blocks
import (
"github.com/op/go-logging"
)
var log = logging.MustGetLogger("block")
func init() {
logging.SetFormatter(logging.MustStringFormatter("[%{level:.1s}] %{message}"))
}
func SetLogger(l *logging.Logger) {
log = l
}

View File

@ -6,12 +6,12 @@ import (
) )
/* /*
Throttler fires an event at most "dur" after each .Set() call. ThrottleTimer fires an event at most "dur" after each .Set() call.
If a short burst of .Set() calls happens, Throttler fires once. If a short burst of .Set() calls happens, ThrottleTimer fires once.
If a long continuous burst of .Set() calls happens, Throttler fires If a long continuous burst of .Set() calls happens, ThrottleTimer fires
at most once every "dur". at most once every "dur".
*/ */
type Throttler struct { type ThrottleTimer struct {
Ch chan struct{} Ch chan struct{}
quit chan struct{} quit chan struct{}
dur time.Duration dur time.Duration
@ -19,16 +19,16 @@ type Throttler struct {
isSet uint32 isSet uint32
} }
func NewThrottler(dur time.Duration) *Throttler { func NewThrottleTimer(dur time.Duration) *ThrottleTimer {
var ch = make(chan struct{}) var ch = make(chan struct{})
var quit = make(chan struct{}) var quit = make(chan struct{})
var t = &Throttler{Ch: ch, dur: dur, quit: quit} var t = &ThrottleTimer{Ch: ch, dur: dur, quit: quit}
t.timer = time.AfterFunc(dur, t.fireHandler) t.timer = time.AfterFunc(dur, t.fireHandler)
t.timer.Stop() t.timer.Stop()
return t return t
} }
func (t *Throttler) fireHandler() { func (t *ThrottleTimer) fireHandler() {
select { select {
case t.Ch <- struct{}{}: case t.Ch <- struct{}{}:
atomic.StoreUint32(&t.isSet, 0) atomic.StoreUint32(&t.isSet, 0)
@ -36,13 +36,13 @@ func (t *Throttler) fireHandler() {
} }
} }
func (t *Throttler) Set() { func (t *ThrottleTimer) Set() {
if atomic.CompareAndSwapUint32(&t.isSet, 0, 1) { if atomic.CompareAndSwapUint32(&t.isSet, 0, 1) {
t.timer.Reset(t.dur) t.timer.Reset(t.dur)
} }
} }
func (t *Throttler) Stop() bool { func (t *ThrottleTimer) Stop() bool {
close(t.quit) close(t.quit)
return t.timer.Stop() return t.timer.Stop()
} }

2
log.go
View File

@ -4,6 +4,7 @@ import (
"os" "os"
"github.com/op/go-logging" "github.com/op/go-logging"
"github.com/tendermint/tendermint/block"
"github.com/tendermint/tendermint/p2p" "github.com/tendermint/tendermint/p2p"
) )
@ -27,4 +28,5 @@ func init() {
*/ */
p2p.SetLogger(log) p2p.SetLogger(log)
block.SetLogger(log)
} }

View File

@ -305,23 +305,21 @@ func (self *IAVLNode) fill(db Db) {
self.height = uint8(ReadUInt8(r)) self.height = uint8(ReadUInt8(r))
self.size = uint64(ReadUInt64(r)) self.size = uint64(ReadUInt64(r))
// key // key
key := ReadBinary(r) key, _ := ReadBinaryN(r)
self.key = key.(Key) self.key = key.(Key)
if self.height == 0 { if self.height == 0 {
// value // value
self.value = ReadBinary(r) self.value, _ = ReadBinaryN(r)
} else { } else {
// left // left
var leftHash ByteSlice leftHash := ReadByteSlice(r)
leftHash = ReadByteSlice(r)
self.left = &IAVLNode{ self.left = &IAVLNode{
hash: leftHash, hash: leftHash,
flags: IAVLNODE_FLAG_PERSISTED | IAVLNODE_FLAG_PLACEHOLDER, flags: IAVLNODE_FLAG_PERSISTED | IAVLNODE_FLAG_PLACEHOLDER,
} }
// right // right
var rightHash ByteSlice rightHash := ReadByteSlice(r)
rightHash = ReadByteSlice(r)
self.right = &IAVLNode{ self.right = &IAVLNode{
hash: rightHash, hash: rightHash,
flags: IAVLNODE_FLAG_PERSISTED | IAVLNODE_FLAG_PLACEHOLDER, flags: IAVLNODE_FLAG_PERSISTED | IAVLNODE_FLAG_PLACEHOLDER,

View File

@ -3,85 +3,110 @@ package p2p
import ( import (
"bufio" "bufio"
"fmt" "fmt"
"io"
"math"
"net" "net"
"sync/atomic" "sync/atomic"
"time" "time"
flow "code.google.com/p/mxk/go1/flowcontrol"
"github.com/op/go-logging" "github.com/op/go-logging"
. "github.com/tendermint/tendermint/binary" . "github.com/tendermint/tendermint/binary"
. "github.com/tendermint/tendermint/common" . "github.com/tendermint/tendermint/common"
) )
const ( const (
numBatchPackets = 10
minReadBufferSize = 1024 minReadBufferSize = 1024
minWriteBufferSize = 1024 minWriteBufferSize = 1024
flushThrottleMS = 50 flushThrottleMS = 50
outQueueSize = 50
idleTimeoutMinutes = 5 idleTimeoutMinutes = 5
updateStatsSeconds = 2
pingTimeoutMinutes = 2 pingTimeoutMinutes = 2
defaultSendRate = 51200 // 5Kb/s
defaultRecvRate = 51200 // 5Kb/s
) )
/* /*
A Connection wraps a network connection and handles buffering and multiplexing. A MConnection wraps a network connection and handles buffering and multiplexing.
"Packets" are sent with ".Send(Packet)". ByteSlices are sent with ".Send(channelId, bytes)".
Packets received are sent to channels as commanded by the ".Start(...)" method. Inbound ByteSlices are pushed to the designated chan<- InboundBytes.
*/ */
type Connection struct { type MConnection struct {
ioStats IOStats conn net.Conn
bufReader *bufio.Reader
bufWriter *bufio.Writer
sendMonitor *flow.Monitor
recvMonitor *flow.Monitor
sendRate int64
recvRate int64
flushTimer *ThrottleTimer // flush writes as necessary but throttled.
canSend chan struct{}
quit chan struct{}
pingTimer *RepeatTimer // send pings periodically
pong chan struct{}
chStatsTimer *RepeatTimer // update channel stats periodically
channels []*Channel
channelsIdx map[byte]*Channel
onError func(interface{})
started uint32
stopped uint32
errored uint32
sendQueue chan Packet // never closes _peer *Peer // hacky optimization
conn net.Conn
bufReader *bufio.Reader
bufWriter *bufio.Writer
flushThrottler *Throttler
quit chan struct{}
pingRepeatTimer *RepeatTimer
pong chan struct{}
channels map[string]*Channel
onError func(interface{})
started uint32
stopped uint32
errored uint32
} }
const ( func NewMConnection(conn net.Conn, chDescs []*ChannelDescriptor, onError func(interface{})) *MConnection {
packetTypePing = UInt8(0x00)
packetTypePong = UInt8(0x01)
packetTypeMessage = UInt8(0x10)
)
func NewConnection(conn net.Conn) *Connection { mconn := &MConnection{
return &Connection{ conn: conn,
sendQueue: make(chan Packet, outQueueSize), bufReader: bufio.NewReaderSize(conn, minReadBufferSize),
conn: conn, bufWriter: bufio.NewWriterSize(conn, minWriteBufferSize),
bufReader: bufio.NewReaderSize(conn, minReadBufferSize), sendMonitor: flow.New(0, 0),
bufWriter: bufio.NewWriterSize(conn, minWriteBufferSize), recvMonitor: flow.New(0, 0),
flushThrottler: NewThrottler(flushThrottleMS * time.Millisecond), sendRate: defaultSendRate,
quit: make(chan struct{}), recvRate: defaultRecvRate,
pingRepeatTimer: NewRepeatTimer(pingTimeoutMinutes * time.Minute), flushTimer: NewThrottleTimer(flushThrottleMS * time.Millisecond),
pong: make(chan struct{}), canSend: make(chan struct{}, 1),
quit: make(chan struct{}),
pingTimer: NewRepeatTimer(pingTimeoutMinutes * time.Minute),
pong: make(chan struct{}),
chStatsTimer: NewRepeatTimer(updateStatsSeconds * time.Second),
onError: onError,
} }
// Create channels
var channelsIdx = map[byte]*Channel{}
var channels = []*Channel{}
for _, desc := range chDescs {
channel := newChannel(mconn, desc)
channelsIdx[channel.id] = channel
channels = append(channels, channel)
}
mconn.channels = channels
mconn.channelsIdx = channelsIdx
return mconn
} }
// .Start() begins multiplexing packets to and from "channels". // .Start() begins multiplexing packets to and from "channels".
// If an error occurs, the recovered reason is passed to "onError". func (c *MConnection) Start() {
func (c *Connection) Start(channels map[string]*Channel, onError func(interface{})) {
if atomic.CompareAndSwapUint32(&c.started, 0, 1) { if atomic.CompareAndSwapUint32(&c.started, 0, 1) {
log.Debug("Starting %v", c) log.Debug("Starting %v", c)
c.channels = channels
c.onError = onError
go c.sendHandler() go c.sendHandler()
go c.recvHandler() go c.recvHandler()
} }
} }
func (c *Connection) Stop() { func (c *MConnection) Stop() {
if atomic.CompareAndSwapUint32(&c.stopped, 0, 1) { if atomic.CompareAndSwapUint32(&c.stopped, 0, 1) {
log.Debug("Stopping %v", c) log.Debug("Stopping %v", c)
close(c.quit) close(c.quit)
c.conn.Close() c.conn.Close()
c.flushThrottler.Stop() c.flushTimer.Stop()
c.pingRepeatTimer.Stop() c.chStatsTimer.Stop()
c.pingTimer.Stop()
// We can't close pong safely here because // We can't close pong safely here because
// recvHandler may write to it after we've stopped. // recvHandler may write to it after we've stopped.
// Though it doesn't need to get closed at all, // Though it doesn't need to get closed at all,
@ -90,81 +115,123 @@ func (c *Connection) Stop() {
} }
} }
func (c *Connection) LocalAddress() *NetAddress { func (c *MConnection) LocalAddress() *NetAddress {
return NewNetAddress(c.conn.LocalAddr()) return NewNetAddress(c.conn.LocalAddr())
} }
func (c *Connection) RemoteAddress() *NetAddress { func (c *MConnection) RemoteAddress() *NetAddress {
return NewNetAddress(c.conn.RemoteAddr()) return NewNetAddress(c.conn.RemoteAddr())
} }
// Returns true if successfully queued, func (c *MConnection) String() string {
// Returns false if connection was closed.
// Blocks.
func (c *Connection) Send(pkt Packet) bool {
select {
case c.sendQueue <- pkt:
return true
case <-c.quit:
return false
}
}
func (c *Connection) String() string {
return fmt.Sprintf("/%v/", c.conn.RemoteAddr()) return fmt.Sprintf("/%v/", c.conn.RemoteAddr())
} }
func (c *Connection) flush() { func (c *MConnection) flush() {
// TODO: this is pretty naive.
// We end up flushing when we don't have to (yet).
// A better solution might require us implementing our own buffered writer.
err := c.bufWriter.Flush() err := c.bufWriter.Flush()
if err != nil { if err != nil {
if atomic.LoadUint32(&c.stopped) != 1 { if atomic.LoadUint32(&c.stopped) != 1 {
log.Warning("Connection flush failed: %v", err) log.Warning("MConnection flush failed: %v", err)
} }
} }
} }
// Catch panics, usually caused by remote disconnects. // Catch panics, usually caused by remote disconnects.
func (c *Connection) _recover() { func (c *MConnection) _recover() {
if r := recover(); r != nil { if r := recover(); r != nil {
c.Stop() c.stopForError(r)
if atomic.CompareAndSwapUint32(&c.errored, 0, 1) { }
if c.onError != nil { }
c.onError(r)
} func (c *MConnection) stopForError(r interface{}) {
c.Stop()
if atomic.CompareAndSwapUint32(&c.errored, 0, 1) {
if c.onError != nil {
c.onError(r)
} }
} }
} }
// sendHandler pulls from .sendQueue and writes to .bufWriter // Queues a message to be sent to channel.
func (c *Connection) sendHandler() { func (c *MConnection) Send(chId byte, bytes ByteSlice) bool {
// Send message to channel.
channel, ok := c.channelsIdx[chId]
if !ok {
log.Error("Cannot send bytes, unknown channel %X", chId)
return false
}
channel.sendBytes(bytes)
// Wake up sendHandler if necessary
select {
case c.canSend <- struct{}{}:
default:
}
return true
}
// Queues a message to be sent to channel.
// Nonblocking, returns true if successful.
func (c *MConnection) TrySend(chId byte, bytes ByteSlice) bool {
// Send message to channel.
channel, ok := c.channelsIdx[chId]
if !ok {
log.Error("Cannot send bytes, unknown channel %X", chId)
return false
}
ok = channel.trySendBytes(bytes)
if ok {
// Wake up sendHandler if necessary
select {
case c.canSend <- struct{}{}:
default:
}
}
return ok
}
// sendHandler polls for packets to send from channels.
func (c *MConnection) sendHandler() {
defer c._recover() defer c._recover()
FOR_LOOP: FOR_LOOP:
for { for {
var err error var err error
select { select {
case sendPkt := <-c.sendQueue: case <-c.flushTimer.Ch:
_, err = packetTypeMessage.WriteTo(c.bufWriter) // NOTE: flushTimer.Set() must be called every time
if err != nil { // something is written to .bufWriter.
break
}
_, err = sendPkt.WriteTo(c.bufWriter)
c.flushThrottler.Set()
case <-c.flushThrottler.Ch:
c.flush() c.flush()
case <-c.pingRepeatTimer.Ch: case <-c.chStatsTimer.Ch:
_, err = packetTypePing.WriteTo(c.bufWriter) for _, channel := range c.channels {
// log.Debug("PING %v", c) channel.updateStats()
}
case <-c.pingTimer.Ch:
var n int64
n, err = packetTypePing.WriteTo(c.bufWriter)
c.sendMonitor.Update(int(n))
c.flush() c.flush()
case <-c.pong: case <-c.pong:
_, err = packetTypePong.WriteTo(c.bufWriter) var n int64
// log.Debug("PONG %v", c) n, err = packetTypePong.WriteTo(c.bufWriter)
c.sendMonitor.Update(int(n))
c.flush() c.flush()
case <-c.quit: case <-c.quit:
break FOR_LOOP break FOR_LOOP
case <-c.canSend:
// Send some packets
eof := c.sendSomePackets()
if !eof {
// Keep sendHandler awake.
select {
case c.canSend <- struct{}{}:
default:
}
}
} }
if atomic.LoadUint32(&c.stopped) == 1 { if atomic.LoadUint32(&c.stopped) == 1 {
@ -180,22 +247,75 @@ FOR_LOOP:
// Cleanup // Cleanup
} }
// recvHandler reads from .bufReader and pushes to the appropriate // Returns true if messages from channels were exhausted.
// channel's recvQueue. // Blocks in accordance to .sendMonitor throttling.
func (c *Connection) recvHandler() { func (c *MConnection) sendSomePackets() bool {
// Block until .sendMonitor says we can write.
// Once we're ready we send more than we asked for,
// but amortized it should even out.
c.sendMonitor.Limit(maxPacketSize, atomic.LoadInt64(&c.sendRate), true)
// Now send some packets.
for i := 0; i < numBatchPackets; i++ {
if c.sendPacket() {
return true
}
}
return false
}
// Returns true if messages from channels were exhausted.
func (c *MConnection) sendPacket() bool {
// Choose a channel to create a packet from.
// The chosen channel will be the one whose recentlySent/priority is the least.
var leastRatio float32 = math.MaxFloat32
var leastChannel *Channel
for _, channel := range c.channels {
// If nothing to send, skip this channel
if !channel.sendPending() {
continue
}
// Get ratio, and keep track of lowest ratio.
ratio := float32(channel.recentlySent) / float32(channel.priority)
if ratio < leastRatio {
leastRatio = ratio
leastChannel = channel
}
}
// Nothing to send?
if leastChannel == nil {
return true
} else {
log.Debug("Found a packet to send")
}
// Make & send a packet from this channel
n, err := leastChannel.writePacketTo(c.bufWriter)
if err != nil {
log.Warning("Failed to write packet. Error: %v", err)
c.stopForError(err)
return true
}
c.sendMonitor.Update(int(n))
c.flushTimer.Set()
return false
}
// recvHandler reads packets and reconstructs the message using the channels' "recving" buffer.
// After a whole message has been assembled, it's pushed to the Channel's recvQueue.
// Blocks depending on how the connection is throttled.
func (c *MConnection) recvHandler() {
defer c._recover() defer c._recover()
FOR_LOOP: FOR_LOOP:
for { for {
pktType, err := ReadUInt8Safe(c.bufReader) // Block until .recvMonitor says we can read.
if log.IsEnabledFor(logging.DEBUG) { c.recvMonitor.Limit(maxPacketSize, atomic.LoadInt64(&c.recvRate), true)
// peeking into bufReader
numBytes := c.bufReader.Buffered() // Read packet type
bytes, err := c.bufReader.Peek(MinInt(numBytes, 100)) pktType, n, err := ReadUInt8Safe(c.bufReader)
if err != nil { c.recvMonitor.Update(int(n))
log.Debug("recvHandler packet type %X, peeked: %X", pktType, bytes)
}
}
if err != nil { if err != nil {
if atomic.LoadUint32(&c.stopped) != 1 { if atomic.LoadUint32(&c.stopped) != 1 {
log.Info("%v failed @ recvHandler with err: %v", c, err) log.Info("%v failed @ recvHandler with err: %v", c, err)
@ -204,15 +324,25 @@ FOR_LOOP:
break FOR_LOOP break FOR_LOOP
} }
// Peek into bufReader for debugging
if log.IsEnabledFor(logging.DEBUG) {
numBytes := c.bufReader.Buffered()
bytes, err := c.bufReader.Peek(MinInt(numBytes, 100))
if err != nil {
log.Debug("recvHandler packet type %X, peeked: %X", pktType, bytes)
}
}
// Read more depending on packet type.
switch pktType { switch pktType {
case packetTypePing: case packetTypePing:
// TODO: keep track of these, make sure it isn't abused // TODO: prevent abuse, as they cause flush()'s.
// as they cause flush()'s in the send buffer.
c.pong <- struct{}{} c.pong <- struct{}{}
case packetTypePong: case packetTypePong:
// do nothing // do nothing
case packetTypeMessage: case packetTypeMessage:
pkt, err := ReadPacketSafe(c.bufReader) pkt, n, err := readPacketSafe(c.bufReader)
c.recvMonitor.Update(int(n))
if err != nil { if err != nil {
if atomic.LoadUint32(&c.stopped) != 1 { if atomic.LoadUint32(&c.stopped) != 1 {
log.Info("%v failed @ recvHandler", c) log.Info("%v failed @ recvHandler", c)
@ -220,16 +350,18 @@ FOR_LOOP:
} }
break FOR_LOOP break FOR_LOOP
} }
channel := c.channels[string(pkt.Channel)] channel := c.channels[pkt.ChannelId]
if channel == nil { if channel == nil {
Panicf("Unknown channel %v", pkt.Channel) Panicf("Unknown channel %v", pkt.ChannelId)
} }
channel.recvQueue <- pkt channel.recvPacket(pkt)
default: default:
Panicf("Unknown message type %v", pktType) Panicf("Unknown message type %v", pktType)
} }
c.pingRepeatTimer.Reset() // TODO: shouldn't this go in the sendHandler?
// Better to send a packet when *we* haven't sent anything for a while.
c.pingTimer.Reset()
} }
// Cleanup // Cleanup
@ -239,13 +371,191 @@ FOR_LOOP:
} }
} }
/* IOStats */ //-----------------------------------------------------------------------------
type IOStats struct {
TimeConnected Time type ChannelDescriptor struct {
LastSent Time Id byte
LastRecv Time SendQueueCapacity int // One per MConnection.
BytesRecv UInt64 RecvQueueCapacity int // Global for this channel.
BytesSent UInt64 RecvBufferSize int
PktsRecv UInt64 DefaultPriority uint
PktsSent UInt64
// TODO: kinda hacky.
// This is created by the switch, one per channel.
recvQueue chan InboundBytes
}
// TODO: lowercase.
// NOTE: not goroutine-safe.
type Channel struct {
conn *MConnection
desc *ChannelDescriptor
id byte
recvQueue chan InboundBytes
sendQueue chan ByteSlice
recving ByteSlice
sending ByteSlice
priority uint
recentlySent int64 // exponential moving average
}
func newChannel(conn *MConnection, desc *ChannelDescriptor) *Channel {
if desc.DefaultPriority <= 0 {
panic("Channel default priority must be a postive integer")
}
return &Channel{
conn: conn,
desc: desc,
id: desc.Id,
recvQueue: desc.recvQueue,
sendQueue: make(chan ByteSlice, desc.SendQueueCapacity),
recving: make([]byte, 0, desc.RecvBufferSize),
priority: desc.DefaultPriority,
}
}
// Queues message to send to this channel.
func (ch *Channel) sendBytes(bytes ByteSlice) {
ch.sendQueue <- bytes
}
// Queues message to send to this channel.
// Nonblocking, returns true if successful.
func (ch *Channel) trySendBytes(bytes ByteSlice) bool {
select {
case ch.sendQueue <- bytes:
return true
default:
return false
}
}
// Returns true if any packets are pending to be sent.
func (ch *Channel) sendPending() bool {
if len(ch.sending) == 0 {
if len(ch.sendQueue) == 0 {
return false
}
ch.sending = <-ch.sendQueue
}
return true
}
// Creates a new packet to send.
func (ch *Channel) nextPacket() packet {
packet := packet{}
packet.ChannelId = Byte(ch.id)
packet.Bytes = ch.sending[:MinInt(maxPacketSize, len(ch.sending))]
if len(ch.sending) <= maxPacketSize {
packet.EOF = Byte(0x01)
ch.sending = nil
} else {
packet.EOF = Byte(0x00)
ch.sending = ch.sending[MinInt(maxPacketSize, len(ch.sending)):]
}
return packet
}
// Writes next packet to w.
func (ch *Channel) writePacketTo(w io.Writer) (n int64, err error) {
packet := ch.nextPacket()
n, err = WriteTo(packetTypeMessage, w, n, err)
n, err = WriteTo(packet, w, n, err)
if err != nil {
ch.recentlySent += n
}
return
}
// Handles incoming packets.
func (ch *Channel) recvPacket(pkt packet) {
ch.recving = append(ch.recving, pkt.Bytes...)
if pkt.EOF == Byte(0x01) {
ch.recvQueue <- InboundBytes{ch.conn, ch.recving}
ch.recving = make([]byte, 0, ch.desc.RecvBufferSize)
}
}
// Call this periodically to update stats for throttling purposes.
func (ch *Channel) updateStats() {
// Exponential decay of stats.
// TODO: optimize.
ch.recentlySent = int64(float64(ch.recentlySent) * 0.5)
}
//-----------------------------------------------------------------------------
const (
maxPacketSize = 1024
packetTypePing = UInt8(0x00)
packetTypePong = UInt8(0x01)
packetTypeMessage = UInt8(0x10)
)
// Messages in channels are chopped into smaller packets for multiplexing.
type packet struct {
ChannelId Byte
EOF Byte // 1 means message ends here.
Bytes ByteSlice
}
func (p packet) WriteTo(w io.Writer) (n int64, err error) {
n, err = WriteTo(p.ChannelId, w, n, err)
n, err = WriteTo(p.EOF, w, n, err)
n, err = WriteTo(p.Bytes, w, n, err)
return
}
func (p packet) String() string {
return fmt.Sprintf("%v:%X", p.ChannelId, p.Bytes)
}
func readPacketSafe(r io.Reader) (pkt packet, n int64, err error) {
chId, n_, err := ReadByteSafe(r)
n += n_
if err != nil {
return
}
eof, n_, err := ReadByteSafe(r)
n += n_
if err != nil {
return
}
// TODO: packet length sanity check.
bytes, n_, err := ReadByteSliceSafe(r)
n += n_
if err != nil {
return
}
return packet{chId, eof, bytes}, n, nil
}
//-----------------------------------------------------------------------------
type InboundBytes struct {
MConn *MConnection
Bytes ByteSlice
}
//-----------------------------------------------------------------------------
// Convenience struct for writing typed messages.
// Reading requires a custom decoder that switches on the first type byte of a ByteSlice.
type TypedMessage struct {
Type Byte
Msg Binary
}
func (tm TypedMessage) WriteTo(w io.Writer) (n int64, err error) {
n, err = WriteTo(tm.Type, w, n, err)
n, err = WriteTo(tm.Msg, w, n, err)
return
}
func (tm TypedMessage) String() string {
return fmt.Sprintf("<%X:%v>", tm.Type, tm.Msg)
}
func (tm TypedMessage) Bytes() ByteSlice {
return BinaryBytes(tm)
} }

View File

@ -15,18 +15,18 @@ import (
Listener is part of a Server. Listener is part of a Server.
*/ */
type Listener interface { type Listener interface {
Connections() <-chan *Connection Connections() <-chan net.Conn
ExternalAddress() *NetAddress ExternalAddress() *NetAddress
Stop() Stop()
} }
/* /*
DefaultListener is an implementation that works on the golang network stack. DefaultListener is an implementation of Listener.
*/ */
type DefaultListener struct { type DefaultListener struct {
listener net.Listener listener net.Listener
extAddr *NetAddress extAddr *NetAddress
connections chan *Connection connections chan net.Conn
stopped uint32 stopped uint32
} }
@ -77,7 +77,7 @@ func NewDefaultListener(protocol string, lAddr string) Listener {
dl := &DefaultListener{ dl := &DefaultListener{
listener: listener, listener: listener,
extAddr: extAddr, extAddr: extAddr,
connections: make(chan *Connection, numBufferedConnections), connections: make(chan net.Conn, numBufferedConnections),
} }
go dl.listenHandler() go dl.listenHandler()
@ -100,8 +100,7 @@ func (l *DefaultListener) listenHandler() {
panic(err) panic(err)
} }
c := NewConnection(conn) l.connections <- conn
l.connections <- c
} }
// Cleanup // Cleanup
@ -113,7 +112,7 @@ func (l *DefaultListener) listenHandler() {
// A channel of inbound connections. // A channel of inbound connections.
// It gets closed when the listener closes. // It gets closed when the listener closes.
func (l *DefaultListener) Connections() <-chan *Connection { func (l *DefaultListener) Connections() <-chan net.Conn {
return l.connections return l.connections
} }

View File

@ -1,94 +0,0 @@
package p2p
import (
"bytes"
"fmt"
"io"
. "github.com/tendermint/tendermint/binary"
)
/*
A Message is anything that can be serialized.
The resulting serialized bytes of Message don't contain type information,
so messages are typically wrapped in a TypedMessage before put in the wire.
*/
type Message interface {
Binary
}
/*
A TypedMessage extends a Message with a single byte of type information.
When deserializing a message from the wire, a switch statement is needed
to dispatch to the correct constructor, typically named "ReadXXXMessage".
*/
type TypedMessage struct {
Type Byte
Message Message
}
func (tm TypedMessage) WriteTo(w io.Writer) (n int64, err error) {
n, err = WriteTo(tm.Type, w, n, err)
n, err = WriteTo(tm.Message, w, n, err)
return
}
func (tm TypedMessage) String() string {
return fmt.Sprintf("0x%X⋺%v", tm.Type, tm.Message)
}
//-----------------------------------------------------------------------------
/*
Packet encapsulates a ByteSlice on a Channel.
Typically the Bytes are the serialized form of a TypedMessage.
*/
type Packet struct {
Channel String
Bytes ByteSlice
// Hash
}
func NewPacket(chName String, msg Binary) Packet {
msgBytes := BinaryBytes(msg)
return Packet{
Channel: chName,
Bytes: msgBytes,
}
}
func (p Packet) WriteTo(w io.Writer) (n int64, err error) {
n, err = WriteTo(p.Channel, w, n, err)
n, err = WriteTo(p.Bytes, w, n, err)
return
}
func (p Packet) Reader() io.Reader {
return bytes.NewReader(p.Bytes)
}
func (p Packet) String() string {
return fmt.Sprintf("%v:%X", p.Channel, p.Bytes)
}
func ReadPacketSafe(r io.Reader) (pkt Packet, err error) {
chName, err := ReadStringSafe(r)
if err != nil {
return
}
// TODO: packet length sanity check.
bytes, err := ReadByteSliceSafe(r)
if err != nil {
return
}
return Packet{Channel: chName, Bytes: bytes}, nil
}
/*
InboundPacket extends Packet with fields relevant to inbound packets.
*/
type InboundPacket struct {
Peer *Peer
Time Time
Packet
}

View File

@ -55,9 +55,11 @@ func NewNetAddressIPPort(ip net.IP, port UInt16) *NetAddress {
} }
func ReadNetAddress(r io.Reader) *NetAddress { func ReadNetAddress(r io.Reader) *NetAddress {
ipBytes := ReadByteSlice(r)
port := ReadUInt16(r)
return &NetAddress{ return &NetAddress{
IP: net.IP(ReadByteSlice(r)), IP: net.IP(ipBytes),
Port: ReadUInt16(r), Port: port,
} }
} }
@ -89,20 +91,20 @@ func (na *NetAddress) String() string {
return addr return addr
} }
func (na *NetAddress) Dial() (*Connection, error) { func (na *NetAddress) Dial() (net.Conn, error) {
conn, err := net.Dial("tcp", na.String()) conn, err := net.Dial("tcp", na.String())
if err != nil { if err != nil {
return nil, err return nil, err
} }
return NewConnection(conn), nil return conn, nil
} }
func (na *NetAddress) DialTimeout(timeout time.Duration) (*Connection, error) { func (na *NetAddress) DialTimeout(timeout time.Duration) (net.Conn, error) {
conn, err := net.DialTimeout("tcp", na.String(), timeout) conn, err := net.DialTimeout("tcp", na.String(), timeout)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return NewConnection(conn), nil return conn, nil
} }
func (na *NetAddress) Routable() bool { func (na *NetAddress) Routable() bool {

View File

@ -3,8 +3,8 @@ package p2p
import ( import (
"fmt" "fmt"
"io" "io"
"net"
"sync/atomic" "sync/atomic"
"time"
. "github.com/tendermint/tendermint/binary" . "github.com/tendermint/tendermint/binary"
) )
@ -13,45 +13,38 @@ import (
type Peer struct { type Peer struct {
outbound bool outbound bool
conn *Connection mconn *MConnection
channels map[string]*Channel
quit chan struct{}
started uint32 started uint32
stopped uint32 stopped uint32
} }
func newPeer(conn *Connection, channels map[string]*Channel) *Peer { func newPeer(conn net.Conn, outbound bool, chDescs []*ChannelDescriptor, onPeerError func(*Peer, interface{})) *Peer {
return &Peer{ var p *Peer
conn: conn, onError := func(r interface{}) {
channels: channels, p.stop()
quit: make(chan struct{}), onPeerError(p, r)
}
mconn := NewMConnection(conn, chDescs, onError)
p = &Peer{
outbound: outbound,
mconn: mconn,
stopped: 0, stopped: 0,
} }
mconn._peer = p // hacky optimization
return p
} }
func (p *Peer) start(pktRecvQueues map[string]chan *InboundPacket, onPeerError func(*Peer, interface{})) { func (p *Peer) start() {
log.Debug("Starting %v", p)
if atomic.CompareAndSwapUint32(&p.started, 0, 1) { if atomic.CompareAndSwapUint32(&p.started, 0, 1) {
// on connection error log.Debug("Starting %v", p)
onError := func(r interface{}) { p.mconn.Start()
p.stop()
onPeerError(p, r)
}
p.conn.Start(p.channels, onError)
for chName, _ := range p.channels {
chInQueue := pktRecvQueues[chName]
go p.recvHandler(chName, chInQueue)
go p.sendHandler(chName)
}
} }
} }
func (p *Peer) stop() { func (p *Peer) stop() {
if atomic.CompareAndSwapUint32(&p.stopped, 0, 1) { if atomic.CompareAndSwapUint32(&p.stopped, 0, 1) {
log.Debug("Stopping %v", p) log.Debug("Stopping %v", p)
close(p.quit) p.mconn.Stop()
p.conn.Stop()
} }
} }
@ -60,44 +53,21 @@ func (p *Peer) IsOutbound() bool {
} }
func (p *Peer) RemoteAddress() *NetAddress { func (p *Peer) RemoteAddress() *NetAddress {
return p.conn.RemoteAddress() return p.mconn.RemoteAddress()
} }
func (p *Peer) Channel(chName string) *Channel { func (p *Peer) TrySend(chId byte, bytes ByteSlice) bool {
return p.channels[chName]
}
// TrySend returns true if the packet was successfully queued.
// Returning true does not imply that the packet will be sent.
func (p *Peer) TrySend(pkt Packet) bool {
channel := p.Channel(string(pkt.Channel))
sendQueue := channel.sendQueue
if atomic.LoadUint32(&p.stopped) == 1 { if atomic.LoadUint32(&p.stopped) == 1 {
return false return false
} }
return p.mconn.TrySend(chId, bytes)
select {
case sendQueue <- pkt:
log.Debug("SEND %v: %v", p, pkt)
return true
default: // buffer full
log.Debug("FAIL SEND %v: %v", p, pkt)
return false
}
} }
func (p *Peer) Send(pkt Packet) bool { func (p *Peer) Send(chId byte, bytes ByteSlice) bool {
channel := p.Channel(string(pkt.Channel))
sendQueue := channel.sendQueue
if atomic.LoadUint32(&p.stopped) == 1 { if atomic.LoadUint32(&p.stopped) == 1 {
return false return false
} }
return p.mconn.Send(chId, bytes)
sendQueue <- pkt
log.Debug("SEND %v: %v", p, pkt)
return true
} }
func (p *Peer) WriteTo(w io.Writer) (n int64, err error) { func (p *Peer) WriteTo(w io.Writer) (n int64, err error) {
@ -106,104 +76,8 @@ func (p *Peer) WriteTo(w io.Writer) (n int64, err error) {
func (p *Peer) String() string { func (p *Peer) String() string {
if p.outbound { if p.outbound {
return fmt.Sprintf("P(->%v)", p.conn) return fmt.Sprintf("P(->%v)", p.mconn)
} else { } else {
return fmt.Sprintf("P(%v->)", p.conn) return fmt.Sprintf("P(%v->)", p.mconn)
} }
} }
// sendHandler pulls from a channel and pushes to the connection.
// Each channel gets its own sendHandler goroutine;
// Golang's channel implementation handles the scheduling.
func (p *Peer) sendHandler(chName string) {
// log.Debug("%v sendHandler [%v]", p, chName)
channel := p.channels[chName]
sendQueue := channel.sendQueue
FOR_LOOP:
for {
select {
case <-p.quit:
break FOR_LOOP
case pkt := <-sendQueue:
// blocks until the connection is Stop'd,
// which happens when this peer is Stop'd.
p.conn.Send(pkt)
}
}
// log.Debug("%v sendHandler [%v] closed", p, chName)
// Cleanup
}
// recvHandler pulls from a channel and pushes to the given pktRecvQueue.
// Each channel gets its own recvHandler goroutine.
// Many peers have goroutines that push to the same pktRecvQueue.
// Golang's channel implementation handles the scheduling.
func (p *Peer) recvHandler(chName string, pktRecvQueue chan<- *InboundPacket) {
// log.Debug("%v recvHandler [%v]", p, chName)
channel := p.channels[chName]
recvQueue := channel.recvQueue
FOR_LOOP:
for {
select {
case <-p.quit:
break FOR_LOOP
case pkt := <-recvQueue:
// send to pktRecvQueue
inboundPacket := &InboundPacket{
Peer: p,
Time: Time{time.Now()},
Packet: pkt,
}
select {
case <-p.quit:
break FOR_LOOP
case pktRecvQueue <- inboundPacket:
continue
}
}
}
// log.Debug("%v recvHandler [%v] closed", p, chName)
// Cleanup
}
//-----------------------------------------------------------------------------
/* ChannelDescriptor */
type ChannelDescriptor struct {
Name string
SendBufferSize int
RecvBufferSize int
}
/* Channel */
type Channel struct {
name string
recvQueue chan Packet
sendQueue chan Packet
//stats Stats
}
func newChannel(desc ChannelDescriptor) *Channel {
return &Channel{
name: desc.Name,
recvQueue: make(chan Packet, desc.RecvBufferSize),
sendQueue: make(chan Packet, desc.SendBufferSize),
}
}
func (c *Channel) Name() string {
return c.name
}
func (c *Channel) RecvQueue() <-chan Packet {
return c.recvQueue
}
func (c *Channel) SendQueue() chan<- Packet {
return c.sendQueue
}

View File

@ -15,7 +15,7 @@ import (
var pexErrInvalidMessage = errors.New("Invalid PEX message") var pexErrInvalidMessage = errors.New("Invalid PEX message")
const ( const (
pexCh = "PEX" pexCh = byte(0x00)
ensurePeersPeriodSeconds = 30 ensurePeersPeriodSeconds = 30
minNumOutboundPeers = 10 minNumOutboundPeers = 10
maxNumPeers = 50 maxNumPeers = 50
@ -67,13 +67,13 @@ func (pm *PeerManager) Stop() {
func (pm *PeerManager) RequestPEX(peer *Peer) { func (pm *PeerManager) RequestPEX(peer *Peer) {
msg := &pexRequestMessage{} msg := &pexRequestMessage{}
tm := TypedMessage{msgTypeRequest, msg} tm := TypedMessage{msgTypeRequest, msg}
peer.TrySend(NewPacket(pexCh, tm)) peer.TrySend(pexCh, tm.Bytes())
} }
func (pm *PeerManager) SendAddrs(peer *Peer, addrs []*NetAddress) { func (pm *PeerManager) SendAddrs(peer *Peer, addrs []*NetAddress) {
msg := &pexAddrsMessage{Addrs: addrs} msg := &pexAddrsMessage{Addrs: addrs}
tm := TypedMessage{msgTypeAddrs, msg} tm := TypedMessage{msgTypeAddrs, msg}
peer.Send(NewPacket(pexCh, tm)) peer.Send(pexCh, tm.Bytes())
} }
// For new outbound peers, announce our listener addresses if any, // For new outbound peers, announce our listener addresses if any,
@ -170,38 +170,38 @@ func (pm *PeerManager) ensurePeers() {
func (pm *PeerManager) requestHandler() { func (pm *PeerManager) requestHandler() {
for { for {
inPkt := pm.sw.Receive(pexCh) // {Peer, Time, Packet} inBytes, ok := pm.sw.Receive(pexCh) // {Peer, Time, Packet}
if inPkt == nil { if !ok {
// Client has stopped // Client has stopped
break break
} }
// decode message // decode message
msg := decodeMessage(inPkt.Bytes) msg := decodeMessage(inBytes.Bytes)
log.Info("requestHandler received %v", msg) log.Info("requestHandler received %v", msg)
switch msg.(type) { switch msg.(type) {
case *pexRequestMessage: case *pexRequestMessage:
// inPkt.Peer requested some peers. // inBytes.MConn._peer requested some peers.
// TODO: prevent abuse. // TODO: prevent abuse.
addrs := pm.book.GetSelection() addrs := pm.book.GetSelection()
msg := &pexAddrsMessage{Addrs: addrs} msg := &pexAddrsMessage{Addrs: addrs}
tm := TypedMessage{msgTypeRequest, msg} tm := TypedMessage{msgTypeRequest, msg}
queued := inPkt.Peer.TrySend(NewPacket(pexCh, tm)) queued := inBytes.MConn._peer.TrySend(pexCh, tm.Bytes())
if !queued { if !queued {
// ignore // ignore
} }
case *pexAddrsMessage: case *pexAddrsMessage:
// We received some peer addresses from inPkt.Peer. // We received some peer addresses from inBytes.MConn._peer.
// TODO: prevent abuse. // TODO: prevent abuse.
// (We don't want to get spammed with bad peers) // (We don't want to get spammed with bad peers)
srcAddr := inPkt.Peer.RemoteAddress() srcAddr := inBytes.MConn._peer.RemoteAddress()
for _, addr := range msg.(*pexAddrsMessage).Addrs { for _, addr := range msg.(*pexAddrsMessage).Addrs {
pm.book.AddAddress(addr, srcAddr) pm.book.AddAddress(addr, srcAddr)
} }
default: default:
// Ignore unknown message. // Ignore unknown message.
// pm.sw.StopPeerForError(inPkt.Peer, pexErrInvalidMessage) // pm.sw.StopPeerForError(inBytes.MConn._peer, pexErrInvalidMessage)
} }
} }
@ -220,7 +220,7 @@ const (
) )
// TODO: check for unnecessary extra bytes at the end. // TODO: check for unnecessary extra bytes at the end.
func decodeMessage(bz ByteSlice) (msg Message) { func decodeMessage(bz ByteSlice) (msg interface{}) {
// log.Debug("decoding msg bytes: %X", bz) // log.Debug("decoding msg bytes: %X", bz)
switch Byte(bz[0]) { switch Byte(bz[0]) {
case msgTypeRequest: case msgTypeRequest:

View File

@ -2,9 +2,11 @@ package p2p
import ( import (
"errors" "errors"
"net"
"sync/atomic" "sync/atomic"
"time" "time"
. "github.com/tendermint/tendermint/binary"
. "github.com/tendermint/tendermint/common" . "github.com/tendermint/tendermint/common"
) )
@ -12,22 +14,26 @@ import (
All communication amongst peers are multiplexed by "channels". All communication amongst peers are multiplexed by "channels".
(Not the same as Go "channels") (Not the same as Go "channels")
To send a message, encapsulate it into a "Packet" and send it to each peer. To send a message, serialize it into a ByteSlice and send it to each peer.
For best performance, re-use the same immutable ByteSlice to each peer.
You can also use a TypedBytes{} struct for convenience.
You can find all connected and active peers by iterating over ".Peers().List()". You can find all connected and active peers by iterating over ".Peers().List()".
".Broadcast()" is provided for convenience, but by iterating over ".Broadcast()" is provided for convenience, but by iterating over
the peers manually the caller can decide which subset receives a message. the peers manually the caller can decide which subset receives a message.
Inbound messages are received by calling ".Receive()". Inbound messages are received by calling ".Receive()".
The receiver is responsible for decoding the message bytes, which may be preceded
by a single type byte if a TypedBytes{} was used.
*/ */
type Switch struct { type Switch struct {
channels []ChannelDescriptor chDescs []*ChannelDescriptor
pktRecvQueues map[string]chan *InboundPacket recvQueues map[byte]chan InboundBytes
peers *PeerSet peers *PeerSet
dialing *CMap dialing *CMap
listeners *CMap // name -> chan interface{} listeners *CMap // listenerName -> chan interface{}
quit chan struct{} quit chan struct{}
started uint32 started uint32
stopped uint32 stopped uint32
} }
var ( var (
@ -39,22 +45,22 @@ const (
peerDialTimeoutSeconds = 30 peerDialTimeoutSeconds = 30
) )
func NewSwitch(channels []ChannelDescriptor) *Switch { func NewSwitch(chDescs []*ChannelDescriptor) *Switch {
// make pktRecvQueues... s := &Switch{
pktRecvQueues := make(map[string]chan *InboundPacket) chDescs: chDescs,
for _, chDesc := range channels { recvQueues: make(map[byte]chan InboundBytes),
// XXX: buffer this peers: NewPeerSet(),
pktRecvQueues[chDesc.Name] = make(chan *InboundPacket) dialing: NewCMap(),
listeners: NewCMap(),
quit: make(chan struct{}),
stopped: 0,
} }
s := &Switch{ // Create global recvQueues, one per channel.
channels: channels, for _, chDesc := range chDescs {
pktRecvQueues: pktRecvQueues, recvQueue := make(chan InboundBytes, chDesc.RecvQueueCapacity)
peers: NewPeerSet(), chDesc.recvQueue = recvQueue
dialing: NewCMap(), s.recvQueues[chDesc.Id] = recvQueue
listeners: NewCMap(),
quit: make(chan struct{}),
stopped: 0,
} }
return s return s
@ -79,18 +85,12 @@ func (s *Switch) Stop() {
} }
} }
func (s *Switch) AddPeerWithConnection(conn *Connection, outbound bool) (*Peer, error) { func (s *Switch) AddPeerWithConnection(conn net.Conn, outbound bool) (*Peer, error) {
if atomic.LoadUint32(&s.stopped) == 1 { if atomic.LoadUint32(&s.stopped) == 1 {
return nil, ErrSwitchStopped return nil, ErrSwitchStopped
} }
// Create channels for peer peer := newPeer(conn, outbound, s.chDescs, s.StopPeerForError)
channels := map[string]*Channel{}
for _, chDesc := range s.channels {
channels[chDesc.Name] = newChannel(chDesc)
}
peer := newPeer(conn, channels)
peer.outbound = outbound
// Add the peer to .peers // Add the peer to .peers
if s.peers.Add(peer) { if s.peers.Add(peer) {
@ -101,7 +101,7 @@ func (s *Switch) AddPeerWithConnection(conn *Connection, outbound bool) (*Peer,
} }
// Start the peer // Start the peer
go peer.start(s.pktRecvQueues, s.StopPeerForError) go peer.start()
// Notify listeners. // Notify listeners.
s.emit(SwitchEventNewPeer{Peer: peer}) s.emit(SwitchEventNewPeer{Peer: peer})
@ -132,14 +132,16 @@ func (s *Switch) IsDialing(addr *NetAddress) bool {
return s.dialing.Has(addr.String()) return s.dialing.Has(addr.String())
} }
func (s *Switch) Broadcast(pkt Packet) (numSuccess, numFailure int) { func (s *Switch) Broadcast(chId byte, msg Binary) (numSuccess, numFailure int) {
if atomic.LoadUint32(&s.stopped) == 1 { if atomic.LoadUint32(&s.stopped) == 1 {
return return
} }
log.Debug("Broadcast on [%v] len: %v", pkt.Channel, len(pkt.Bytes)) msgBytes := BinaryBytes(msg)
log.Debug("Broadcast on [%X] len: %v", chId, len(msgBytes))
for _, peer := range s.peers.List() { for _, peer := range s.peers.List() {
success := peer.TrySend(pkt) success := peer.TrySend(chId, msgBytes)
log.Debug("Broadcast for peer %v success: %v", peer, success) log.Debug("Broadcast for peer %v success: %v", peer, success)
if success { if success {
numSuccess += 1 numSuccess += 1
@ -164,22 +166,22 @@ func (s *Switch) RemoveEventListener(name string) {
/* /*
Receive blocks on a channel until a message is found. Receive blocks on a channel until a message is found.
*/ */
func (s *Switch) Receive(chName string) *InboundPacket { func (s *Switch) Receive(chId byte) (InboundBytes, bool) {
if atomic.LoadUint32(&s.stopped) == 1 { if atomic.LoadUint32(&s.stopped) == 1 {
return nil return InboundBytes{}, false
} }
q := s.pktRecvQueues[chName] q := s.recvQueues[chId]
if q == nil { if q == nil {
Panicf("Expected pktRecvQueues[%f], found none", chName) Panicf("Expected recvQueues[%X], found none", chId)
} }
select { select {
case <-s.quit: case <-s.quit:
return nil return InboundBytes{}, false
case inPacket := <-q: case inBytes := <-q:
log.Debug("RECV %v", inPacket) log.Debug("RECV %v", inBytes)
return inPacket return inBytes, true
} }
} }

View File

@ -8,14 +8,23 @@ import (
) )
// convenience method for creating two switches connected to each other. // convenience method for creating two switches connected to each other.
func makeSwitchPair(t testing.TB, bufferSize int, chNames []string) (*Switch, *Switch) { func makeSwitchPair(t testing.TB, numChannels int, sendQueueCapacity int, recvBufferSize int, recvQueueCapacity int) (*Switch, *Switch, []*ChannelDescriptor) {
chDescs := []ChannelDescriptor{} // Make numChannels channels starting at byte(0x00)
for _, chName := range chNames { chIds := []byte{}
chDescs = append(chDescs, ChannelDescriptor{ for i := 0; i < numChannels; i++ {
Name: chName, chIds = append(chIds, byte(i))
SendBufferSize: bufferSize, }
RecvBufferSize: bufferSize,
// Make some channel descriptors.
chDescs := []*ChannelDescriptor{}
for _, chId := range chIds {
chDescs = append(chDescs, &ChannelDescriptor{
Id: chId,
SendQueueCapacity: sendQueueCapacity,
RecvBufferSize: recvBufferSize,
RecvQueueCapacity: recvQueueCapacity,
DefaultPriority: 1,
}) })
} }
@ -48,13 +57,11 @@ func makeSwitchPair(t testing.TB, bufferSize int, chNames []string) (*Switch, *S
// Close the server, no longer needed. // Close the server, no longer needed.
l.Stop() l.Stop()
return s1, s2 return s1, s2, chDescs
} }
func TestSwitches(t *testing.T) { func TestSwitches(t *testing.T) {
s1, s2, _ := makeSwitchPair(t, 10, 10, 1024, 10)
channels := []string{"ch1", "ch2", "ch3", "ch4", "ch5", "ch6", "ch7", "ch8", "ch9", "ch0"}
s1, s2 := makeSwitchPair(t, 10, channels)
defer s1.Stop() defer s1.Stop()
defer s2.Stop() defer s2.Stop()
@ -66,26 +73,32 @@ func TestSwitches(t *testing.T) {
t.Errorf("Expected exactly 1 peer in s2, got %v", s2.Peers().Size()) t.Errorf("Expected exactly 1 peer in s2, got %v", s2.Peers().Size())
} }
// Broadcast a message on ch0
s1.Broadcast(byte(0x00), String("channel zero"))
// Broadcast a message on ch1 // Broadcast a message on ch1
s1.Broadcast(NewPacket("ch1", String("channel one"))) s1.Broadcast(byte(0x01), String("channel one"))
// Broadcast a message on ch2 // Broadcast a message on ch2
s1.Broadcast(NewPacket("ch2", String("channel two"))) s1.Broadcast(byte(0x02), String("channel two"))
// Broadcast a message on ch3
s1.Broadcast(NewPacket("ch3", String("channel three")))
// Wait for things to settle... // Wait for things to settle...
time.Sleep(100 * time.Millisecond) time.Sleep(100 * time.Millisecond)
// Receive message from channel 2 and check // Receive message from channel 1 and check
inMsg := s2.Receive("ch2") inMsg, ok := s2.Receive(byte(0x01))
if ReadString(inMsg.Reader()) != "channel two" { if !ok {
t.Errorf("Unexpected received message bytes: %X = [%v]", inMsg.Bytes, ReadString(inMsg.Reader())) t.Errorf("Failed to receive from channel one")
}
if ReadString(inMsg.Bytes.Reader()) != "channel one" {
t.Errorf("Unexpected received message bytes: %X = [%v]", inMsg.Bytes, ReadString(inMsg.Bytes.Reader()))
} }
// Receive message from channel 1 and check // Receive message from channel 0 and check
inMsg = s2.Receive("ch1") inMsg, ok = s2.Receive(byte(0x00))
if ReadString(inMsg.Reader()) != "channel one" { if !ok {
t.Errorf("Unexpected received message bytes: %X = [%v]", inMsg.Bytes, ReadString(inMsg.Reader())) t.Errorf("Failed to receive from channel zero")
}
if ReadString(inMsg.Bytes.Reader()) != "channel zero" {
t.Errorf("Unexpected received message bytes: %X = [%v]", inMsg.Bytes, ReadString(inMsg.Bytes.Reader()))
} }
} }
@ -93,24 +106,24 @@ func BenchmarkSwitches(b *testing.B) {
b.StopTimer() b.StopTimer()
channels := []string{"ch1", "ch2", "ch3", "ch4", "ch5", "ch6", "ch7", "ch8", "ch9", "ch0"} s1, s2, chDescs := makeSwitchPair(b, 10, 10, 1024, 10)
s1, s2 := makeSwitchPair(b, 10, channels)
defer s1.Stop() defer s1.Stop()
defer s2.Stop() defer s2.Stop()
// Create a sink on either channel to just pop off messages. // Create a sink on either channel to just pop off messages.
recvHandler := func(c *Switch, chName string) { recvHandler := func(c *Switch, chId byte) {
for { for {
it := c.Receive(chName) _, ok := c.Receive(chId)
if it == nil { if !ok {
break break
} }
} }
} }
for _, chName := range channels { // Create routines to consume from recvQueues.
go recvHandler(s1, chName) for _, chDesc := range chDescs {
go recvHandler(s2, chName) go recvHandler(s1, chDesc.Id)
go recvHandler(s2, chDesc.Id)
} }
// Allow time for goroutines to boot up // Allow time for goroutines to boot up
@ -121,9 +134,8 @@ func BenchmarkSwitches(b *testing.B) {
// Send random message from one channel to another // Send random message from one channel to another
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
chName := channels[i%len(channels)] chId := chDescs[i%len(chDescs)].Id
pkt := NewPacket(String(chName), ByteSlice("test data")) nS, nF := s1.Broadcast(chId, String("test data"))
nS, nF := s1.Broadcast(pkt)
numSuccess += nS numSuccess += nS
numFailure += nF numFailure += nF
} }