summaryrefslogtreecommitdiff
path: root/lib/server/TcpNice.h
blob: 4381df42dcceae861f5c65a57935ced95ae2d643 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
// --------------------------------------------------------------------------
//
// File
//		Name:    TcpNice.h
//		Purpose: Calculator for adaptive TCP window sizing to support
//               low-priority background flows using the stochastic
//               algorithm, as described in
//               http://www.thlab.net/~lmassoul/p18-key.pdf
//		Created: 11/02/2012
//
// --------------------------------------------------------------------------

#ifndef TCPNICE__H
#define TCPNICE__H

#include <memory>

#include "SocketStream.h"
#include "Timer.h"

// --------------------------------------------------------------------------
//
// Class
//		Name:    TcpNice
//		Purpose: Calculator for adaptive TCP window sizing.
//		Created: 11/02/2012
//
// --------------------------------------------------------------------------

class TcpNice
{
public:
	TcpNice();
	int GetNextWindowSize(int bytesChange, box_time_t timeElapsed,
		int rttEstimateMicros);
	
private:
	/**
	 * The previous (last recommended) window size is one of the parameters
	 * used to calculate the next window size.
	 */
	int mLastWindowSize;
	
	/**
	 * Controls the speed of adaptation and the variance (random variation)
	 * of the stable state in response to noise. The paper suggests using
	 * 1.0 (100%).
	 */
	int mGammaPercent;
	
	/**
	 * Controls the extent to which background flows are allowed to affect
	 * foreground flows. Its detailed meaning is not explained in the paper,
	 * but its units are bytes, and I think it controls how aggressive we
	 * are at increasing window size, potentially at the expense of other
	 * competing flows.
	 */
	int mAlphaStar;
	
	/**
	 * Controls the speed of adaptation of the exponential weighted moving
	 * average (EWMA) estimate of the bandwidth available to this flow.
	 * The paper uses 10%.
	 */
	int mDeltaPercent;
	
	/**
	 * The stochastic algorithm in the paper uses the rate estimate for the
	 * last-but-one period (rbHat[n-2]) to calculate the next window size.
	 * So we keep both the last (in rateEstimateMovingAverage[1]) and the
	 * last-but-one (in rateEstimateMovingAverage[0]) values.
	 */
	int mRateEstimateMovingAverage[2];	
};

// --------------------------------------------------------------------------
//
// Class
//		Name:    NiceSocketStream
//		Purpose: Wrapper around a SocketStream to limit sending rate to
//               avoid interference with higher-priority flows.
//		Created: 11/02/2012
//
// --------------------------------------------------------------------------

class NiceSocketStream : public SocketStream
{
private:
	std::auto_ptr<SocketStream> mapSocket;
	TcpNice mTcpNice;
	std::auto_ptr<Timer> mapTimer;
	int mBytesWrittenThisPeriod;
	box_time_t mPeriodStartTime;
	
	/**
	 * The control interval T from the paper, in milliseconds. The available
	 * bandwidth is estimated over this period, and the window size is
	 * recalculated at the end of each period. It should be long enough for
	 * TCP to adapt to a change in window size; perhaps 10-100 RTTs. One
	 * second (1000) is probably a good first approximation in many cases.
	 */
	int mTimeIntervalMillis;

	/**
	 * Because our data use is bursty, and tcp nice works on the assumption
	 * that we've always got data to send, we should only enable nice mode
	 * when we're doing a bulk upload, and disable it afterwards.
	 */
	bool mEnabled;
	
	void StartTimer()
	{
		mapTimer.reset(new Timer(mTimeIntervalMillis, "NiceSocketStream"));
	}

	void StopTimer()
	{
		mapTimer.reset();
	}

public:
	NiceSocketStream(std::auto_ptr<SocketStream> apSocket);
	virtual ~NiceSocketStream()
	{
		// Be nice about closing the socket
		mapSocket->Shutdown();
		mapSocket->Close();
	}

	// This is the only magic
	virtual void Write(const void *pBuffer, int NBytes);

	// Everything else is delegated to the sink
	virtual int Read(void *pBuffer, int NBytes,
		int Timeout = IOStream::TimeOutInfinite)
	{
		return mapSocket->Read(pBuffer, NBytes, Timeout);
	}
	virtual pos_type BytesLeftToRead()
	{
		return mapSocket->BytesLeftToRead();
	}
	virtual pos_type GetPosition() const
	{
		return mapSocket->GetPosition();
	}
	virtual void Seek(IOStream::pos_type Offset, int SeekType)
	{
		mapSocket->Seek(Offset, SeekType);
	}
	virtual void Flush(int Timeout = IOStream::TimeOutInfinite)
	{
		mapSocket->Flush(Timeout);
	}
	virtual void Close()
	{
		mapSocket->Close();
	}
	virtual bool StreamDataLeft()
	{
		return mapSocket->StreamDataLeft();
	}
	virtual bool StreamClosed()
	{
		return mapSocket->StreamClosed();
	}
	virtual void SetEnabled(bool enabled);

	off_t GetBytesRead() const { return mapSocket->GetBytesRead(); }
	off_t GetBytesWritten() const { return mapSocket->GetBytesWritten(); }
	void ResetCounters() { mapSocket->ResetCounters(); }

private:
	NiceSocketStream(const NiceSocketStream &rToCopy) 
	{ /* do not call */ }
};

#endif // TCPNICE__H