Working unit test for critical sections.
This extends unittest coverage, and allows to add more tests if these functions ever are found to behave strangely. BUG= TEST= Review URL: https://webrtc-codereview.appspot.com/632005 git-svn-id: http://webrtc.googlecode.com/svn/trunk@2427 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
parent
5608fe9861
commit
3168e5349c
167
src/system_wrappers/source/critical_section_unittest.cc
Normal file
167
src/system_wrappers/source/critical_section_unittest.cc
Normal file
@ -0,0 +1,167 @@
|
||||
/*
|
||||
* Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifdef _WIN32
|
||||
// For Sleep()
|
||||
#include <windows.h>
|
||||
#else
|
||||
// For nanosleep()
|
||||
#include <time.h>
|
||||
#endif
|
||||
|
||||
#include "system_wrappers/interface/critical_section_wrapper.h"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "system_wrappers/interface/sleep.h"
|
||||
#include "system_wrappers/interface/thread_wrapper.h"
|
||||
#include "system_wrappers/interface/trace.h"
|
||||
#include "system_wrappers/source/unittest_utilities.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
namespace {
|
||||
|
||||
const bool kLogTrace = false; // Set to true to enable debug logging to stdout.
|
||||
|
||||
#define LOG(...) WEBRTC_TRACE(kTraceStateInfo, kTraceUtility, -1, __VA_ARGS__);
|
||||
|
||||
// Cause a process switch. Needed to avoid depending on
|
||||
// busy-wait in tests.
|
||||
static void SwitchProcess() {
|
||||
// Note - sched_yield has been tried as process switch. This does
|
||||
// not cause a process switch enough of the time for reliability.
|
||||
SleepMs(1);
|
||||
}
|
||||
|
||||
class ProtectedCount {
|
||||
public:
|
||||
explicit ProtectedCount(CriticalSectionWrapper* crit_sect)
|
||||
: crit_sect_(crit_sect),
|
||||
count_(0) {
|
||||
}
|
||||
|
||||
void Increment() {
|
||||
CriticalSectionScoped cs(crit_sect_);
|
||||
++count_;
|
||||
LOG("Inc to %d", count_);
|
||||
}
|
||||
|
||||
int Count() const {
|
||||
CriticalSectionScoped cs(crit_sect_);
|
||||
return count_;
|
||||
}
|
||||
|
||||
private:
|
||||
CriticalSectionWrapper* crit_sect_;
|
||||
int count_;
|
||||
};
|
||||
|
||||
class CritSectTest : public ::testing::Test {
|
||||
public:
|
||||
CritSectTest() : trace_(kLogTrace) {
|
||||
}
|
||||
|
||||
// Waits a number of cycles for the count to reach a given value.
|
||||
// Returns true if the target is reached or passed.
|
||||
bool WaitForCount(int target, ProtectedCount* count) {
|
||||
int loop_counter = 0;
|
||||
// On Posix, this SwitchProcess() needs to be in a loop to make the
|
||||
// test both fast and non-flaky.
|
||||
// With 1 us wait as the switch, up to 7 rounds have been observed.
|
||||
while (count->Count() < target && loop_counter < 100*target) {
|
||||
++loop_counter;
|
||||
SwitchProcess();
|
||||
}
|
||||
LOG("Test looped %d times\n", loop_counter);
|
||||
return (count->Count() >= target);
|
||||
}
|
||||
|
||||
private:
|
||||
ScopedTracing trace_;
|
||||
};
|
||||
|
||||
bool LockUnlockThenStopRunFunction(void* obj) {
|
||||
LOG("Wait starting");
|
||||
ProtectedCount* the_count = static_cast<ProtectedCount*> (obj);
|
||||
LOG("Wait incrementing");
|
||||
the_count->Increment();
|
||||
LOG("Wait returning");
|
||||
return false;
|
||||
}
|
||||
|
||||
TEST_F(CritSectTest, ThreadWakesOnce) {
|
||||
CriticalSectionWrapper* crit_sect
|
||||
= CriticalSectionWrapper::CreateCriticalSection();
|
||||
ProtectedCount count(crit_sect);
|
||||
ThreadWrapper* thread = ThreadWrapper::CreateThread(
|
||||
&LockUnlockThenStopRunFunction, &count);
|
||||
unsigned int id = 42;
|
||||
crit_sect->Enter();
|
||||
ASSERT_TRUE(thread->Start(id));
|
||||
SwitchProcess();
|
||||
// The critical section is of reentrant mode, so this should not release
|
||||
// the lock, even though count.Count() locks and unlocks the critical section
|
||||
// again.
|
||||
// Thus, the thread should not be able to increment the count
|
||||
ASSERT_EQ(0, count.Count());
|
||||
crit_sect->Leave(); // This frees the thread to act.
|
||||
EXPECT_TRUE(WaitForCount(1, &count));
|
||||
EXPECT_TRUE(thread->Stop());
|
||||
delete thread;
|
||||
delete crit_sect;
|
||||
}
|
||||
|
||||
bool LockUnlockRunFunction(void* obj) {
|
||||
LOG("Wait starting");
|
||||
ProtectedCount* the_count = static_cast<ProtectedCount*> (obj);
|
||||
LOG("Wait incrementing");
|
||||
the_count->Increment();
|
||||
SwitchProcess();
|
||||
LOG("Wait returning");
|
||||
return true;
|
||||
}
|
||||
|
||||
TEST_F(CritSectTest, ThreadWakesTwice) {
|
||||
CriticalSectionWrapper* crit_sect
|
||||
= CriticalSectionWrapper::CreateCriticalSection();
|
||||
ProtectedCount count(crit_sect);
|
||||
ThreadWrapper* thread = ThreadWrapper::CreateThread(&LockUnlockRunFunction,
|
||||
&count);
|
||||
unsigned int id = 42;
|
||||
crit_sect->Enter(); // Make sure counter stays 0 until we wait for it.
|
||||
ASSERT_TRUE(thread->Start(id));
|
||||
crit_sect->Leave();
|
||||
|
||||
// The thread is capable of grabbing the lock multiple times,
|
||||
// incrementing counter once each time.
|
||||
// It's possible for the count to be incremented by more than 2.
|
||||
EXPECT_TRUE(WaitForCount(2, &count));
|
||||
EXPECT_LE(2, count.Count());
|
||||
|
||||
// The thread does not increment while lock is held.
|
||||
crit_sect->Enter();
|
||||
int count_before = count.Count();
|
||||
for (int i = 0; i < 10; i++) {
|
||||
SwitchProcess();
|
||||
}
|
||||
EXPECT_EQ(count_before, count.Count());
|
||||
crit_sect->Leave();
|
||||
|
||||
thread->SetNotAlive(); // Tell thread to exit once run function finishes.
|
||||
SwitchProcess();
|
||||
EXPECT_LT(count_before, count.Count());
|
||||
EXPECT_TRUE(thread->Stop());
|
||||
delete thread;
|
||||
delete crit_sect;
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
} // namespace webrtc
|
@ -189,6 +189,7 @@
|
||||
'cpu_wrapper_unittest.cc',
|
||||
'cpu_measurement_harness.h',
|
||||
'cpu_measurement_harness.cc',
|
||||
'critical_section_unittest.cc',
|
||||
'list_unittest.cc',
|
||||
'map_unittest.cc',
|
||||
'data_log_unittest.cc',
|
||||
|
Loading…
Reference in New Issue
Block a user