Squashed 'third_party/protobuf/' content from commit e35e248

Change-Id: I6cbe123d09fe50fdcad0e51466665daeee7433c7
git-subtree-dir: third_party/protobuf
git-subtree-split: e35e24800fb8d694bdeea5fd63dc7d1b14d68723
diff --git a/src/google/protobuf/stubs/once_unittest.cc b/src/google/protobuf/stubs/once_unittest.cc
new file mode 100644
index 0000000..37def58
--- /dev/null
+++ b/src/google/protobuf/stubs/once_unittest.cc
@@ -0,0 +1,255 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Author: kenton@google.com (Kenton Varda)
+
+#ifdef _WIN32
+#include <windows.h>
+#else
+#include <unistd.h>
+#include <pthread.h>
+#endif
+
+#include <google/protobuf/stubs/once.h>
+#include <google/protobuf/testing/googletest.h>
+#include <gtest/gtest.h>
+
+namespace google {
+namespace protobuf {
+using internal::NewCallback;
+namespace {
+
+class OnceInitTest : public testing::Test {
+ protected:
+  void SetUp() {
+    state_ = INIT_NOT_STARTED;
+    current_test_ = this;
+  }
+
+  // Since ProtobufOnceType is only allowed to be allocated in static storage,
+  // each test must use a different pair of ProtobufOnceType objects which it
+  // must declare itself.
+  void SetOnces(ProtobufOnceType* once, ProtobufOnceType* recursive_once) {
+    once_ = once;
+    recursive_once_ = recursive_once;
+  }
+
+  void InitOnce() {
+    GoogleOnceInit(once_, &InitStatic);
+  }
+  void InitRecursiveOnce() {
+    GoogleOnceInit(recursive_once_, &InitRecursiveStatic);
+  }
+
+  void BlockInit() { init_blocker_.Lock(); }
+  void UnblockInit() { init_blocker_.Unlock(); }
+
+  class TestThread {
+   public:
+    TestThread(Closure* callback)
+        : done_(false), joined_(false), callback_(callback) {
+#ifdef _WIN32
+      thread_ = CreateThread(NULL, 0, &Start, this, 0, NULL);
+#else
+      pthread_create(&thread_, NULL, &Start, this);
+#endif
+    }
+    ~TestThread() {
+      if (!joined_) Join();
+    }
+
+    bool IsDone() {
+      MutexLock lock(&done_mutex_);
+      return done_;
+    }
+    void Join() {
+      joined_ = true;
+#ifdef _WIN32
+      WaitForSingleObject(thread_, INFINITE);
+      CloseHandle(thread_);
+#else
+      pthread_join(thread_, NULL);
+#endif
+    }
+
+   private:
+#ifdef _WIN32
+    HANDLE thread_;
+#else
+    pthread_t thread_;
+#endif
+
+    Mutex done_mutex_;
+    bool done_;
+    bool joined_;
+    Closure* callback_;
+
+#ifdef _WIN32
+    static DWORD WINAPI Start(LPVOID arg) {
+#else
+    static void* Start(void* arg) {
+#endif
+      reinterpret_cast<TestThread*>(arg)->Run();
+      return 0;
+    }
+
+    void Run() {
+      callback_->Run();
+      MutexLock lock(&done_mutex_);
+      done_ = true;
+    }
+  };
+
+  TestThread* RunInitOnceInNewThread() {
+    return new TestThread(internal::NewCallback(this, &OnceInitTest::InitOnce));
+  }
+  TestThread* RunInitRecursiveOnceInNewThread() {
+    return new TestThread(
+        internal::NewCallback(this, &OnceInitTest::InitRecursiveOnce));
+  }
+
+  enum State {
+    INIT_NOT_STARTED,
+    INIT_STARTED,
+    INIT_DONE
+  };
+  State CurrentState() {
+    MutexLock lock(&mutex_);
+    return state_;
+  }
+
+  void WaitABit() {
+#ifdef _WIN32
+    Sleep(1000);
+#else
+    sleep(1);
+#endif
+  }
+
+ private:
+  Mutex mutex_;
+  Mutex init_blocker_;
+  State state_;
+  ProtobufOnceType* once_;
+  ProtobufOnceType* recursive_once_;
+
+  void Init() {
+    MutexLock lock(&mutex_);
+    EXPECT_EQ(INIT_NOT_STARTED, state_);
+    state_ = INIT_STARTED;
+    mutex_.Unlock();
+    init_blocker_.Lock();
+    init_blocker_.Unlock();
+    mutex_.Lock();
+    state_ = INIT_DONE;
+  }
+
+  static OnceInitTest* current_test_;
+  static void InitStatic() { current_test_->Init(); }
+  static void InitRecursiveStatic() { current_test_->InitOnce(); }
+};
+
+OnceInitTest* OnceInitTest::current_test_ = NULL;
+
+GOOGLE_PROTOBUF_DECLARE_ONCE(simple_once);
+
+TEST_F(OnceInitTest, Simple) {
+  SetOnces(&simple_once, NULL);
+
+  EXPECT_EQ(INIT_NOT_STARTED, CurrentState());
+  InitOnce();
+  EXPECT_EQ(INIT_DONE, CurrentState());
+
+  // Calling again has no effect.
+  InitOnce();
+  EXPECT_EQ(INIT_DONE, CurrentState());
+}
+
+GOOGLE_PROTOBUF_DECLARE_ONCE(recursive_once1);
+GOOGLE_PROTOBUF_DECLARE_ONCE(recursive_once2);
+
+TEST_F(OnceInitTest, Recursive) {
+  SetOnces(&recursive_once1, &recursive_once2);
+
+  EXPECT_EQ(INIT_NOT_STARTED, CurrentState());
+  InitRecursiveOnce();
+  EXPECT_EQ(INIT_DONE, CurrentState());
+}
+
+GOOGLE_PROTOBUF_DECLARE_ONCE(multiple_threads_once);
+
+TEST_F(OnceInitTest, MultipleThreads) {
+  SetOnces(&multiple_threads_once, NULL);
+
+  scoped_ptr<TestThread> threads[4];
+  EXPECT_EQ(INIT_NOT_STARTED, CurrentState());
+  for (int i = 0; i < 4; i++) {
+    threads[i].reset(RunInitOnceInNewThread());
+  }
+  for (int i = 0; i < 4; i++) {
+    threads[i]->Join();
+  }
+  EXPECT_EQ(INIT_DONE, CurrentState());
+}
+
+GOOGLE_PROTOBUF_DECLARE_ONCE(multiple_threads_blocked_once1);
+GOOGLE_PROTOBUF_DECLARE_ONCE(multiple_threads_blocked_once2);
+
+TEST_F(OnceInitTest, MultipleThreadsBlocked) {
+  SetOnces(&multiple_threads_blocked_once1, &multiple_threads_blocked_once2);
+
+  scoped_ptr<TestThread> threads[8];
+  EXPECT_EQ(INIT_NOT_STARTED, CurrentState());
+
+  BlockInit();
+  for (int i = 0; i < 4; i++) {
+    threads[i].reset(RunInitOnceInNewThread());
+  }
+  for (int i = 4; i < 8; i++) {
+    threads[i].reset(RunInitRecursiveOnceInNewThread());
+  }
+
+  WaitABit();
+
+  // We should now have one thread blocked inside Init(), four blocked waiting
+  // for Init() to complete, and three blocked waiting for InitRecursive() to
+  // complete.
+  EXPECT_EQ(INIT_STARTED, CurrentState());
+  UnblockInit();
+
+  for (int i = 0; i < 8; i++) {
+    threads[i]->Join();
+  }
+  EXPECT_EQ(INIT_DONE, CurrentState());
+}
+
+}  // anonymous namespace
+}  // namespace protobuf
+}  // namespace google