Steel/pkg/network/resend_map.go

85 lines
2.5 KiB
Go
Raw Normal View History

2023-12-19 20:49:11 +01:00
package network
2023-12-18 21:06:17 +01:00
import (
2023-12-19 20:49:11 +01:00
"cimeyclust.com/steel/pkg/network/packets"
2023-12-18 21:06:17 +01:00
"cimeyclust.com/steel/pkg/utils"
"time"
)
// resendMap is a map of packets, used to recover datagrams if the other end of
// the connection ended up not having them.
type resendMap struct {
unacknowledged map[utils.Uint24]resendRecord
delays map[time.Time]time.Duration
}
// resendRecord represents a single packet with a timestamp from when it was
// initially sent. It may be either acknowledged or NACKed by the other end.
type resendRecord struct {
pk *packets.Packet
timestamp time.Time
}
// newRecoveryQueue returns a new initialised recovery queue.
func newRecoveryQueue() *resendMap {
return &resendMap{
delays: make(map[time.Time]time.Duration),
unacknowledged: make(map[utils.Uint24]resendRecord),
}
}
// add puts a packet at the index passed and records the current time.
func (m *resendMap) add(index utils.Uint24, pk *packets.Packet) {
m.unacknowledged[index] = resendRecord{pk: pk, timestamp: time.Now()}
}
// acknowledge marks a packet with the index passed as acknowledged. The packet
// is removed from the resendMap and returned if found.
func (m *resendMap) acknowledge(index utils.Uint24) (*packets.Packet, bool) {
return m.remove(index, 1)
}
// retransmit looks up a packet with an index from the resendMap so that it may
// be resent.
func (m *resendMap) retransmit(index utils.Uint24) (*packets.Packet, bool) {
return m.remove(index, 2)
}
// remove deletes an index from the resendMap and adds the time since the
// packet was originally sent multiplied by mul to the delays slice.
func (m *resendMap) remove(index utils.Uint24, mul int) (*packets.Packet, bool) {
record, ok := m.unacknowledged[index]
if !ok {
return nil, false
}
delete(m.unacknowledged, index)
now := time.Now()
m.delays[now] = now.Sub(record.timestamp) * time.Duration(mul)
return record.pk, true
}
// rtt returns the average round trip time between the putting of the value
// into the recovery queue and the taking out of it again. It is measured over
// the last delayRecordCount values add in.
func (m *resendMap) rtt() time.Duration {
const averageDuration = time.Second * 5
var (
total, records time.Duration
now = time.Now()
)
for t, rtt := range m.delays {
if now.Sub(t) > averageDuration {
delete(m.delays, t)
continue
}
total += rtt
records++
}
if records == 0 {
// No records yet, generally should not happen. Just return a reasonable amount of time.
return time.Millisecond * 50
}
return total / records
}