summaryrefslogtreecommitdiff
path: root/lib/server/TcpNice.h
diff options
context:
space:
mode:
Diffstat (limited to 'lib/server/TcpNice.h')
-rw-r--r--lib/server/TcpNice.h174
1 files changed, 174 insertions, 0 deletions
diff --git a/lib/server/TcpNice.h b/lib/server/TcpNice.h
new file mode 100644
index 00000000..e2027749
--- /dev/null
+++ b/lib/server/TcpNice.h
@@ -0,0 +1,174 @@
+// --------------------------------------------------------------------------
+//
+// 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 IOStream
+{
+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);
+
+private:
+ NiceSocketStream(const NiceSocketStream &rToCopy)
+ { /* do not call */ }
+};
+
+#endif // TCPNICE__H