1// Protocol Buffers - Google's data interchange format 2// Copyright 2008 Google Inc. All rights reserved. 3// http://code.google.com/p/protobuf/ 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 { 46namespace { 47 48class OnceInitTest : public testing::Test { 49 protected: 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. 58 void SetOnces(ProtobufOnceType* once, ProtobufOnceType* recursive_once) { 59 once_ = once; 60 recursive_once_ = recursive_once; 61 } 62 63 void InitOnce() { 64 GoogleOnceInit(once_, &InitStatic); 65 } 66 void InitRecursiveOnce() { 67 GoogleOnceInit(recursive_once_, &InitRecursiveStatic); 68 } 69 70 void BlockInit() { init_blocker_.Lock(); } 71 void UnblockInit() { init_blocker_.Unlock(); } 72 73 class TestThread { 74 public: 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 } 83 ~TestThread() { 84 if (!joined_) Join(); 85 } 86 87 bool IsDone() { 88 MutexLock lock(&done_mutex_); 89 return done_; 90 } 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 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 129 TestThread* RunInitOnceInNewThread() { 130 return new TestThread(NewCallback(this, &OnceInitTest::InitOnce)); 131 } 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 }; 141 State CurrentState() { 142 MutexLock lock(&mutex_); 143 return state_; 144 } 145 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 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_; 173 static void InitStatic() { current_test_->Init(); } 174 static void InitRecursiveStatic() { current_test_->InitOnce(); } 175}; 176 177OnceInitTest* OnceInitTest::current_test_ = NULL; 178 179GOOGLE_PROTOBUF_DECLARE_ONCE(simple_once); 180 181TEST_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 193GOOGLE_PROTOBUF_DECLARE_ONCE(recursive_once1); 194GOOGLE_PROTOBUF_DECLARE_ONCE(recursive_once2); 195 196TEST_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 204GOOGLE_PROTOBUF_DECLARE_ONCE(multiple_threads_once); 205 206TEST_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 220GOOGLE_PROTOBUF_DECLARE_ONCE(multiple_threads_blocked_once1); 221GOOGLE_PROTOBUF_DECLARE_ONCE(multiple_threads_blocked_once2); 222 223TEST_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