blob: 9a5a5c13be72169db6b8afc189edd5d9d3741cb8 [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 {
27namespace {
28
29absl::once_flag once;
30
31ABSL_CONST_INIT Mutex counters_mu(absl::kConstInit);
32
33int running_thread_count ABSL_GUARDED_BY(counters_mu) = 0;
34int call_once_invoke_count ABSL_GUARDED_BY(counters_mu) = 0;
35int call_once_finished_count ABSL_GUARDED_BY(counters_mu) = 0;
36int call_once_return_count ABSL_GUARDED_BY(counters_mu) = 0;
37bool done_blocking ABSL_GUARDED_BY(counters_mu) = false;
38
39// Function to be called from absl::call_once. Waits for a notification.
40void WaitAndIncrement() {
41 counters_mu.Lock();
42 ++call_once_invoke_count;
43 counters_mu.Unlock();
44
45 counters_mu.LockWhen(Condition(&done_blocking));
46 ++call_once_finished_count;
47 counters_mu.Unlock();
48}
49
50void ThreadBody() {
51 counters_mu.Lock();
52 ++running_thread_count;
53 counters_mu.Unlock();
54
55 absl::call_once(once, WaitAndIncrement);
56
57 counters_mu.Lock();
58 ++call_once_return_count;
59 counters_mu.Unlock();
60}
61
62// Returns true if all threads are set up for the test.
63bool ThreadsAreSetup(void*) ABSL_EXCLUSIVE_LOCKS_REQUIRED(counters_mu) {
64 // All ten threads must be running, and WaitAndIncrement should be blocked.
65 return running_thread_count == 10 && call_once_invoke_count == 1;
66}
67
68TEST(CallOnceTest, ExecutionCount) {
69 std::vector<std::thread> threads;
70
71 // Start 10 threads all calling call_once on the same once_flag.
72 for (int i = 0; i < 10; ++i) {
73 threads.emplace_back(ThreadBody);
74 }
75
76
77 // Wait until all ten threads have started, and WaitAndIncrement has been
78 // invoked.
79 counters_mu.LockWhen(Condition(ThreadsAreSetup, nullptr));
80
81 // WaitAndIncrement should have been invoked by exactly one call_once()
82 // instance. That thread should be blocking on a notification, and all other
83 // call_once instances should be blocking as well.
84 EXPECT_EQ(call_once_invoke_count, 1);
85 EXPECT_EQ(call_once_finished_count, 0);
86 EXPECT_EQ(call_once_return_count, 0);
87
88 // Allow WaitAndIncrement to finish executing. Once it does, the other
89 // call_once waiters will be unblocked.
90 done_blocking = true;
91 counters_mu.Unlock();
92
93 for (std::thread& thread : threads) {
94 thread.join();
95 }
96
97 counters_mu.Lock();
98 EXPECT_EQ(call_once_invoke_count, 1);
99 EXPECT_EQ(call_once_finished_count, 1);
100 EXPECT_EQ(call_once_return_count, 10);
101 counters_mu.Unlock();
102}
103
104} // namespace
105} // namespace absl