This commit is contained in:
2021-02-02 10:50:48 +03:00
parent 8eb6b7be74
commit a80622c956

View File

@@ -5,196 +5,199 @@
class ConditionVariable : public ::testing::Test, public TestUtil { class ConditionVariable : public ::testing::Test, public TestUtil {
public: public:
~ConditionVariable() {
delete variable;
}
PIMutex m; PIMutex m;
PIConditionVariable* variable; PIConditionVariable* variable;
protected: protected:
void SetUp() override { void SetUp() override {
variable = new PIConditionVariable(); variable = new PIConditionVariable();
adapterFunctionDefault = [&](){ adapterFunctionDefault = [&](){
m.lock(); m.lock();
variable->wait(m); variable->wait(m);
m.unlock(); m.unlock();
}; };
} }
}; };
TEST_F(ConditionVariable, wait_is_block) { TEST_F(ConditionVariable, wait_is_block) {
createThread(); createThread();
ASSERT_FALSE(thread->waitForFinish(WAIT_THREAD_TIME_MS)); ASSERT_FALSE(thread->waitForFinish(WAIT_THREAD_TIME_MS));
} }
TEST_F(ConditionVariable, wait_is_block_when_notifyOne_before_wait) { TEST_F(ConditionVariable, wait_is_block_when_notifyOne_before_wait) {
variable->notifyOne(); variable->notifyOne();
createThread(); createThread();
ASSERT_FALSE(thread->waitForFinish(WAIT_THREAD_TIME_MS)); ASSERT_FALSE(thread->waitForFinish(WAIT_THREAD_TIME_MS));
} }
TEST_F(ConditionVariable, wait_is_block_when_notifyAll_before_wait) { TEST_F(ConditionVariable, wait_is_block_when_notifyAll_before_wait) {
variable->notifyAll(); variable->notifyAll();
createThread(); createThread();
ASSERT_FALSE(thread->waitForFinish(WAIT_THREAD_TIME_MS)); ASSERT_FALSE(thread->waitForFinish(WAIT_THREAD_TIME_MS));
} }
TEST_F(ConditionVariable, wait_is_unblock_when_notifyOne_after_wait) { TEST_F(ConditionVariable, wait_is_unblock_when_notifyOne_after_wait) {
createThread(); createThread();
variable->notifyOne(); variable->notifyOne();
ASSERT_TRUE(thread->waitForFinish(WAIT_THREAD_TIME_MS)); ASSERT_TRUE(thread->waitForFinish(WAIT_THREAD_TIME_MS));
} }
TEST_F(ConditionVariable, wait_is_unblock_when_notifyAll_after_wait) { TEST_F(ConditionVariable, wait_is_unblock_when_notifyAll_after_wait) {
PIVector<PIThread*> threads; PIVector<PIThread*> threads;
for (int i = 0; i < THREAD_COUNT; ++i) { for (int i = 0; i < THREAD_COUNT; ++i) {
threads.push_back(new PIThread([=](){ adapterFunctionDefault(); })); threads.push_back(new PIThread([=](){ adapterFunctionDefault(); }));
} }
piForeach(PIThread* thread, threads) thread->startOnce(); piForeach(PIThread* thread, threads) thread->startOnce();
piMSleep(WAIT_THREAD_TIME_MS * THREAD_COUNT); piMSleep(WAIT_THREAD_TIME_MS * THREAD_COUNT);
variable->notifyAll(); variable->notifyAll();
PITimeMeasurer measurer; PITimeMeasurer measurer;
piForeach(PIThread* thread, threads) { piForeach(PIThread* thread, threads) {
int timeout = WAIT_THREAD_TIME_MS * THREAD_COUNT - (int)measurer.elapsed_m(); int timeout = WAIT_THREAD_TIME_MS * THREAD_COUNT - (int)measurer.elapsed_m();
thread->waitForFinish(timeout > 0 ? timeout : 0); thread->waitForFinish(timeout > 0 ? timeout : 0);
} }
for (size_t i = 0; i < threads.size(); ++i) EXPECT_FALSE(threads[i]->isRunning()) << "Thread " << i << " still running"; for (size_t i = 0; i < threads.size(); ++i) EXPECT_FALSE(threads[i]->isRunning()) << "Thread " << i << " still running";
piForeach(PIThread* thread, threads) delete thread; piForeach(PIThread* thread, threads) delete thread;
} }
TEST_F(ConditionVariable, wait_is_one_unblock_when_notifyOne) { TEST_F(ConditionVariable, wait_is_one_unblock_when_notifyOne) {
PIVector<PIThread*> threads; PIVector<PIThread*> threads;
for (int i = 0; i < THREAD_COUNT; ++i) { for (int i = 0; i < THREAD_COUNT; ++i) {
threads.push_back(new PIThread(adapterFunctionDefault)); threads.push_back(new PIThread(adapterFunctionDefault));
} }
piForeach(PIThread* thread, threads) thread->startOnce(); piForeach(PIThread* thread, threads) thread->startOnce();
piMSleep(WAIT_THREAD_TIME_MS * THREAD_COUNT); piMSleep(WAIT_THREAD_TIME_MS * THREAD_COUNT);
variable->notifyOne(); variable->notifyOne();
piMSleep(WAIT_THREAD_TIME_MS * THREAD_COUNT); piMSleep(WAIT_THREAD_TIME_MS * THREAD_COUNT);
int runningThreadCount = 0; int runningThreadCount = 0;
piForeach(PIThread* thread, threads) if (thread->isRunning()) runningThreadCount++; piForeach(PIThread* thread, threads) if (thread->isRunning()) runningThreadCount++;
ASSERT_EQ(runningThreadCount, THREAD_COUNT - 1); ASSERT_EQ(runningThreadCount, THREAD_COUNT - 1);
} }
TEST_F(ConditionVariable, wait_is_protected_unblock_when_notifyOne) { TEST_F(ConditionVariable, wait_is_protected_unblock_when_notifyOne) {
createThread([&](){ createThread([&](){
m.lock(); m.lock();
variable->wait(m); variable->wait(m);
piMSleep(2 * WAIT_THREAD_TIME_MS); piMSleep(2 * WAIT_THREAD_TIME_MS);
// Missing unlock // Missing unlock
}); });
variable->notifyOne(); variable->notifyOne();
msleep(WAIT_THREAD_TIME_MS); msleep(WAIT_THREAD_TIME_MS);
ASSERT_FALSE(m.tryLock()); ASSERT_FALSE(m.tryLock());
} }
TEST_F(ConditionVariable, wait_condition_is_block) { TEST_F(ConditionVariable, wait_condition_is_block) {
createThread([&](){ createThread([&](){
m.lock(); m.lock();
variable->wait(m, [](){ return false; }); variable->wait(m, [](){ return false; });
m.unlock(); m.unlock();
}); });
ASSERT_FALSE(thread->waitForFinish(WAIT_THREAD_TIME_MS)); ASSERT_FALSE(thread->waitForFinish(WAIT_THREAD_TIME_MS));
} }
TEST_F(ConditionVariable, wait_condition_is_check_condition_before_block) { TEST_F(ConditionVariable, wait_condition_is_check_condition_before_block) {
bool isConditionChecked = false; bool isConditionChecked = false;
createThread([&](){ createThread([&](){
m.lock(); m.lock();
variable->wait(m, [&](){ variable->wait(m, [&](){
isConditionChecked = true; isConditionChecked = true;
return false; return false;
}); });
m.unlock(); m.unlock();
}); });
m.lock(); m.lock();
ASSERT_TRUE(isConditionChecked); ASSERT_TRUE(isConditionChecked);
m.unlock(); m.unlock();
} }
TEST_F(ConditionVariable, wait_condition_is_check_condition_when_notifyOne) { TEST_F(ConditionVariable, wait_condition_is_check_condition_when_notifyOne) {
bool isConditionChecked; bool isConditionChecked;
createThread([&](){ createThread([&](){
m.lock(); m.lock();
variable->wait(m, [&](){ variable->wait(m, [&](){
isConditionChecked = true; isConditionChecked = true;
return false; return false;
}); });
m.unlock(); m.unlock();
}); });
m.lock(); m.lock();
isConditionChecked = false; isConditionChecked = false;
m.unlock(); m.unlock();
variable->notifyOne(); variable->notifyOne();
msleep(threadStartTime + 1); msleep(threadStartTime + 1);
m.lock(); m.lock();
ASSERT_TRUE(isConditionChecked); ASSERT_TRUE(isConditionChecked);
m.unlock(); m.unlock();
} }
TEST_F(ConditionVariable, wait_condition_is_unblock_when_condition_and_notifyOne) { TEST_F(ConditionVariable, wait_condition_is_unblock_when_condition_and_notifyOne) {
bool condition = false; bool condition = false;
createThread([&](){ createThread([&](){
m.lock(); m.lock();
variable->wait(m, [&](){ return condition; }); variable->wait(m, [&](){ return condition; });
m.unlock(); m.unlock();
}); });
m.lock(); m.lock();
condition = true; condition = true;
m.unlock(); m.unlock();
variable->notifyOne(); variable->notifyOne();
ASSERT_TRUE(thread->waitForFinish(WAIT_THREAD_TIME_MS)); ASSERT_TRUE(thread->waitForFinish(WAIT_THREAD_TIME_MS));
} }
TEST_F(ConditionVariable, DISABLED_waitFor_is_block_before_timeout) { TEST_F(ConditionVariable, DISABLED_waitFor_is_block_before_timeout) {
createThread([&](){ createThread([&](){
PITimeMeasurer measurer; PITimeMeasurer measurer;
m.lock(); m.lock();
variable->waitFor(m, WAIT_THREAD_TIME_MS * 2); variable->waitFor(m, WAIT_THREAD_TIME_MS * 2);
m.unlock(); m.unlock();
// Not reliable because spurious wakeup may happen // Not reliable because spurious wakeup may happen
ASSERT_GE(measurer.elapsed_m(), WAIT_THREAD_TIME_MS); ASSERT_GE(measurer.elapsed_m(), WAIT_THREAD_TIME_MS);
}); });
EXPECT_TRUE(thread->waitForFinish(WAIT_THREAD_TIME_MS * 3)); EXPECT_TRUE(thread->waitForFinish(WAIT_THREAD_TIME_MS * 3));
} }
TEST_F(ConditionVariable, waitFor_is_unblock_when_timeout) { TEST_F(ConditionVariable, waitFor_is_unblock_when_timeout) {
std::atomic_bool isUnblock(false); std::atomic_bool isUnblock(false);
createThread([&](){ createThread([&](){
m.lock(); m.lock();
variable->waitFor(m, WAIT_THREAD_TIME_MS); variable->waitFor(m, WAIT_THREAD_TIME_MS);
isUnblock = true; isUnblock = true;
m.unlock(); m.unlock();
}); });
// Test failed if suspend forever // Test failed if suspend forever
EXPECT_TRUE(thread->waitForFinish(2 * WAIT_THREAD_TIME_MS)); EXPECT_TRUE(thread->waitForFinish(2 * WAIT_THREAD_TIME_MS));
ASSERT_TRUE(isUnblock); ASSERT_TRUE(isUnblock);
} }
TEST_F(ConditionVariable, waitFor_is_false_when_timeout) { TEST_F(ConditionVariable, waitFor_is_false_when_timeout) {
bool waitRet = true; bool waitRet = true;
createThread([&](){ createThread([&](){
m.lock(); m.lock();
waitRet = variable->waitFor(m, WAIT_THREAD_TIME_MS); waitRet = variable->waitFor(m, WAIT_THREAD_TIME_MS);
m.unlock(); m.unlock();
}); });
EXPECT_TRUE(thread->waitForFinish(2 * WAIT_THREAD_TIME_MS)); EXPECT_TRUE(thread->waitForFinish(2 * WAIT_THREAD_TIME_MS));
ASSERT_FALSE(waitRet); ASSERT_FALSE(waitRet);
} }
TEST_F(ConditionVariable, waitFor_is_unblock_when_condition_and_notifyOne) { TEST_F(ConditionVariable, waitFor_is_unblock_when_condition_and_notifyOne) {
bool condition = false; bool condition = false;
createThread([&](){ createThread([&](){
m.lock(); m.lock();
variable->waitFor(m, 3 * WAIT_THREAD_TIME_MS, [&](){ return condition; }); variable->waitFor(m, 3 * WAIT_THREAD_TIME_MS, [&](){ return condition; });
m.unlock(); m.unlock();
}); });
EXPECT_TRUE(thread->isRunning()); EXPECT_TRUE(thread->isRunning());
m.lock(); m.lock();
condition = true; condition = true;
m.unlock(); m.unlock();
variable->notifyOne(); variable->notifyOne();
msleep(WAIT_THREAD_TIME_MS); msleep(WAIT_THREAD_TIME_MS);
ASSERT_FALSE(thread->isRunning()); ASSERT_FALSE(thread->isRunning());
} }