1 // 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
44 namespace google {
45 namespace protobuf {
46 using internal::NewCallback;
47 namespace {
48
49 class OnceInitTest : public testing::Test {
50 protected:
SetUp()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.
SetOnces(ProtobufOnceType * once,ProtobufOnceType * recursive_once)59 void SetOnces(ProtobufOnceType* once, ProtobufOnceType* recursive_once) {
60 once_ = once;
61 recursive_once_ = recursive_once;
62 }
63
InitOnce()64 void InitOnce() {
65 GoogleOnceInit(once_, &InitStatic);
66 }
InitRecursiveOnce()67 void InitRecursiveOnce() {
68 GoogleOnceInit(recursive_once_, &InitRecursiveStatic);
69 }
70
BlockInit()71 void BlockInit() { init_blocker_.Lock(); }
UnblockInit()72 void UnblockInit() { init_blocker_.Unlock(); }
73
74 class TestThread {
75 public:
TestThread(Closure * callback)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 }
~TestThread()84 ~TestThread() {
85 if (!joined_) Join();
86 }
87
IsDone()88 bool IsDone() {
89 MutexLock lock(&done_mutex_);
90 return done_;
91 }
Join()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
Start(LPVOID arg)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
RunInitOnceInNewThread()130 TestThread* RunInitOnceInNewThread() {
131 return new TestThread(internal::NewCallback(this, &OnceInitTest::InitOnce));
132 }
RunInitRecursiveOnceInNewThread()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 };
CurrentState()143 State CurrentState() {
144 MutexLock lock(&mutex_);
145 return state_;
146 }
147
WaitABit()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
Init()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_;
InitStatic()175 static void InitStatic() { current_test_->Init(); }
InitRecursiveStatic()176 static void InitRecursiveStatic() { current_test_->InitOnce(); }
177 };
178
179 OnceInitTest* OnceInitTest::current_test_ = NULL;
180
181 GOOGLE_PROTOBUF_DECLARE_ONCE(simple_once);
182
TEST_F(OnceInitTest,Simple)183 TEST_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
195 GOOGLE_PROTOBUF_DECLARE_ONCE(recursive_once1);
196 GOOGLE_PROTOBUF_DECLARE_ONCE(recursive_once2);
197
TEST_F(OnceInitTest,Recursive)198 TEST_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
206 GOOGLE_PROTOBUF_DECLARE_ONCE(multiple_threads_once);
207
TEST_F(OnceInitTest,MultipleThreads)208 TEST_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
222 GOOGLE_PROTOBUF_DECLARE_ONCE(multiple_threads_blocked_once1);
223 GOOGLE_PROTOBUF_DECLARE_ONCE(multiple_threads_blocked_once2);
224
TEST_F(OnceInitTest,MultipleThreadsBlocked)225 TEST_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
256