Solution attempt for timing bugs in write_bufferRateLimit

It's probably better to wait until the buffer is sent, rather than
waiting until its empty. To test the output it has to be sent, but
because of timing, it may be emptied at any point.
This commit is contained in:
Nick Bolton 2015-05-20 15:51:07 +01:00
parent f1af62927e
commit 9636af61d6
4 changed files with 29 additions and 29 deletions

View File

@ -46,8 +46,6 @@ IpcLogOutputter::IpcLogOutputter(IpcServer& ipcServer) :
m_notifyMutex(ARCH->newMutex()), m_notifyMutex(ARCH->newMutex()),
m_bufferWaiting(false), m_bufferWaiting(false),
m_bufferMaxSize(kBufferMaxSize), m_bufferMaxSize(kBufferMaxSize),
m_bufferEmptyCond(ARCH->newCondVar()),
m_bufferEmptyMutex(ARCH->newMutex()),
m_bufferRateWriteLimit(kBufferRateWriteLimit), m_bufferRateWriteLimit(kBufferRateWriteLimit),
m_bufferRateTimeLimit(kBufferRateTimeLimit), m_bufferRateTimeLimit(kBufferRateTimeLimit),
m_bufferWriteCount(0), m_bufferWriteCount(0),
@ -66,13 +64,6 @@ IpcLogOutputter::~IpcLogOutputter()
ARCH->closeCondVar(m_notifyCond); ARCH->closeCondVar(m_notifyCond);
ARCH->closeMutex(m_notifyMutex); ARCH->closeMutex(m_notifyMutex);
ARCH->closeCondVar(m_bufferEmptyCond);
#ifndef WINAPI_CARBON
// HACK: assert fails on mac debug, can't see why.
ARCH->closeMutex(m_bufferEmptyMutex);
#endif // WINAPI_CARBON
} }
void void
@ -172,11 +163,6 @@ IpcLogOutputter::bufferThread(void*)
break; break;
} }
if (m_buffer.empty()) {
ArchMutexLock lock(m_bufferEmptyMutex);
ARCH->broadcastCondVar(m_bufferEmptyCond);
}
m_bufferWaiting = true; m_bufferWaiting = true;
ARCH->waitCondVar(m_notifyCond, m_notifyMutex, -1); ARCH->waitCondVar(m_notifyCond, m_notifyMutex, -1);
m_bufferWaiting = false; m_bufferWaiting = false;
@ -239,12 +225,6 @@ IpcLogOutputter::bufferMaxSize() const
return m_bufferMaxSize; return m_bufferMaxSize;
} }
void
IpcLogOutputter::waitForEmpty()
{
ARCH->waitCondVar(m_bufferEmptyCond, m_bufferEmptyMutex, -1);
}
void void
IpcLogOutputter::bufferRateLimit(UInt16 writeLimit, double timeLimit) IpcLogOutputter::bufferRateLimit(UInt16 writeLimit, double timeLimit)
{ {

View File

@ -106,8 +106,6 @@ private:
IArchMultithread::ThreadID IArchMultithread::ThreadID
m_bufferThreadId; m_bufferThreadId;
UInt16 m_bufferMaxSize; UInt16 m_bufferMaxSize;
ArchCond m_bufferEmptyCond;
ArchMutex m_bufferEmptyMutex;
UInt16 m_bufferRateWriteLimit; UInt16 m_bufferRateWriteLimit;
double m_bufferRateTimeLimit; double m_bufferRateTimeLimit;
UInt16 m_bufferWriteCount; UInt16 m_bufferWriteCount;

View File

@ -19,17 +19,40 @@
#include "ipc/IpcServer.h" #include "ipc/IpcServer.h"
#include "ipc/IpcMessage.h" #include "ipc/IpcMessage.h"
#include "arch/Arch.h"
#include "test/global/gmock.h" #include "test/global/gmock.h"
using ::testing::_;
using ::testing::Invoke;
class IEventQueue; class IEventQueue;
class MockIpcServer : public IpcServer class MockIpcServer : public IpcServer
{ {
public: public:
MockIpcServer() { } MockIpcServer() :
m_sendCond(ARCH->newCondVar()),
m_sendMutex(ARCH->newMutex()) { }
MOCK_METHOD0(listen, void()); MOCK_METHOD0(listen, void());
MOCK_METHOD2(send, void(const IpcMessage&, EIpcClientType)); MOCK_METHOD2(send, void(const IpcMessage&, EIpcClientType));
MOCK_CONST_METHOD1(hasClients, bool(EIpcClientType)); MOCK_CONST_METHOD1(hasClients, bool(EIpcClientType));
void delegateToFake() {
ON_CALL(*this, send(_, _)).WillByDefault(Invoke(this, &MockIpcServer::mockSend));
}
void waitForSend() {
ARCH->waitCondVar(m_sendCond, m_sendMutex, -1);
}
private:
void mockSend(const IpcMessage&, EIpcClientType) {
ArchMutexLock lock(m_sendMutex);
ARCH->broadcastCondVar(m_sendCond);
}
ArchCond m_sendCond;
ArchMutex m_sendMutex;
}; };

View File

@ -48,6 +48,7 @@ inline const Matcher<const IpcMessage&> IpcLogLineMessageEq(const String& s) {
TEST(IpcLogOutputterTests, write_bufferSizeWrapping) TEST(IpcLogOutputterTests, write_bufferSizeWrapping)
{ {
MockIpcServer mockServer; MockIpcServer mockServer;
mockServer.delegateToFake();
ON_CALL(mockServer, hasClients(_)).WillByDefault(Return(true)); ON_CALL(mockServer, hasClients(_)).WillByDefault(Return(true));
@ -61,14 +62,13 @@ TEST(IpcLogOutputterTests, write_bufferSizeWrapping)
outputter.write(kNOTE, "mock 1"); outputter.write(kNOTE, "mock 1");
outputter.write(kNOTE, "mock 2"); outputter.write(kNOTE, "mock 2");
outputter.write(kNOTE, "mock 3"); outputter.write(kNOTE, "mock 3");
mockServer.waitForSend();
// wait for the buffer to be empty (all lines sent to IPC)
outputter.waitForEmpty();
} }
TEST(IpcLogOutputterTests, write_bufferRateLimit) TEST(IpcLogOutputterTests, write_bufferRateLimit)
{ {
MockIpcServer mockServer; MockIpcServer mockServer;
mockServer.delegateToFake();
ON_CALL(mockServer, hasClients(_)).WillByDefault(Return(true)); ON_CALL(mockServer, hasClients(_)).WillByDefault(Return(true));
@ -82,14 +82,13 @@ TEST(IpcLogOutputterTests, write_bufferRateLimit)
// log 1 more line than the buffer can accept in time limit. // log 1 more line than the buffer can accept in time limit.
outputter.write(kNOTE, "mock 1"); outputter.write(kNOTE, "mock 1");
outputter.write(kNOTE, "mock 2"); outputter.write(kNOTE, "mock 2");
outputter.waitForEmpty(); mockServer.waitForSend();
// after waiting the time limit send another to make sure // after waiting the time limit send another to make sure
// we can log after the time limit passes. // we can log after the time limit passes.
ARCH->sleep(0.01); // 10ms
outputter.write(kNOTE, "mock 3"); outputter.write(kNOTE, "mock 3");
outputter.write(kNOTE, "mock 4"); outputter.write(kNOTE, "mock 4");
outputter.waitForEmpty(); mockServer.waitForSend();
} }
#endif // WINAPI_MSWINDOWS #endif // WINAPI_MSWINDOWS