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 namespace {
47 
48 class OnceInitTest : public testing::Test {
49  protected:
SetUp()50   void SetUp() {
51     state_ = INIT_NOT_STARTED;
52     current_test_ = this;
53   }
54 
55   // Since ProtobufOnceType is only allowed to be allocated in static storage,
56   // each test must use a different pair of ProtobufOnceType objects which it
57   // must declare itself.
SetOnces(ProtobufOnceType * once,ProtobufOnceType * recursive_once)58   void SetOnces(ProtobufOnceType* once, ProtobufOnceType* recursive_once) {
59     once_ = once;
60     recursive_once_ = recursive_once;
61   }
62 
InitOnce()63   void InitOnce() {
64     GoogleOnceInit(once_, &InitStatic);
65   }
InitRecursiveOnce()66   void InitRecursiveOnce() {
67     GoogleOnceInit(recursive_once_, &InitRecursiveStatic);
68   }
69 
BlockInit()70   void BlockInit() { init_blocker_.Lock(); }
UnblockInit()71   void UnblockInit() { init_blocker_.Unlock(); }
72 
73   class TestThread {
74    public:
TestThread(Closure * callback)75     TestThread(Closure* callback)
76         : done_(false), joined_(false), callback_(callback) {
77 #ifdef _WIN32
78       thread_ = CreateThread(NULL, 0, &Start, this, 0, NULL);
79 #else
80       pthread_create(&thread_, NULL, &Start, this);
81 #endif
82     }
~TestThread()83     ~TestThread() {
84       if (!joined_) Join();
85     }
86 
IsDone()87     bool IsDone() {
88       MutexLock lock(&done_mutex_);
89       return done_;
90     }
Join()91     void Join() {
92       joined_ = true;
93 #ifdef _WIN32
94       WaitForSingleObject(thread_, INFINITE);
95       CloseHandle(thread_);
96 #else
97       pthread_join(thread_, NULL);
98 #endif
99     }
100 
101    private:
102 #ifdef _WIN32
103     HANDLE thread_;
104 #else
105     pthread_t thread_;
106 #endif
107 
108     Mutex done_mutex_;
109     bool done_;
110     bool joined_;
111     Closure* callback_;
112 
113 #ifdef _WIN32
Start(LPVOID arg)114     static DWORD WINAPI Start(LPVOID arg) {
115 #else
116     static void* Start(void* arg) {
117 #endif
118       reinterpret_cast<TestThread*>(arg)->Run();
119       return 0;
120     }
121 
122     void Run() {
123       callback_->Run();
124       MutexLock lock(&done_mutex_);
125       done_ = true;
126     }
127   };
128 
RunInitOnceInNewThread()129   TestThread* RunInitOnceInNewThread() {
130     return new TestThread(NewCallback(this, &OnceInitTest::InitOnce));
131   }
RunInitRecursiveOnceInNewThread()132   TestThread* RunInitRecursiveOnceInNewThread() {
133     return new TestThread(NewCallback(this, &OnceInitTest::InitRecursiveOnce));
134   }
135 
136   enum State {
137     INIT_NOT_STARTED,
138     INIT_STARTED,
139     INIT_DONE
140   };
CurrentState()141   State CurrentState() {
142     MutexLock lock(&mutex_);
143     return state_;
144   }
145 
WaitABit()146   void WaitABit() {
147 #ifdef _WIN32
148     Sleep(1000);
149 #else
150     sleep(1);
151 #endif
152   }
153 
154  private:
155   Mutex mutex_;
156   Mutex init_blocker_;
157   State state_;
158   ProtobufOnceType* once_;
159   ProtobufOnceType* recursive_once_;
160 
Init()161   void Init() {
162     MutexLock lock(&mutex_);
163     EXPECT_EQ(INIT_NOT_STARTED, state_);
164     state_ = INIT_STARTED;
165     mutex_.Unlock();
166     init_blocker_.Lock();
167     init_blocker_.Unlock();
168     mutex_.Lock();
169     state_ = INIT_DONE;
170   }
171 
172   static OnceInitTest* current_test_;
InitStatic()173   static void InitStatic() { current_test_->Init(); }
InitRecursiveStatic()174   static void InitRecursiveStatic() { current_test_->InitOnce(); }
175 };
176 
177 OnceInitTest* OnceInitTest::current_test_ = NULL;
178 
179 GOOGLE_PROTOBUF_DECLARE_ONCE(simple_once);
180 
TEST_F(OnceInitTest,Simple)181 TEST_F(OnceInitTest, Simple) {
182   SetOnces(&simple_once, NULL);
183 
184   EXPECT_EQ(INIT_NOT_STARTED, CurrentState());
185   InitOnce();
186   EXPECT_EQ(INIT_DONE, CurrentState());
187 
188   // Calling again has no effect.
189   InitOnce();
190   EXPECT_EQ(INIT_DONE, CurrentState());
191 }
192 
193 GOOGLE_PROTOBUF_DECLARE_ONCE(recursive_once1);
194 GOOGLE_PROTOBUF_DECLARE_ONCE(recursive_once2);
195 
TEST_F(OnceInitTest,Recursive)196 TEST_F(OnceInitTest, Recursive) {
197   SetOnces(&recursive_once1, &recursive_once2);
198 
199   EXPECT_EQ(INIT_NOT_STARTED, CurrentState());
200   InitRecursiveOnce();
201   EXPECT_EQ(INIT_DONE, CurrentState());
202 }
203 
204 GOOGLE_PROTOBUF_DECLARE_ONCE(multiple_threads_once);
205 
TEST_F(OnceInitTest,MultipleThreads)206 TEST_F(OnceInitTest, MultipleThreads) {
207   SetOnces(&multiple_threads_once, NULL);
208 
209   scoped_ptr<TestThread> threads[4];
210   EXPECT_EQ(INIT_NOT_STARTED, CurrentState());
211   for (int i = 0; i < 4; i++) {
212     threads[i].reset(RunInitOnceInNewThread());
213   }
214   for (int i = 0; i < 4; i++) {
215     threads[i]->Join();
216   }
217   EXPECT_EQ(INIT_DONE, CurrentState());
218 }
219 
220 GOOGLE_PROTOBUF_DECLARE_ONCE(multiple_threads_blocked_once1);
221 GOOGLE_PROTOBUF_DECLARE_ONCE(multiple_threads_blocked_once2);
222 
TEST_F(OnceInitTest,MultipleThreadsBlocked)223 TEST_F(OnceInitTest, MultipleThreadsBlocked) {
224   SetOnces(&multiple_threads_blocked_once1, &multiple_threads_blocked_once2);
225 
226   scoped_ptr<TestThread> threads[8];
227   EXPECT_EQ(INIT_NOT_STARTED, CurrentState());
228 
229   BlockInit();
230   for (int i = 0; i < 4; i++) {
231     threads[i].reset(RunInitOnceInNewThread());
232   }
233   for (int i = 4; i < 8; i++) {
234     threads[i].reset(RunInitRecursiveOnceInNewThread());
235   }
236 
237   WaitABit();
238 
239   // We should now have one thread blocked inside Init(), four blocked waiting
240   // for Init() to complete, and three blocked waiting for InitRecursive() to
241   // complete.
242   EXPECT_EQ(INIT_STARTED, CurrentState());
243   UnblockInit();
244 
245   for (int i = 0; i < 8; i++) {
246     threads[i]->Join();
247   }
248   EXPECT_EQ(INIT_DONE, CurrentState());
249 }
250 
251 }  // anonymous namespace
252 }  // namespace protobuf
253 }  // namespace google
254