blob: 11d26c44d1a4647572e3fbe0245ce35574dde39a [file] [log] [blame]
Austin Schuh36244a12019-09-21 17:52:38 -07001// 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
26namespace absl {
Austin Schuhb4691e92020-12-31 12:37:18 -080027ABSL_NAMESPACE_BEGIN
Austin Schuh36244a12019-09-21 17:52:38 -070028namespace {
29
30absl::once_flag once;
31
32ABSL_CONST_INIT Mutex counters_mu(absl::kConstInit);
33
34int running_thread_count ABSL_GUARDED_BY(counters_mu) = 0;
35int call_once_invoke_count ABSL_GUARDED_BY(counters_mu) = 0;
36int call_once_finished_count ABSL_GUARDED_BY(counters_mu) = 0;
37int call_once_return_count ABSL_GUARDED_BY(counters_mu) = 0;
38bool done_blocking ABSL_GUARDED_BY(counters_mu) = false;
39
40// Function to be called from absl::call_once. Waits for a notification.
41void 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
51void 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.
64bool 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
69TEST(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
Austin Schuhb4691e92020-12-31 12:37:18 -0800106ABSL_NAMESPACE_END
Austin Schuh36244a12019-09-21 17:52:38 -0700107} // namespace absl