Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

gbn: set resend timeout dynamically #88

Merged
98 changes: 81 additions & 17 deletions gbn/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,82 @@ package gbn

import "time"

// TimeoutOptions can be used to modify the default timeout values used within
// the TimeoutManager.
type TimeoutOptions func(manager *TimeoutManager)
ViktorTigerstrom marked this conversation as resolved.
Show resolved Hide resolved
ellemouton marked this conversation as resolved.
Show resolved Hide resolved

// WithStaticResendTimeout is used to set a static resend timeout. This is the
// time to wait for ACKs before resending the queue.
func WithStaticResendTimeout(timeout time.Duration) TimeoutOptions {
return func(manager *TimeoutManager) {
manager.useStaticTimeout = true
manager.resendTimeout = timeout
}
}

// WithResendMultiplier is used to set the resend multiplier. This is the
// multiplier we use when dynamically setting the resend timeout, based on how
// long it took for other party to respond.
// Note that when setting the resend timeout manually with the
// WithStaticResendTimeout option, this option will have no effect.
// Note that the passed multiplier must be greater than zero or this option will
// have no effect.
func WithResendMultiplier(multiplier int) TimeoutOptions {
return func(manager *TimeoutManager) {
if multiplier > 0 {
manager.resendMultiplier = multiplier
}
}
}

// WithTimeoutUpdateFrequency is used to set the frequency of how many
// corresponding responses we need to receive until updating the resend timeout.
// Note that when setting the resend timeout manually with the WithTimeout
// option, this option will have no effect.
// Also note that the passed frequency must be greater than zero or this option
// will have no effect.
func WithTimeoutUpdateFrequency(frequency int) TimeoutOptions {
return func(manager *TimeoutManager) {
if frequency > 0 {
manager.timeoutUpdateFrequency = frequency
}
}
}

// WithHandshakeTimeout is used to set the timeout used during the handshake.
// If the timeout is reached without response from the peer then the handshake
// will be aborted and restarted.
func WithHandshakeTimeout(timeout time.Duration) TimeoutOptions {
return func(manager *TimeoutManager) {
manager.handshakeTimeout = timeout
}
}

// WithKeepalivePing is used to send a ping packet if no packets have been
// received from the other side for the given duration. This helps keep the
// connection alive and also ensures that the connection is closed if the
// other side does not respond to the ping in a timely manner. After the ping
// the connection will be closed if the other side does not respond within
// time duration.
func WithKeepalivePing(ping, pong time.Duration) TimeoutOptions {
return func(manager *TimeoutManager) {
manager.pingTime = ping
manager.pongTime = pong
}
}

// WithBoostPercent is used to set the boost percent that the timeout manager
// will use to boost the resend timeout & handshake timeout every time a resend
// is required due to not receiving a response within the current timeout.
func WithBoostPercent(boostPercent float32) TimeoutOptions {
ViktorTigerstrom marked this conversation as resolved.
Show resolved Hide resolved
return func(manager *TimeoutManager) {
if boostPercent > 0 {
manager.resendBoostPercent = boostPercent
manager.handshakeBoostPercent = boostPercent
}
}
}

// config holds the configuration values for an instance of GoBackNConn.
type config struct {
// n is the window size. The sender can send a maximum of n packets
Expand All @@ -26,10 +102,6 @@ type config struct {
// between packets.
maxChunkSize int

// resendTimeout is the duration that will be waited before resending
// the packets in the current queue.
resendTimeout time.Duration

// recvFromStream is the function that will be used to acquire the next
// available packet.
recvFromStream recvBytesFunc
Expand All @@ -42,25 +114,17 @@ type config struct {
// been received and processed.
onFIN func()

// handshakeTimeout is the time after which the server or client
// will abort and restart the handshake if the expected response is
// not received from the peer.
handshakeTimeout time.Duration

pingTime time.Duration
pongTime time.Duration
timeoutOptions []TimeoutOptions
}

// newConfig constructs a new config struct.
func newConfig(sendFunc sendBytesFunc, recvFunc recvBytesFunc,
n uint8) *config {

return &config{
n: n,
s: n + 1,
recvFromStream: recvFunc,
sendToStream: sendFunc,
resendTimeout: defaultResendTimeout,
handshakeTimeout: defaultHandshakeTimeout,
n: n,
s: n + 1,
recvFromStream: recvFunc,
sendToStream: sendFunc,
}
}
13 changes: 12 additions & 1 deletion gbn/gbn_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ func (g *GoBackNConn) clientHandshake() error {
var (
resp Message
respSYN *PacketSYN
resent bool
)
handshake:
for {
Expand All @@ -115,6 +116,9 @@ handshake:
return err
}

// Notify the timeout manager that we sent a SYN.
g.timeoutManager.Sent(msg, resent)

for {
// Wait for SYN
g.log.Debugf("Waiting for SYN")
Expand All @@ -128,11 +132,14 @@ handshake:
default:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice!

i'd just add a small note to the commit message saying that this is basically just a refactor with no behaviour changes since the timeout mgr is not set being used for dynamic timeouts. Could even, just to emphasis this no-behaviour change point, add the "WithStaticTimeout" option on initialisation (to be removed in the upcoming commits of course)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! The commit messages & order have been updated

}

timeout := g.timeoutManager.GetHandshakeTimeout()

var b []byte
select {
case <-time.After(g.cfg.handshakeTimeout):
case <-time.After(timeout):
g.log.Debugf("SYN resendTimeout. Resending " +
"SYN.")
resent = true

continue handshake
case <-g.quit:
Expand Down Expand Up @@ -171,6 +178,10 @@ handshake:
return io.EOF
}

// Notify the timeout manager we've received the SYN response from the
// counterparty.
g.timeoutManager.Received(resp)

// Send SYNACK
g.log.Debugf("Sending SYNACK")
synack, err := new(PacketSYNACK).Serialize()
Expand Down
Loading
Loading