call_once_test.cc (3208B)
1 // Copyright 2017 The Abseil Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // https://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #include "absl/base/call_once.h" 16 17 #include <thread> 18 #include <vector> 19 20 #include "gtest/gtest.h" 21 #include "absl/base/attributes.h" 22 #include "absl/base/const_init.h" 23 #include "absl/base/thread_annotations.h" 24 #include "absl/synchronization/mutex.h" 25 26 namespace absl { 27 ABSL_NAMESPACE_BEGIN 28 namespace { 29 30 absl::once_flag once; 31 32 ABSL_CONST_INIT Mutex counters_mu(absl::kConstInit); 33 34 int running_thread_count ABSL_GUARDED_BY(counters_mu) = 0; 35 int call_once_invoke_count ABSL_GUARDED_BY(counters_mu) = 0; 36 int call_once_finished_count ABSL_GUARDED_BY(counters_mu) = 0; 37 int call_once_return_count ABSL_GUARDED_BY(counters_mu) = 0; 38 bool done_blocking ABSL_GUARDED_BY(counters_mu) = false; 39 40 // Function to be called from absl::call_once. Waits for a notification. 41 void WaitAndIncrement() { 42 counters_mu.Lock(); 43 ++call_once_invoke_count; 44 counters_mu.Unlock(); 45 46 counters_mu.LockWhen(Condition(&done_blocking)); 47 ++call_once_finished_count; 48 counters_mu.Unlock(); 49 } 50 51 void ThreadBody() { 52 counters_mu.Lock(); 53 ++running_thread_count; 54 counters_mu.Unlock(); 55 56 absl::call_once(once, WaitAndIncrement); 57 58 counters_mu.Lock(); 59 ++call_once_return_count; 60 counters_mu.Unlock(); 61 } 62 63 // Returns true if all threads are set up for the test. 64 bool ThreadsAreSetup(void*) ABSL_EXCLUSIVE_LOCKS_REQUIRED(counters_mu) { 65 // All ten threads must be running, and WaitAndIncrement should be blocked. 66 return running_thread_count == 10 && call_once_invoke_count == 1; 67 } 68 69 TEST(CallOnceTest, ExecutionCount) { 70 std::vector<std::thread> threads; 71 72 // Start 10 threads all calling call_once on the same once_flag. 73 for (int i = 0; i < 10; ++i) { 74 threads.emplace_back(ThreadBody); 75 } 76 77 78 // Wait until all ten threads have started, and WaitAndIncrement has been 79 // invoked. 80 counters_mu.LockWhen(Condition(ThreadsAreSetup, nullptr)); 81 82 // WaitAndIncrement should have been invoked by exactly one call_once() 83 // instance. That thread should be blocking on a notification, and all other 84 // call_once instances should be blocking as well. 85 EXPECT_EQ(call_once_invoke_count, 1); 86 EXPECT_EQ(call_once_finished_count, 0); 87 EXPECT_EQ(call_once_return_count, 0); 88 89 // Allow WaitAndIncrement to finish executing. Once it does, the other 90 // call_once waiters will be unblocked. 91 done_blocking = true; 92 counters_mu.Unlock(); 93 94 for (std::thread& thread : threads) { 95 thread.join(); 96 } 97 98 counters_mu.Lock(); 99 EXPECT_EQ(call_once_invoke_count, 1); 100 EXPECT_EQ(call_once_finished_count, 1); 101 EXPECT_EQ(call_once_return_count, 10); 102 counters_mu.Unlock(); 103 } 104 105 } // namespace 106 ABSL_NAMESPACE_END 107 } // namespace absl