blob: 37def58d034e51270765d0e6efe28dc78d8203cf [file] [log] [blame]
Brian Silverman9c614bc2016-02-15 20:20:02 -05001// Protocol Buffers - Google's data interchange format
2// Copyright 2008 Google Inc. All rights reserved.
3// https://developers.google.com/protocol-buffers/
4//
5// Redistribution and use in source and binary forms, with or without
6// modification, are permitted provided that the following conditions are
7// met:
8//
9// * Redistributions of source code must retain the above copyright
10// notice, this list of conditions and the following disclaimer.
11// * Redistributions in binary form must reproduce the above
12// copyright notice, this list of conditions and the following disclaimer
13// in the documentation and/or other materials provided with the
14// distribution.
15// * Neither the name of Google Inc. nor the names of its
16// contributors may be used to endorse or promote products derived from
17// this software without specific prior written permission.
18//
19// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
31// Author: kenton@google.com (Kenton Varda)
32
33#ifdef _WIN32
34#include <windows.h>
35#else
36#include <unistd.h>
37#include <pthread.h>
38#endif
39
40#include <google/protobuf/stubs/once.h>
41#include <google/protobuf/testing/googletest.h>
42#include <gtest/gtest.h>
43
44namespace google {
45namespace protobuf {
46using internal::NewCallback;
47namespace {
48
49class OnceInitTest : public testing::Test {
50 protected:
51 void SetUp() {
52 state_ = INIT_NOT_STARTED;
53 current_test_ = this;
54 }
55
56 // Since ProtobufOnceType is only allowed to be allocated in static storage,
57 // each test must use a different pair of ProtobufOnceType objects which it
58 // must declare itself.
59 void SetOnces(ProtobufOnceType* once, ProtobufOnceType* recursive_once) {
60 once_ = once;
61 recursive_once_ = recursive_once;
62 }
63
64 void InitOnce() {
65 GoogleOnceInit(once_, &InitStatic);
66 }
67 void InitRecursiveOnce() {
68 GoogleOnceInit(recursive_once_, &InitRecursiveStatic);
69 }
70
71 void BlockInit() { init_blocker_.Lock(); }
72 void UnblockInit() { init_blocker_.Unlock(); }
73
74 class TestThread {
75 public:
76 TestThread(Closure* callback)
77 : done_(false), joined_(false), callback_(callback) {
78#ifdef _WIN32
79 thread_ = CreateThread(NULL, 0, &Start, this, 0, NULL);
80#else
81 pthread_create(&thread_, NULL, &Start, this);
82#endif
83 }
84 ~TestThread() {
85 if (!joined_) Join();
86 }
87
88 bool IsDone() {
89 MutexLock lock(&done_mutex_);
90 return done_;
91 }
92 void Join() {
93 joined_ = true;
94#ifdef _WIN32
95 WaitForSingleObject(thread_, INFINITE);
96 CloseHandle(thread_);
97#else
98 pthread_join(thread_, NULL);
99#endif
100 }
101
102 private:
103#ifdef _WIN32
104 HANDLE thread_;
105#else
106 pthread_t thread_;
107#endif
108
109 Mutex done_mutex_;
110 bool done_;
111 bool joined_;
112 Closure* callback_;
113
114#ifdef _WIN32
115 static DWORD WINAPI Start(LPVOID arg) {
116#else
117 static void* Start(void* arg) {
118#endif
119 reinterpret_cast<TestThread*>(arg)->Run();
120 return 0;
121 }
122
123 void Run() {
124 callback_->Run();
125 MutexLock lock(&done_mutex_);
126 done_ = true;
127 }
128 };
129
130 TestThread* RunInitOnceInNewThread() {
131 return new TestThread(internal::NewCallback(this, &OnceInitTest::InitOnce));
132 }
133 TestThread* RunInitRecursiveOnceInNewThread() {
134 return new TestThread(
135 internal::NewCallback(this, &OnceInitTest::InitRecursiveOnce));
136 }
137
138 enum State {
139 INIT_NOT_STARTED,
140 INIT_STARTED,
141 INIT_DONE
142 };
143 State CurrentState() {
144 MutexLock lock(&mutex_);
145 return state_;
146 }
147
148 void WaitABit() {
149#ifdef _WIN32
150 Sleep(1000);
151#else
152 sleep(1);
153#endif
154 }
155
156 private:
157 Mutex mutex_;
158 Mutex init_blocker_;
159 State state_;
160 ProtobufOnceType* once_;
161 ProtobufOnceType* recursive_once_;
162
163 void Init() {
164 MutexLock lock(&mutex_);
165 EXPECT_EQ(INIT_NOT_STARTED, state_);
166 state_ = INIT_STARTED;
167 mutex_.Unlock();
168 init_blocker_.Lock();
169 init_blocker_.Unlock();
170 mutex_.Lock();
171 state_ = INIT_DONE;
172 }
173
174 static OnceInitTest* current_test_;
175 static void InitStatic() { current_test_->Init(); }
176 static void InitRecursiveStatic() { current_test_->InitOnce(); }
177};
178
179OnceInitTest* OnceInitTest::current_test_ = NULL;
180
181GOOGLE_PROTOBUF_DECLARE_ONCE(simple_once);
182
183TEST_F(OnceInitTest, Simple) {
184 SetOnces(&simple_once, NULL);
185
186 EXPECT_EQ(INIT_NOT_STARTED, CurrentState());
187 InitOnce();
188 EXPECT_EQ(INIT_DONE, CurrentState());
189
190 // Calling again has no effect.
191 InitOnce();
192 EXPECT_EQ(INIT_DONE, CurrentState());
193}
194
195GOOGLE_PROTOBUF_DECLARE_ONCE(recursive_once1);
196GOOGLE_PROTOBUF_DECLARE_ONCE(recursive_once2);
197
198TEST_F(OnceInitTest, Recursive) {
199 SetOnces(&recursive_once1, &recursive_once2);
200
201 EXPECT_EQ(INIT_NOT_STARTED, CurrentState());
202 InitRecursiveOnce();
203 EXPECT_EQ(INIT_DONE, CurrentState());
204}
205
206GOOGLE_PROTOBUF_DECLARE_ONCE(multiple_threads_once);
207
208TEST_F(OnceInitTest, MultipleThreads) {
209 SetOnces(&multiple_threads_once, NULL);
210
211 scoped_ptr<TestThread> threads[4];
212 EXPECT_EQ(INIT_NOT_STARTED, CurrentState());
213 for (int i = 0; i < 4; i++) {
214 threads[i].reset(RunInitOnceInNewThread());
215 }
216 for (int i = 0; i < 4; i++) {
217 threads[i]->Join();
218 }
219 EXPECT_EQ(INIT_DONE, CurrentState());
220}
221
222GOOGLE_PROTOBUF_DECLARE_ONCE(multiple_threads_blocked_once1);
223GOOGLE_PROTOBUF_DECLARE_ONCE(multiple_threads_blocked_once2);
224
225TEST_F(OnceInitTest, MultipleThreadsBlocked) {
226 SetOnces(&multiple_threads_blocked_once1, &multiple_threads_blocked_once2);
227
228 scoped_ptr<TestThread> threads[8];
229 EXPECT_EQ(INIT_NOT_STARTED, CurrentState());
230
231 BlockInit();
232 for (int i = 0; i < 4; i++) {
233 threads[i].reset(RunInitOnceInNewThread());
234 }
235 for (int i = 4; i < 8; i++) {
236 threads[i].reset(RunInitRecursiveOnceInNewThread());
237 }
238
239 WaitABit();
240
241 // We should now have one thread blocked inside Init(), four blocked waiting
242 // for Init() to complete, and three blocked waiting for InitRecursive() to
243 // complete.
244 EXPECT_EQ(INIT_STARTED, CurrentState());
245 UnblockInit();
246
247 for (int i = 0; i < 8; i++) {
248 threads[i]->Join();
249 }
250 EXPECT_EQ(INIT_DONE, CurrentState());
251}
252
253} // anonymous namespace
254} // namespace protobuf
255} // namespace google