• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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