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