Skip to content

Commit 7a722d7

Browse files
committed
factor out the exponential backoff code, too
1 parent 6a2ea1b commit 7a722d7

File tree

2 files changed

+24
-19
lines changed

2 files changed

+24
-19
lines changed

src/churn-pipe/churn-pipe.ts

Lines changed: 7 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -12,30 +12,14 @@ import ipaddr = require('ipaddr.js');
1212
import logging = require('../logging/logging');
1313
import net = require('../net/net.types');
1414
import PassThrough = require('../transformers/passthrough');
15+
import promises = require('../promises/promises');
1516
import protean = require('../transformers/protean');
1617
import sequence = require('../transformers/byteSequenceShaper');
1718

1819
import Socket = freedom.UdpSocket.Socket;
1920

2021
var log :logging.Log = new logging.Log('churn-pipe');
2122

22-
// Retry an async function with exponential backoff for up to 2 seconds
23-
// before failing.
24-
var retry_ = <T>(func:() => Promise<T>, delayMs?:number) : Promise<T> => {
25-
delayMs = delayMs || 10;
26-
return func().catch((err) => {
27-
delayMs *= 2;
28-
if (delayMs > 2000) {
29-
return Promise.reject(err);
30-
}
31-
return new Promise<T>((F, R) => {
32-
setTimeout(() => {
33-
retry_(func, delayMs).then(F, R);
34-
}, delayMs);
35-
});
36-
});
37-
}
38-
3923
// Maps transformer names to class constructors.
4024
var transformers :{[name:string] : new() => Transformer} = {
4125
'caesar': caesar.CaesarCipher,
@@ -47,6 +31,10 @@ var transformers :{[name:string] : new() => Transformer} = {
4731
'sequenceShaper': sequence.ByteSequenceShaper
4832
};
4933

34+
// Local socket rebinding retry timing (see bindLocal)
35+
const INITIAL_REBIND_INTERVAL_MS = 10;
36+
const MAX_REBIND_INTERVAL_MS = 2000;
37+
5038
interface MirrorSet {
5139
// If true, these mirrors represent a remote endpoint that has been
5240
// explicitly signaled to us.
@@ -198,13 +186,13 @@ class Pipe {
198186
// This retry is needed because the browser releases the UDP port
199187
// asynchronously after we call close() on the RTCPeerConnection, so
200188
// this call to bind() may initially fail, until the port is released.
201-
portPromise = retry_(() => {
189+
portPromise = promises.retryWithExponentialBackoff(() => {
202190
log.debug('%1: trying to bind public endpoint: %2',
203191
this.name_, publicEndpoint);
204192
// TODO: Once https://github.com/freedomjs/freedom/issues/283 is
205193
// fixed, catch here, and only retry on an ALREADY_BOUND error.
206194
return socket.bind(anyInterface, publicEndpoint.port);
207-
}).then(() => {
195+
}, MAX_REBIND_INTERVAL_MS, INITIAL_REBIND_INTERVAL_MS).then(() => {
208196
log.debug('%1: successfully bound public endpoint: %2',
209197
this.name_, publicEndpoint);
210198
socket.on('onData', (recvFromInfo:freedom.UdpSocket.RecvFromInfo) => {

src/promises/promises.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,20 @@ export const retry = <T>(f: () => Promise<T>, maxAttempts: number): Promise<T> =
1212
}
1313
});
1414
};
15+
16+
// Invokes f with exponential backoff between retries, resolving with its result
17+
// on the first success and rejecting on maxAttempts-th failure.
18+
export const retryWithExponentialBackoff = <T>(f: () => Promise<T>,
19+
maxIntervalMs: number, initialIntervalMs: number): Promise<T> => {
20+
return f().catch((e: Error) => {
21+
initialIntervalMs *= 2;
22+
if (initialIntervalMs > maxIntervalMs) {
23+
return Promise.reject(e);
24+
}
25+
return new Promise<T>((F, R) => {
26+
setTimeout(() => {
27+
retryWithExponentialBackoff(f, maxIntervalMs, initialIntervalMs).then(F, R);
28+
}, initialIntervalMs);
29+
});
30+
});
31+
};

0 commit comments

Comments
 (0)