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 (at) 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: 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 177 OnceInitTest* OnceInitTest::current_test_ = NULL; 178 179 GOOGLE_PROTOBUF_DECLARE_ONCE(simple_once); 180 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 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 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 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