// Copyright (c) 2014 Baidu, Inc. // Author: Zhangyi Chen (chenzhangyi01@baidu.com) // Date: 2015/03/06 18:34:03 #include <iostream> #if __cplusplus >= 201103L #include <condition_variable> #endif #include <gtest/gtest.h> #include "butil/gperftools_profiler.h" #include "bvar/utils/lock_timer.h" namespace { struct DummyMutex {}; } namespace std { template <> class lock_guard<DummyMutex> { public: lock_guard(DummyMutex&) {} }; template <> class unique_lock<DummyMutex> { public: unique_lock() {} unique_lock(DummyMutex&) {} template <typename T> unique_lock(DummyMutex&, T) {} bool try_lock() { return true; } void lock() {} void unlock() {} }; } // namespace std namespace { using bvar::IntRecorder; using bvar::LatencyRecorder; using bvar::utils::MutexWithRecorder; using bvar::utils::MutexWithLatencyRecorder; class LockTimerTest : public testing::Test { }; #if __cplusplus >= 201103L TEST_F(LockTimerTest, MutexWithRecorder) { IntRecorder recorder; MutexWithRecorder<std::mutex> mutex(recorder); { BAIDU_SCOPED_LOCK(mutex); } ASSERT_EQ(1u, recorder.get_value().num); LOG(INFO) << recorder; { std::unique_lock<decltype(mutex) > lck(mutex); lck.unlock(); lck.lock(); ASSERT_EQ(2u, recorder.get_value().num); LOG(INFO) << recorder; std::condition_variable cond; cond.wait_for(lck, std::chrono::milliseconds(10)); } ASSERT_EQ(3u, recorder.get_value().num); } TEST_F(LockTimerTest, MutexWithLatencyRecorder) { LatencyRecorder recorder(10); MutexWithLatencyRecorder<std::mutex> mutex(recorder); { BAIDU_SCOPED_LOCK(mutex); } ASSERT_EQ(1u, recorder.count()); { std::unique_lock<decltype(mutex) > lck(mutex); lck.unlock(); lck.lock(); ASSERT_EQ(2u, recorder.count()); LOG(INFO) << recorder; std::condition_variable cond; cond.wait_for(lck, std::chrono::milliseconds(10)); } ASSERT_EQ(3u, recorder.count()); } #endif TEST_F(LockTimerTest, pthread_mutex_and_cond) { LatencyRecorder recorder(10); MutexWithLatencyRecorder<pthread_mutex_t> mutex(recorder); { BAIDU_SCOPED_LOCK(mutex); } ASSERT_EQ(1u, recorder.count()); { std::unique_lock<MutexWithLatencyRecorder<pthread_mutex_t> > lck(mutex); ASSERT_EQ(1u, recorder.count()); timespec due_time = butil::milliseconds_from_now(10); pthread_cond_t cond; ASSERT_EQ(0, pthread_cond_init(&cond, NULL)); pthread_cond_timedwait(&cond, &(pthread_mutex_t&)mutex, &due_time); pthread_cond_timedwait(&cond, &mutex.mutex(), &due_time); ASSERT_EQ(0, pthread_cond_destroy(&cond)); } ASSERT_EQ(2u, recorder.count()); } const static size_t OPS_PER_THREAD = 1000; template <typename M> void *signal_lock_thread(void *arg) { M *m = (M*)arg; for (size_t i = 0; i < OPS_PER_THREAD; ++i) { { std::unique_lock<M> lck(*m); usleep(10); } } return NULL; } TEST_F(LockTimerTest, signal_lock_time) { IntRecorder r0; MutexWithRecorder<pthread_mutex_t> m0(r0); pthread_t threads[4]; for (size_t i = 0; i < ARRAY_SIZE(threads); ++i) { ASSERT_EQ(0, pthread_create(&threads[i], NULL, signal_lock_thread<MutexWithRecorder<pthread_mutex_t> >, &m0)); } for (size_t i = 0; i < ARRAY_SIZE(threads); ++i) { pthread_join(threads[i], NULL); } LOG(INFO) << r0; ASSERT_EQ(OPS_PER_THREAD * ARRAY_SIZE(threads), (size_t)r0.get_value().num); LatencyRecorder r1; MutexWithLatencyRecorder<pthread_mutex_t> m1(r1); for (size_t i = 0; i < ARRAY_SIZE(threads); ++i) { ASSERT_EQ(0, pthread_create(&threads[i], NULL, signal_lock_thread<MutexWithLatencyRecorder<pthread_mutex_t> >, &m1)); } for (size_t i = 0; i < ARRAY_SIZE(threads); ++i) { pthread_join(threads[i], NULL); } LOG(INFO) << r1._latency; ASSERT_EQ(OPS_PER_THREAD * ARRAY_SIZE(threads), (size_t)r1.count()); } template <typename M0, typename M1> struct DoubleLockArg { M0 m0; M1 m1; }; template <typename M0, typename M1> void *double_lock_thread(void *arg) { DoubleLockArg<M0, M1>* dla = (DoubleLockArg<M0, M1>*)arg; for (size_t i = 0; i < OPS_PER_THREAD; ++i) { std::unique_lock<M0> lck0(dla->m0, std::defer_lock); std::unique_lock<M1> lck1(dla->m1, std::defer_lock); butil::double_lock(lck0, lck1); usleep(10); } return NULL; } TEST_F(LockTimerTest, double_lock_time) { typedef MutexWithRecorder<pthread_mutex_t> M0; typedef MutexWithLatencyRecorder<pthread_mutex_t> M1; DoubleLockArg<M0, M1> arg; IntRecorder r0; LatencyRecorder r1; arg.m0.set_recorder(r0); arg.m1.set_recorder(r1); pthread_t threads[4]; for (size_t i = 0; i < ARRAY_SIZE(threads); ++i) { ASSERT_EQ(0, pthread_create(&threads[i], NULL, double_lock_thread<M0, M1>, &arg)); } for (size_t i = 0; i < ARRAY_SIZE(threads); ++i) { pthread_join(threads[i], NULL); } ASSERT_EQ(OPS_PER_THREAD * ARRAY_SIZE(threads), (size_t)r0.get_value().num); ASSERT_EQ(OPS_PER_THREAD * ARRAY_SIZE(threads), (size_t)r1.count()); LOG(INFO) << r0; LOG(INFO) << r1._latency; r0.reset(); r1._latency.reset(); DoubleLockArg<M1, M0> arg1; arg1.m0.set_recorder(r1); arg1.m1.set_recorder(r0); for (size_t i = 0; i < ARRAY_SIZE(threads); ++i) { ASSERT_EQ(0, pthread_create(&threads[i], NULL, double_lock_thread<M1, M0>, &arg1)); } for (size_t i = 0; i < ARRAY_SIZE(threads); ++i) { pthread_join(threads[i], NULL); } ASSERT_EQ(OPS_PER_THREAD * ARRAY_SIZE(threads), (size_t)r0.get_value().num); ASSERT_EQ(OPS_PER_THREAD * ARRAY_SIZE(threads), (size_t)r1.count()); LOG(INFO) << r0; LOG(INFO) << r1._latency; } TEST_F(LockTimerTest, overhead) { LatencyRecorder r0; MutexWithLatencyRecorder<DummyMutex> m0(r0); butil::Timer timer; const size_t N = 1000 * 1000 * 10; ProfilerStart("mutex_with_latency_recorder.prof"); timer.start(); for (size_t i = 0; i < N; ++i) { BAIDU_SCOPED_LOCK(m0); } timer.stop(); ProfilerStop(); LOG(INFO) << "The overhead of MutexWithLatencyRecorder is " << timer.n_elapsed() / N << "ns"; IntRecorder r1; MutexWithRecorder<DummyMutex> m1(r1); ProfilerStart("mutex_with_recorder.prof"); timer.start(); for (size_t i = 0; i < N; ++i) { BAIDU_SCOPED_LOCK(m1); } timer.stop(); ProfilerStop(); LOG(INFO) << "The overhead of MutexWithRecorder is " << timer.n_elapsed() / N << "ns"; MutexWithRecorder<DummyMutex> m2; ProfilerStart("mutex_with_timer.prof"); timer.start(); for (size_t i = 0; i < N; ++i) { BAIDU_SCOPED_LOCK(m2); } timer.stop(); ProfilerStop(); LOG(INFO) << "The overhead of timer is " << timer.n_elapsed() / N << "ns"; } } // namespace