• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
WaitAndIncrement()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 
ThreadBody()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.
ThreadsAreSetup(void *)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 
TEST(CallOnceTest,ExecutionCount)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
108