Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 42 additions & 8 deletions congestion.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,9 @@ type CongestionControllerIetf struct {
largestAckedPacket uint64
// largestRtt time.Duration
smoothedRtt time.Duration
rttVar float32
rttVar time.Duration
smoothedRttTcp time.Duration
rttVarTcp time.Duration
reorderingThreshold int
timeReorderingFraction float32
lossTime time.Time
Expand Down Expand Up @@ -115,7 +117,7 @@ func (cc *CongestionControllerIetf) onPacketSent(pn uint64, isAckOnly bool, sent


// acks is received to be a sorted list, where the largest packet numbers are at the beginning
func(cc *CongestionControllerIetf) onAckReceived(acks ackRanges, delay time.Duration){
func(cc *CongestionControllerIetf) onAckReceived(acks ackRanges, ackDelay time.Duration){

// keep track of largest packet acked overall
if acks[0].lastPacket > cc.largestAckedPacket {
Expand All @@ -125,11 +127,14 @@ func(cc *CongestionControllerIetf) onAckReceived(acks ackRanges, delay time.Dura
// If the largest acked is newly acked update rtt
_, present := cc.sentPackets[acks[0].lastPacket]
if present {
//TODO([email protected]) RTT stuff
//largestRtt = time.Now - cc.sentPackets[acks[0].lastPacket].txTime
//if (latestRtt > delay){
// latestRtt -= delay
// cc.updateRtt(latestRtt)
latestRtt := time.Since(cc.sentPackets[acks[0].lastPacket].txTime)
cc.conn.log(logTypeCongestion, "latestRtt: %v, ackDelay: %v", latestRtt, ackDelay)
cc.updateRttTcp(latestRtt)

if (latestRtt > ackDelay){
latestRtt -= ackDelay
}
cc.updateRtt(latestRtt)
}

// find and proccess newly acked packets
Expand All @@ -154,9 +159,36 @@ func (cc *CongestionControllerIetf) setLostPacketHandler(handler func(pn uint64)


func(cc *CongestionControllerIetf) updateRtt(latestRtt time.Duration){
//TODO([email protected])
if (cc.smoothedRtt == 0){
cc.smoothedRtt = latestRtt
cc.rttVar = time.Duration(int64(latestRtt) / 2)
} else {
rttDelta := cc.smoothedRtt - latestRtt;
if rttDelta < 0 {
rttDelta = -rttDelta
}
cc.rttVar = time.Duration(int64(cc.rttVar) * 3/4 + int64(rttDelta) * 1/4)
cc.smoothedRtt = time.Duration(int64(cc.smoothedRtt) * 7/8 + int64(latestRtt) * 1/8)
}
cc.conn.log(logTypeCongestion, "New RTT estimate: %v, variance: %v", cc.smoothedRtt, cc.rttVar)
}

func(cc *CongestionControllerIetf) updateRttTcp(latestRtt time.Duration){
if (cc.smoothedRttTcp == 0){
cc.smoothedRttTcp = latestRtt
cc.rttVarTcp = time.Duration(int64(latestRtt) / 2)
} else {
rttDelta := cc.smoothedRttTcp - latestRtt;
if rttDelta < 0 {
rttDelta = -rttDelta
}
cc.rttVarTcp = time.Duration(int64(cc.rttVarTcp) * 3/4 + int64(rttDelta) * 3/4)
cc.smoothedRttTcp = time.Duration(int64(cc.smoothedRttTcp) * 7/8 + int64(latestRtt) * 1/8)
}
cc.conn.log(logTypeCongestion, "New RTT(TCP) estimate: %v, variance: %v", cc.smoothedRttTcp, cc.rttVarTcp)
}


func(cc *CongestionControllerIetf) onPacketAcked(pn uint64){
cc.onPacketAckedCC(pn)
//TODO([email protected]) some RTO stuff here
Expand Down Expand Up @@ -269,6 +301,8 @@ func newCongestionControllerIetf(conn *Connection) *CongestionControllerIetf{
0, // largestAckedPacket
0, // smoothedRtt
0, // rttVar
0, // smoothedRttTcp
0, // rttVarTcp
kReorderingThreshold, // reorderingThreshold
math.MaxFloat32, // timeReorderingFraction
time.Unix(0,0), // lossTime
Expand Down
8 changes: 6 additions & 2 deletions connection.go
Original file line number Diff line number Diff line change
Expand Up @@ -593,7 +593,7 @@ func (c *Connection) sendOnStream(streamId uint32, data []byte) error {

func (c *Connection) makeAckFrame(acks ackRanges, left int) (*frame, int, error) {
c.log(logTypeConnection, "Making ack frame, room=%d", left)
af, rangesSent, err := newAckFrame(acks, left)
af, rangesSent, err := newAckFrame(c.recvd, acks, left)
if err != nil {
c.log(logTypeConnection, "Couldn't prepare ACK frame %v", err)
return nil, 0, err
Expand Down Expand Up @@ -1560,6 +1560,10 @@ func (c *Connection) processAckFrame(f *ackFrame, protected bool) error {
end := f.LargestAcknowledged
start := end - f.AckBlockLength

// Decode ACK Delay
ackDelayMicros := QuicFloat16(f.AckDelay).Float32()
ackDelay := time.Duration(ackDelayMicros * 1e3)

// Process the First ACK Block
c.log(logTypeAck, "%s: processing ACK range %x-%x", c.label(), start, end)
c.processAckRange(start, end, protected)
Expand Down Expand Up @@ -1593,7 +1597,7 @@ func (c *Connection) processAckFrame(f *ackFrame, protected bool) error {
receivedAcks = append(receivedAcks, ackRange{end, end - start + 1})
}

c.congestion.onAckReceived(receivedAcks, 0)
c.congestion.onAckReceived(receivedAcks, ackDelay)

return nil
}
Expand Down
11 changes: 8 additions & 3 deletions frame.go
Original file line number Diff line number Diff line change
Expand Up @@ -392,8 +392,8 @@ func (f ackFrame) AckBlockSection__length() uintptr {
return uintptr(f.NumBlocks) * (1 + f.AckBlockLength__length())
}

func newAckFrame(rs ackRanges, left int) (*frame, int, error) {
if left < 16 {
func newAckFrame(recvd *recvdPackets, rs ackRanges, left int) (*frame, int, error) {
if left < kAckHeaderLength {
return nil, 0, nil
}
logf(logTypeFrame, "Making ACK frame %v", rs)
Expand All @@ -411,8 +411,13 @@ func newAckFrame(rs ackRanges, left int) (*frame, int, error) {
f.LargestAcknowledged = rs[0].lastPacket
f.AckBlockLength = rs[0].count - 1
last := f.LargestAcknowledged - f.AckBlockLength
f.AckDelay = 0

largestAckData, ok := recvd.packets[f.LargestAcknowledged]
/* Should always be there. Packets only get removed after being set to ack2,
* which means we should not be acking it again */
assert(ok)
ackDelayMicros := float32(time.Since(largestAckData.t).Nanoseconds())/1e3
f.AckDelay = uint16(NewQuicFloat16(ackDelayMicros))
addedRanges := 1

// SECOND, add the remaining ACK blocks that fit and that we have
Expand Down
12 changes: 10 additions & 2 deletions frame_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@ import (
func TestAckFrameOneRange(t *testing.T) {
ar := []ackRange{{0xdeadbeef, 2}}

f, _, err := newAckFrame(ar, 21)
recvd := newRecvdPackets(logf)
recvd.init(ar[0].lastPacket)
recvd.packetSetReceived(ar[0].lastPacket, false, false)

f, _, err := newAckFrame(recvd, ar, 21)
assertNotError(t, err, "Couldn't make ack frame")

err = f.encode()
Expand All @@ -25,7 +29,11 @@ func TestAckFrameOneRange(t *testing.T) {
func TestAckFrameTwoRanges(t *testing.T) {
ar := []ackRange{{0xdeadbeef, 2}, {0xdeadbee0, 1}}

f, _, err := newAckFrame(ar, 26)
recvd := newRecvdPackets(logf)
recvd.init(ar[0].lastPacket)
recvd.packetSetReceived(ar[0].lastPacket, false, false)

f, _, err := newAckFrame(recvd, ar, 26)
assertNotError(t, err, "Couldn't make ack frame")

err = f.encode()
Expand Down
51 changes: 51 additions & 0 deletions quicfloat.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* Based of the "half" package. See https://github.com/h2so5/half
*/

package minq

import "math"

//TODO([email protected]) At the moment this is just a IEEE 754 float16

// A QuicFloat16 represents a 16-bit floating point number.
type QuicFloat16 uint16

// NewQuicFloat16 allocates and returns a new Float16 set to f.
func NewQuicFloat16(f float32) QuicFloat16 {
i := math.Float32bits(f)
sign := uint16((i >> 31) & 0x1)
exp := (i >> 23) & 0xff
exp16 := int16(exp) - 127 + 15
frac := uint16(i>>13) & 0x3ff
if exp == 0 {
exp16 = 0
} else if exp == 0xff {
exp16 = 0x1f
} else {
if exp16 > 0x1e {
exp16 = 0x1f
frac = 0
} else if exp16 < 0x01 {
exp16 = 0
frac = 0
}
}
f16 := (sign << 15) | uint16(exp16<<10) | frac
return QuicFloat16(f16)
}

// Float32 returns the float32 representation of f.
func (f QuicFloat16) Float32() float32 {
sign := uint32((f >> 15) & 0x1)
exp := (f >> 10) & 0x1f
exp32 := uint32(exp) + 127 - 15
if exp == 0 {
exp32 = 0
} else if exp == 0x1f {
exp32 = 0xff
}
frac := uint32(f & 0x3ff)
i := (sign << 31) | (exp32 << 23) | (frac << 13)
return math.Float32frombits(i)
}
46 changes: 46 additions & 0 deletions quicfloat_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* Based of the "half" package. See https://github.com/h2so5/half
*/

package minq

import (
"math"
"testing"
)

func getFloatTable() map[QuicFloat16]float32 {
table := map[QuicFloat16]float32{
0x3c00: 1,
0x4000: 2,
0xc000: -2,
0x7bfe: 65472,
0x7bff: 65504,
0xfbff: -65504,
0x0000: 0,
0x8000: float32(math.Copysign(0, -1)),
0x7c00: float32(math.Inf(1)),
0xfc00: float32(math.Inf(-1)),
0x5b8f: 241.875,
0x48c8: 9.5625,
}
return table
}

func TestFloat32(t *testing.T) {
for k, v := range getFloatTable() {
f := k.Float32()
if f != v {
t.Errorf("ToFloat32(%d) = %f, want %f.", k, f, v)
}
}
}

func TestNewQuicFloat16(t *testing.T) {
for k, v := range getFloatTable() {
i := NewQuicFloat16(v)
if i != k {
t.Errorf("FromFloat32(%f) = %d, want %d.", v, i, k)
}
}
}