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 (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 using internal::NewCallback; 47 namespace { 48 49 class OnceInitTest : public testing::Test { 50 protected: 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. 59 void SetOnces(ProtobufOnceType* once, ProtobufOnceType* recursive_once) { 60 once_ = once; 61 recursive_once_ = recursive_once; 62 } 63 64 void InitOnce() { 65 GoogleOnceInit(once_, &InitStatic); 66 } 67 void InitRecursiveOnce() { 68 GoogleOnceInit(recursive_once_, &InitRecursiveStatic); 69 } 70 71 void BlockInit() { init_blocker_.Lock(); } 72 void UnblockInit() { init_blocker_.Unlock(); } 73 74 class TestThread { 75 public: 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 } 84 ~TestThread() { 85 if (!joined_) Join(); 86 } 87 88 bool IsDone() { 89 MutexLock lock(&done_mutex_); 90 return done_; 91 } 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 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 130 TestThread* RunInitOnceInNewThread() { 131 return new TestThread(internal::NewCallback(this, &OnceInitTest::InitOnce)); 132 } 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 }; 143 State CurrentState() { 144 MutexLock lock(&mutex_); 145 return state_; 146 } 147 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 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_; 175 static void InitStatic() { current_test_->Init(); } 176 static void InitRecursiveStatic() { current_test_->InitOnce(); } 177 }; 178 179 OnceInitTest* OnceInitTest::current_test_ = NULL; 180 181 GOOGLE_PROTOBUF_DECLARE_ONCE(simple_once); 182 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 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 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 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