// Licensed to the Apache Software Foundation (ASF) under one // or more contributor license agreements. See the NOTICE file // distributed with this work for additional information // regarding copyright ownership. The ASF licenses this file // to you under the Apache License, Version 2.0 (the // "License"); you may not use this file except in compliance // with the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. // Date: 2015/03/06 18:34:03 #include #if __cplusplus >= 201103L #include #endif #include #include "butil/gperftools_profiler.h" #include "bvar/utils/lock_timer.h" namespace { struct DummyMutex {}; } namespace std { template <> class lock_guard { public: lock_guard(DummyMutex&) {} }; template <> class unique_lock { public: unique_lock() {} unique_lock(DummyMutex&) {} template 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 mutex(recorder); { BAIDU_SCOPED_LOCK(mutex); } ASSERT_EQ(1u, recorder.get_value().num); LOG(INFO) << recorder; { std::unique_lock 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 mutex(recorder); { BAIDU_SCOPED_LOCK(mutex); } ASSERT_EQ(1u, recorder.count()); { std::unique_lock 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 mutex(recorder); { BAIDU_SCOPED_LOCK(mutex); } ASSERT_EQ(1u, recorder.count()); { std::unique_lock > 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 void *signal_lock_thread(void *arg) { M *m = (M*)arg; for (size_t i = 0; i < OPS_PER_THREAD; ++i) { { std::unique_lock lck(*m); usleep(10); } } return NULL; } TEST_F(LockTimerTest, signal_lock_time) { IntRecorder r0; MutexWithRecorder 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 >, &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 m1(r1); for (size_t i = 0; i < ARRAY_SIZE(threads); ++i) { ASSERT_EQ(0, pthread_create(&threads[i], NULL, signal_lock_thread >, &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 struct DoubleLockArg { M0 m0; M1 m1; }; template void *double_lock_thread(void *arg) { DoubleLockArg* dla = (DoubleLockArg*)arg; for (size_t i = 0; i < OPS_PER_THREAD; ++i) { std::unique_lock lck0(dla->m0, std::defer_lock); std::unique_lock lck1(dla->m1, std::defer_lock); butil::double_lock(lck0, lck1); usleep(10); } return NULL; } TEST_F(LockTimerTest, double_lock_time) { typedef MutexWithRecorder M0; typedef MutexWithLatencyRecorder M1; DoubleLockArg 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, &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 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, &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 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 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 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