Added rate limiting to IPC logging #4624

This commit is contained in:
Nick Bolton 2015-05-18 18:06:58 +01:00
parent b27b236c07
commit 9f577ca4f3
3 changed files with 74 additions and 19 deletions

View File

@ -32,7 +32,9 @@
enum EIpcLogOutputter { enum EIpcLogOutputter {
kBufferMaxSize = 1000, kBufferMaxSize = 1000,
kMaxSendLines = 100 kMaxSendLines = 100,
kBufferRateWriteLimit = 1000, // writes per kBufferRateTime
kBufferRateTimeLimit = 1 // seconds
}; };
IpcLogOutputter::IpcLogOutputter(IpcServer& ipcServer) : IpcLogOutputter::IpcLogOutputter(IpcServer& ipcServer) :
@ -45,7 +47,11 @@ IpcLogOutputter::IpcLogOutputter(IpcServer& ipcServer) :
m_bufferWaiting(false), m_bufferWaiting(false),
m_bufferMaxSize(kBufferMaxSize), m_bufferMaxSize(kBufferMaxSize),
m_bufferEmptyCond(ARCH->newCondVar()), m_bufferEmptyCond(ARCH->newCondVar()),
m_bufferEmptyMutex(ARCH->newMutex()) m_bufferEmptyMutex(ARCH->newMutex()),
m_bufferRateWriteLimit(kBufferRateWriteLimit),
m_bufferRateTimeLimit(kBufferRateTimeLimit),
m_bufferWriteCount(0),
m_bufferRateStart(ARCH->time())
{ {
m_bufferThread = new Thread(new TMethodJob<IpcLogOutputter>( m_bufferThread = new Thread(new TMethodJob<IpcLogOutputter>(
this, &IpcLogOutputter::bufferThread)); this, &IpcLogOutputter::bufferThread));
@ -120,12 +126,27 @@ void
IpcLogOutputter::appendBuffer(const String& text) IpcLogOutputter::appendBuffer(const String& text)
{ {
ArchMutexLock lock(m_bufferMutex); ArchMutexLock lock(m_bufferMutex);
double elapsed = ARCH->time() - m_bufferRateStart;
if (elapsed < m_bufferRateTimeLimit) {
if (m_bufferWriteCount >= m_bufferRateWriteLimit) {
// discard the log line if we've logged too much.
return;
}
}
else {
m_bufferWriteCount = 0;
m_bufferRateStart = ARCH->time();
}
if (m_buffer.size() >= m_bufferMaxSize) { if (m_buffer.size() >= m_bufferMaxSize) {
// if the queue is exceeds size limit, // if the queue is exceeds size limit,
// throw away the oldest item // throw away the oldest item
m_buffer.pop_front(); m_buffer.pop_front();
} }
m_buffer.push_back(text); m_buffer.push_back(text);
m_bufferWriteCount++;
} }
void void
@ -219,11 +240,14 @@ IpcLogOutputter::bufferMaxSize() const
} }
void void
IpcLogOutputter::close(bool waitForEmpty) IpcLogOutputter::waitForEmpty()
{ {
if (waitForEmpty) {
ARCH->waitCondVar(m_bufferEmptyCond, m_bufferEmptyMutex, -1); ARCH->waitCondVar(m_bufferEmptyCond, m_bufferEmptyMutex, -1);
} }
close(); void
IpcLogOutputter::bufferRateLimit(UInt16 writeLimit, double timeLimit)
{
m_bufferRateWriteLimit = writeLimit;
m_bufferRateTimeLimit = timeLimit;
} }

View File

@ -59,12 +59,17 @@ public:
*/ */
void bufferMaxSize(UInt16 bufferMaxSize); void bufferMaxSize(UInt16 bufferMaxSize);
//! Close the outputter //! Wait for empty buffer
/*! /*!
Close the outputter. If \p waitForEmpty is true, it will wait until Wait on a cond var until the buffer is empty.
the buffer has been sent to the IPC server before closing.
*/ */
void close(bool waitForEmpty); void waitForEmpty();
//! Set the buffer size.
/*!
Set the maximum number of \p writeRate for every \p timeRate in seconds.
*/
void bufferRateLimit(UInt16 writeLimit, double timeLimit);
//@} //@}
@ -103,4 +108,8 @@ private:
UInt16 m_bufferMaxSize; UInt16 m_bufferMaxSize;
ArchCond m_bufferEmptyCond; ArchCond m_bufferEmptyCond;
ArchMutex m_bufferEmptyMutex; ArchMutex m_bufferEmptyMutex;
UInt16 m_bufferRateWriteLimit;
double m_bufferRateTimeLimit;
UInt16 m_bufferWriteCount;
double m_bufferRateStart;
}; };

View File

@ -57,15 +57,37 @@ TEST(IpcLogOutputterTests, write_bufferSizeWrapping)
outputter.bufferMaxSize(2); outputter.bufferMaxSize(2);
// log more lines than the buffer can contain // log more lines than the buffer can contain
for (UInt8 i = 1; i <= 3; i++) { outputter.write(kNOTE, "mock 1");
String s = string::sprintf("mock %d", i); outputter.write(kNOTE, "mock 2");
outputter.write(kNOTE, s.c_str()); outputter.write(kNOTE, "mock 3");
// wait for the buffer to be empty (all lines sent to IPC)
outputter.waitForEmpty();
} }
// close, but wait until the buffer is empty. TEST(IpcLogOutputterTests, write_bufferRateLimit)
outputter.close(true); {
MockIpcServer mockServer;
EXPECT_EQ(true, true); ON_CALL(mockServer, hasClients(_)).WillByDefault(Return(true));
EXPECT_CALL(mockServer, hasClients(_)).Times(2);
EXPECT_CALL(mockServer, send(IpcLogLineMessageEq("mock 1\n"), _)).Times(1);
EXPECT_CALL(mockServer, send(IpcLogLineMessageEq("mock 3\n"), _)).Times(1);
IpcLogOutputter outputter(mockServer);
outputter.bufferRateLimit(1, 0.01); // 5ms
// log 1 more line than the buffer can accept in time limit.
outputter.write(kNOTE, "mock 1");
outputter.write(kNOTE, "mock 2");
outputter.waitForEmpty();
// after waiting the time limit send another to make sure
// we can log after the time limit passes.
ARCH->sleep(0.001); // 10ms
outputter.write(kNOTE, "mock 3");
outputter.waitForEmpty();
} }
#endif // WINAPI_CARBON #endif // WINAPI_CARBON