You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@marmotta.apache.org by ja...@apache.org on 2018/06/18 18:35:40 UTC
[21/62] [abbrv] [partial] marmotta git commit: * Replace gtest with
upstream version,
including LICENSE header. * Include absl library for faster and safer string
operations. * Update license headers where needed. * Removed custom code
replaced by absl.
http://git-wip-us.apache.org/repos/asf/marmotta/blob/0eb556da/libraries/ostrich/backend/3rdparty/abseil/absl/synchronization/mutex_test.cc
----------------------------------------------------------------------
diff --git a/libraries/ostrich/backend/3rdparty/abseil/absl/synchronization/mutex_test.cc b/libraries/ostrich/backend/3rdparty/abseil/absl/synchronization/mutex_test.cc
new file mode 100644
index 0000000..53b9378
--- /dev/null
+++ b/libraries/ostrich/backend/3rdparty/abseil/absl/synchronization/mutex_test.cc
@@ -0,0 +1,1539 @@
+// Copyright 2017 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "absl/synchronization/mutex.h"
+
+#ifdef WIN32
+#include <windows.h>
+#endif
+
+#include <algorithm>
+#include <atomic>
+#include <cstdlib>
+#include <functional>
+#include <memory>
+#include <random>
+#include <string>
+#include <thread> // NOLINT(build/c++11)
+#include <vector>
+
+#include "gtest/gtest.h"
+#include "absl/base/internal/raw_logging.h"
+#include "absl/base/internal/sysinfo.h"
+#include "absl/memory/memory.h"
+#include "absl/synchronization/internal/thread_pool.h"
+#include "absl/time/clock.h"
+#include "absl/time/time.h"
+
+namespace {
+
+// TODO(dmauro): Replace with a commandline flag.
+static constexpr bool kExtendedTest = false;
+
+std::unique_ptr<absl::synchronization_internal::ThreadPool> CreatePool(
+ int threads) {
+ return absl::make_unique<absl::synchronization_internal::ThreadPool>(threads);
+}
+
+std::unique_ptr<absl::synchronization_internal::ThreadPool>
+CreateDefaultPool() {
+ return CreatePool(kExtendedTest ? 32 : 10);
+}
+
+// Hack to schedule a function to run on a thread pool thread after a
+// duration has elapsed.
+static void ScheduleAfter(absl::synchronization_internal::ThreadPool *tp,
+ const std::function<void()> &func,
+ absl::Duration after) {
+ tp->Schedule([func, after] {
+ absl::SleepFor(after);
+ func();
+ });
+}
+
+struct TestContext {
+ int iterations;
+ int threads;
+ int g0; // global 0
+ int g1; // global 1
+ absl::Mutex mu;
+ absl::CondVar cv;
+};
+
+// To test whether the invariant check call occurs
+static std::atomic<bool> invariant_checked;
+
+static bool GetInvariantChecked() {
+ return invariant_checked.load(std::memory_order_relaxed);
+}
+
+static void SetInvariantChecked(bool new_value) {
+ invariant_checked.store(new_value, std::memory_order_relaxed);
+}
+
+static void CheckSumG0G1(void *v) {
+ TestContext *cxt = static_cast<TestContext *>(v);
+ ABSL_RAW_CHECK(cxt->g0 == -cxt->g1, "Error in CheckSumG0G1");
+ SetInvariantChecked(true);
+}
+
+static void TestMu(TestContext *cxt, int c) {
+ for (int i = 0; i != cxt->iterations; i++) {
+ absl::MutexLock l(&cxt->mu);
+ int a = cxt->g0 + 1;
+ cxt->g0 = a;
+ cxt->g1--;
+ }
+}
+
+static void TestTry(TestContext *cxt, int c) {
+ for (int i = 0; i != cxt->iterations; i++) {
+ do {
+ std::this_thread::yield();
+ } while (!cxt->mu.TryLock());
+ int a = cxt->g0 + 1;
+ cxt->g0 = a;
+ cxt->g1--;
+ cxt->mu.Unlock();
+ }
+}
+
+static void TestR20ms(TestContext *cxt, int c) {
+ for (int i = 0; i != cxt->iterations; i++) {
+ absl::ReaderMutexLock l(&cxt->mu);
+ absl::SleepFor(absl::Milliseconds(20));
+ cxt->mu.AssertReaderHeld();
+ }
+}
+
+static void TestRW(TestContext *cxt, int c) {
+ if ((c & 1) == 0) {
+ for (int i = 0; i != cxt->iterations; i++) {
+ absl::WriterMutexLock l(&cxt->mu);
+ cxt->g0++;
+ cxt->g1--;
+ cxt->mu.AssertHeld();
+ cxt->mu.AssertReaderHeld();
+ }
+ } else {
+ for (int i = 0; i != cxt->iterations; i++) {
+ absl::ReaderMutexLock l(&cxt->mu);
+ ABSL_RAW_CHECK(cxt->g0 == -cxt->g1, "Error in TestRW");
+ cxt->mu.AssertReaderHeld();
+ }
+ }
+}
+
+struct MyContext {
+ int target;
+ TestContext *cxt;
+ bool MyTurn();
+};
+
+bool MyContext::MyTurn() {
+ TestContext *cxt = this->cxt;
+ return cxt->g0 == this->target || cxt->g0 == cxt->iterations;
+}
+
+static void TestAwait(TestContext *cxt, int c) {
+ MyContext mc;
+ mc.target = c;
+ mc.cxt = cxt;
+ absl::MutexLock l(&cxt->mu);
+ cxt->mu.AssertHeld();
+ while (cxt->g0 < cxt->iterations) {
+ cxt->mu.Await(absl::Condition(&mc, &MyContext::MyTurn));
+ ABSL_RAW_CHECK(mc.MyTurn(), "Error in TestAwait");
+ cxt->mu.AssertHeld();
+ if (cxt->g0 < cxt->iterations) {
+ int a = cxt->g0 + 1;
+ cxt->g0 = a;
+ mc.target += cxt->threads;
+ }
+ }
+}
+
+static void TestSignalAll(TestContext *cxt, int c) {
+ int target = c;
+ absl::MutexLock l(&cxt->mu);
+ cxt->mu.AssertHeld();
+ while (cxt->g0 < cxt->iterations) {
+ while (cxt->g0 != target && cxt->g0 != cxt->iterations) {
+ cxt->cv.Wait(&cxt->mu);
+ }
+ if (cxt->g0 < cxt->iterations) {
+ int a = cxt->g0 + 1;
+ cxt->g0 = a;
+ cxt->cv.SignalAll();
+ target += cxt->threads;
+ }
+ }
+}
+
+static void TestSignal(TestContext *cxt, int c) {
+ ABSL_RAW_CHECK(cxt->threads == 2, "TestSignal should use 2 threads");
+ int target = c;
+ absl::MutexLock l(&cxt->mu);
+ cxt->mu.AssertHeld();
+ while (cxt->g0 < cxt->iterations) {
+ while (cxt->g0 != target && cxt->g0 != cxt->iterations) {
+ cxt->cv.Wait(&cxt->mu);
+ }
+ if (cxt->g0 < cxt->iterations) {
+ int a = cxt->g0 + 1;
+ cxt->g0 = a;
+ cxt->cv.Signal();
+ target += cxt->threads;
+ }
+ }
+}
+
+static void TestCVTimeout(TestContext *cxt, int c) {
+ int target = c;
+ absl::MutexLock l(&cxt->mu);
+ cxt->mu.AssertHeld();
+ while (cxt->g0 < cxt->iterations) {
+ while (cxt->g0 != target && cxt->g0 != cxt->iterations) {
+ cxt->cv.WaitWithTimeout(&cxt->mu, absl::Seconds(100));
+ }
+ if (cxt->g0 < cxt->iterations) {
+ int a = cxt->g0 + 1;
+ cxt->g0 = a;
+ cxt->cv.SignalAll();
+ target += cxt->threads;
+ }
+ }
+}
+
+static bool G0GE2(TestContext *cxt) { return cxt->g0 >= 2; }
+
+static void TestTime(TestContext *cxt, int c, bool use_cv) {
+ ABSL_RAW_CHECK(cxt->iterations == 1, "TestTime should only use 1 iteration");
+ ABSL_RAW_CHECK(cxt->threads > 2, "TestTime should use more than 2 threads");
+ const bool kFalse = false;
+ absl::Condition false_cond(&kFalse);
+ absl::Condition g0ge2(G0GE2, cxt);
+ if (c == 0) {
+ absl::MutexLock l(&cxt->mu);
+
+ absl::Time start = absl::Now();
+ if (use_cv) {
+ cxt->cv.WaitWithTimeout(&cxt->mu, absl::Seconds(1));
+ } else {
+ ABSL_RAW_CHECK(!cxt->mu.AwaitWithTimeout(false_cond, absl::Seconds(1)),
+ "TestTime failed");
+ }
+ absl::Duration elapsed = absl::Now() - start;
+ ABSL_RAW_CHECK(
+ absl::Seconds(0.9) <= elapsed && elapsed <= absl::Seconds(2.0),
+ "TestTime failed");
+ ABSL_RAW_CHECK(cxt->g0 == 1, "TestTime failed");
+
+ start = absl::Now();
+ if (use_cv) {
+ cxt->cv.WaitWithTimeout(&cxt->mu, absl::Seconds(1));
+ } else {
+ ABSL_RAW_CHECK(!cxt->mu.AwaitWithTimeout(false_cond, absl::Seconds(1)),
+ "TestTime failed");
+ }
+ elapsed = absl::Now() - start;
+ ABSL_RAW_CHECK(
+ absl::Seconds(0.9) <= elapsed && elapsed <= absl::Seconds(2.0),
+ "TestTime failed");
+ cxt->g0++;
+ if (use_cv) {
+ cxt->cv.Signal();
+ }
+
+ start = absl::Now();
+ if (use_cv) {
+ cxt->cv.WaitWithTimeout(&cxt->mu, absl::Seconds(4));
+ } else {
+ ABSL_RAW_CHECK(!cxt->mu.AwaitWithTimeout(false_cond, absl::Seconds(4)),
+ "TestTime failed");
+ }
+ elapsed = absl::Now() - start;
+ ABSL_RAW_CHECK(
+ absl::Seconds(3.9) <= elapsed && elapsed <= absl::Seconds(6.0),
+ "TestTime failed");
+ ABSL_RAW_CHECK(cxt->g0 >= 3, "TestTime failed");
+
+ start = absl::Now();
+ if (use_cv) {
+ cxt->cv.WaitWithTimeout(&cxt->mu, absl::Seconds(1));
+ } else {
+ ABSL_RAW_CHECK(!cxt->mu.AwaitWithTimeout(false_cond, absl::Seconds(1)),
+ "TestTime failed");
+ }
+ elapsed = absl::Now() - start;
+ ABSL_RAW_CHECK(
+ absl::Seconds(0.9) <= elapsed && elapsed <= absl::Seconds(2.0),
+ "TestTime failed");
+ if (use_cv) {
+ cxt->cv.SignalAll();
+ }
+
+ start = absl::Now();
+ if (use_cv) {
+ cxt->cv.WaitWithTimeout(&cxt->mu, absl::Seconds(1));
+ } else {
+ ABSL_RAW_CHECK(!cxt->mu.AwaitWithTimeout(false_cond, absl::Seconds(1)),
+ "TestTime failed");
+ }
+ elapsed = absl::Now() - start;
+ ABSL_RAW_CHECK(absl::Seconds(0.9) <= elapsed &&
+ elapsed <= absl::Seconds(2.0), "TestTime failed");
+ ABSL_RAW_CHECK(cxt->g0 == cxt->threads, "TestTime failed");
+
+ } else if (c == 1) {
+ absl::MutexLock l(&cxt->mu);
+ const absl::Time start = absl::Now();
+ if (use_cv) {
+ cxt->cv.WaitWithTimeout(&cxt->mu, absl::Milliseconds(500));
+ } else {
+ ABSL_RAW_CHECK(
+ !cxt->mu.AwaitWithTimeout(false_cond, absl::Milliseconds(500)),
+ "TestTime failed");
+ }
+ const absl::Duration elapsed = absl::Now() - start;
+ ABSL_RAW_CHECK(
+ absl::Seconds(0.4) <= elapsed && elapsed <= absl::Seconds(0.9),
+ "TestTime failed");
+ cxt->g0++;
+ } else if (c == 2) {
+ absl::MutexLock l(&cxt->mu);
+ if (use_cv) {
+ while (cxt->g0 < 2) {
+ cxt->cv.WaitWithTimeout(&cxt->mu, absl::Seconds(100));
+ }
+ } else {
+ ABSL_RAW_CHECK(cxt->mu.AwaitWithTimeout(g0ge2, absl::Seconds(100)),
+ "TestTime failed");
+ }
+ cxt->g0++;
+ } else {
+ absl::MutexLock l(&cxt->mu);
+ if (use_cv) {
+ while (cxt->g0 < 2) {
+ cxt->cv.Wait(&cxt->mu);
+ }
+ } else {
+ cxt->mu.Await(g0ge2);
+ }
+ cxt->g0++;
+ }
+}
+
+static void TestMuTime(TestContext *cxt, int c) { TestTime(cxt, c, false); }
+
+static void TestCVTime(TestContext *cxt, int c) { TestTime(cxt, c, true); }
+
+static void EndTest(int *c0, int *c1, absl::Mutex *mu, absl::CondVar *cv,
+ const std::function<void(int)>& cb) {
+ mu->Lock();
+ int c = (*c0)++;
+ mu->Unlock();
+ cb(c);
+ absl::MutexLock l(mu);
+ (*c1)++;
+ cv->Signal();
+}
+
+// Code common to RunTest() and RunTestWithInvariantDebugging().
+static int RunTestCommon(TestContext *cxt, void (*test)(TestContext *cxt, int),
+ int threads, int iterations, int operations) {
+ absl::Mutex mu2;
+ absl::CondVar cv2;
+ int c0 = 0;
+ int c1 = 0;
+ cxt->g0 = 0;
+ cxt->g1 = 0;
+ cxt->iterations = iterations;
+ cxt->threads = threads;
+ absl::synchronization_internal::ThreadPool tp(threads);
+ for (int i = 0; i != threads; i++) {
+ tp.Schedule(std::bind(&EndTest, &c0, &c1, &mu2, &cv2,
+ std::function<void(int)>(
+ std::bind(test, cxt, std::placeholders::_1))));
+ }
+ mu2.Lock();
+ while (c1 != threads) {
+ cv2.Wait(&mu2);
+ }
+ mu2.Unlock();
+ return cxt->g0;
+}
+
+// Basis for the parameterized tests configured below.
+static int RunTest(void (*test)(TestContext *cxt, int), int threads,
+ int iterations, int operations) {
+ TestContext cxt;
+ return RunTestCommon(&cxt, test, threads, iterations, operations);
+}
+
+// Like RunTest(), but sets an invariant on the tested Mutex and
+// verifies that the invariant check happened. The invariant function
+// will be passed the TestContext* as its arg and must call
+// SetInvariantChecked(true);
+#if !defined(ABSL_MUTEX_ENABLE_INVARIANT_DEBUGGING_NOT_IMPLEMENTED)
+static int RunTestWithInvariantDebugging(void (*test)(TestContext *cxt, int),
+ int threads, int iterations,
+ int operations,
+ void (*invariant)(void *)) {
+ absl::EnableMutexInvariantDebugging(true);
+ SetInvariantChecked(false);
+ TestContext cxt;
+ cxt.mu.EnableInvariantDebugging(invariant, &cxt);
+ int ret = RunTestCommon(&cxt, test, threads, iterations, operations);
+ ABSL_RAW_CHECK(GetInvariantChecked(), "Invariant not checked");
+ absl::EnableMutexInvariantDebugging(false); // Restore.
+ return ret;
+}
+#endif
+
+// --------------------------------------------------------
+// Test for fix of bug in TryRemove()
+struct TimeoutBugStruct {
+ absl::Mutex mu;
+ bool a;
+ int a_waiter_count;
+};
+
+static void WaitForA(TimeoutBugStruct *x) {
+ x->mu.LockWhen(absl::Condition(&x->a));
+ x->a_waiter_count--;
+ x->mu.Unlock();
+}
+
+static bool NoAWaiters(TimeoutBugStruct *x) { return x->a_waiter_count == 0; }
+
+// Test that a CondVar.Wait(&mutex) can un-block a call to mutex.Await() in
+// another thread.
+TEST(Mutex, CondVarWaitSignalsAwait) {
+ // Use a struct so the lock annotations apply.
+ struct {
+ absl::Mutex barrier_mu;
+ bool barrier GUARDED_BY(barrier_mu) = false;
+
+ absl::Mutex release_mu;
+ bool release GUARDED_BY(release_mu) = false;
+ absl::CondVar released_cv;
+ } state;
+
+ auto pool = CreateDefaultPool();
+
+ // Thread A. Sets barrier, waits for release using Mutex::Await, then
+ // signals released_cv.
+ pool->Schedule([&state] {
+ state.release_mu.Lock();
+
+ state.barrier_mu.Lock();
+ state.barrier = true;
+ state.barrier_mu.Unlock();
+
+ state.release_mu.Await(absl::Condition(&state.release));
+ state.released_cv.Signal();
+ state.release_mu.Unlock();
+ });
+
+ state.barrier_mu.LockWhen(absl::Condition(&state.barrier));
+ state.barrier_mu.Unlock();
+ state.release_mu.Lock();
+ // Thread A is now blocked on release by way of Mutex::Await().
+
+ // Set release. Calling released_cv.Wait() should un-block thread A,
+ // which will signal released_cv. If not, the test will hang.
+ state.release = true;
+ state.released_cv.Wait(&state.release_mu);
+ state.release_mu.Unlock();
+}
+
+// Test that a CondVar.WaitWithTimeout(&mutex) can un-block a call to
+// mutex.Await() in another thread.
+TEST(Mutex, CondVarWaitWithTimeoutSignalsAwait) {
+ // Use a struct so the lock annotations apply.
+ struct {
+ absl::Mutex barrier_mu;
+ bool barrier GUARDED_BY(barrier_mu) = false;
+
+ absl::Mutex release_mu;
+ bool release GUARDED_BY(release_mu) = false;
+ absl::CondVar released_cv;
+ } state;
+
+ auto pool = CreateDefaultPool();
+
+ // Thread A. Sets barrier, waits for release using Mutex::Await, then
+ // signals released_cv.
+ pool->Schedule([&state] {
+ state.release_mu.Lock();
+
+ state.barrier_mu.Lock();
+ state.barrier = true;
+ state.barrier_mu.Unlock();
+
+ state.release_mu.Await(absl::Condition(&state.release));
+ state.released_cv.Signal();
+ state.release_mu.Unlock();
+ });
+
+ state.barrier_mu.LockWhen(absl::Condition(&state.barrier));
+ state.barrier_mu.Unlock();
+ state.release_mu.Lock();
+ // Thread A is now blocked on release by way of Mutex::Await().
+
+ // Set release. Calling released_cv.Wait() should un-block thread A,
+ // which will signal released_cv. If not, the test will hang.
+ state.release = true;
+ EXPECT_TRUE(
+ !state.released_cv.WaitWithTimeout(&state.release_mu, absl::Seconds(10)))
+ << "; Unrecoverable test failure: CondVar::WaitWithTimeout did not "
+ "unblock the absl::Mutex::Await call in another thread.";
+
+ state.release_mu.Unlock();
+}
+
+// Test for regression of a bug in loop of TryRemove()
+TEST(Mutex, MutexTimeoutBug) {
+ auto tp = CreateDefaultPool();
+
+ TimeoutBugStruct x;
+ x.a = false;
+ x.a_waiter_count = 2;
+ tp->Schedule(std::bind(&WaitForA, &x));
+ tp->Schedule(std::bind(&WaitForA, &x));
+ absl::SleepFor(absl::Seconds(1)); // Allow first two threads to hang.
+ // The skip field of the second will point to the first because there are
+ // only two.
+
+ // Now cause a thread waiting on an always-false to time out
+ // This would deadlock when the bug was present.
+ bool always_false = false;
+ x.mu.LockWhenWithTimeout(absl::Condition(&always_false),
+ absl::Milliseconds(500));
+
+ // if we get here, the bug is not present. Cleanup the state.
+
+ x.a = true; // wakeup the two waiters on A
+ x.mu.Await(absl::Condition(&NoAWaiters, &x)); // wait for them to exit
+ x.mu.Unlock();
+}
+
+struct CondVarWaitDeadlock : testing::TestWithParam<int> {
+ absl::Mutex mu;
+ absl::CondVar cv;
+ bool cond1 = false;
+ bool cond2 = false;
+ bool read_lock1;
+ bool read_lock2;
+ bool signal_unlocked;
+
+ CondVarWaitDeadlock() {
+ read_lock1 = GetParam() & (1 << 0);
+ read_lock2 = GetParam() & (1 << 1);
+ signal_unlocked = GetParam() & (1 << 2);
+ }
+
+ void Waiter1() {
+ if (read_lock1) {
+ mu.ReaderLock();
+ while (!cond1) {
+ cv.Wait(&mu);
+ }
+ mu.ReaderUnlock();
+ } else {
+ mu.Lock();
+ while (!cond1) {
+ cv.Wait(&mu);
+ }
+ mu.Unlock();
+ }
+ }
+
+ void Waiter2() {
+ if (read_lock2) {
+ mu.ReaderLockWhen(absl::Condition(&cond2));
+ mu.ReaderUnlock();
+ } else {
+ mu.LockWhen(absl::Condition(&cond2));
+ mu.Unlock();
+ }
+ }
+};
+
+// Test for a deadlock bug in Mutex::Fer().
+// The sequence of events that lead to the deadlock is:
+// 1. waiter1 blocks on cv in read mode (mu bits = 0).
+// 2. waiter2 blocks on mu in either mode (mu bits = kMuWait).
+// 3. main thread locks mu, sets cond1, unlocks mu (mu bits = kMuWait).
+// 4. main thread signals on cv and this eventually calls Mutex::Fer().
+// Currently Fer wakes waiter1 since mu bits = kMuWait (mutex is unlocked).
+// Before the bug fix Fer neither woke waiter1 nor queued it on mutex,
+// which resulted in deadlock.
+TEST_P(CondVarWaitDeadlock, Test) {
+ auto waiter1 = CreatePool(1);
+ auto waiter2 = CreatePool(1);
+ waiter1->Schedule([this] { this->Waiter1(); });
+ waiter2->Schedule([this] { this->Waiter2(); });
+
+ // Wait while threads block (best-effort is fine).
+ absl::SleepFor(absl::Milliseconds(100));
+
+ // Wake condwaiter.
+ mu.Lock();
+ cond1 = true;
+ if (signal_unlocked) {
+ mu.Unlock();
+ cv.Signal();
+ } else {
+ cv.Signal();
+ mu.Unlock();
+ }
+ waiter1.reset(); // "join" waiter1
+
+ // Wake waiter.
+ mu.Lock();
+ cond2 = true;
+ mu.Unlock();
+ waiter2.reset(); // "join" waiter2
+}
+
+INSTANTIATE_TEST_CASE_P(CondVarWaitDeadlockTest, CondVarWaitDeadlock,
+ ::testing::Range(0, 8),
+ ::testing::PrintToStringParamName());
+
+// --------------------------------------------------------
+// Test for fix of bug in DequeueAllWakeable()
+// Bug was that if there was more than one waiting reader
+// and all should be woken, the most recently blocked one
+// would not be.
+
+struct DequeueAllWakeableBugStruct {
+ absl::Mutex mu;
+ absl::Mutex mu2; // protects all fields below
+ int unfinished_count; // count of unfinished readers; under mu2
+ bool done1; // unfinished_count == 0; under mu2
+ int finished_count; // count of finished readers, under mu2
+ bool done2; // finished_count == 0; under mu2
+};
+
+// Test for regression of a bug in loop of DequeueAllWakeable()
+static void AcquireAsReader(DequeueAllWakeableBugStruct *x) {
+ x->mu.ReaderLock();
+ x->mu2.Lock();
+ x->unfinished_count--;
+ x->done1 = (x->unfinished_count == 0);
+ x->mu2.Unlock();
+ // make sure that both readers acquired mu before we release it.
+ absl::SleepFor(absl::Seconds(2));
+ x->mu.ReaderUnlock();
+
+ x->mu2.Lock();
+ x->finished_count--;
+ x->done2 = (x->finished_count == 0);
+ x->mu2.Unlock();
+}
+
+// Test for regression of a bug in loop of DequeueAllWakeable()
+TEST(Mutex, MutexReaderWakeupBug) {
+ auto tp = CreateDefaultPool();
+
+ DequeueAllWakeableBugStruct x;
+ x.unfinished_count = 2;
+ x.done1 = false;
+ x.finished_count = 2;
+ x.done2 = false;
+ x.mu.Lock(); // acquire mu exclusively
+ // queue two thread that will block on reader locks on x.mu
+ tp->Schedule(std::bind(&AcquireAsReader, &x));
+ tp->Schedule(std::bind(&AcquireAsReader, &x));
+ absl::SleepFor(absl::Seconds(1)); // give time for reader threads to block
+ x.mu.Unlock(); // wake them up
+
+ // both readers should finish promptly
+ EXPECT_TRUE(
+ x.mu2.LockWhenWithTimeout(absl::Condition(&x.done1), absl::Seconds(10)));
+ x.mu2.Unlock();
+
+ EXPECT_TRUE(
+ x.mu2.LockWhenWithTimeout(absl::Condition(&x.done2), absl::Seconds(10)));
+ x.mu2.Unlock();
+}
+
+struct LockWhenTestStruct {
+ absl::Mutex mu1;
+ bool cond = false;
+
+ absl::Mutex mu2;
+ bool waiting = false;
+};
+
+static bool LockWhenTestIsCond(LockWhenTestStruct* s) {
+ s->mu2.Lock();
+ s->waiting = true;
+ s->mu2.Unlock();
+ return s->cond;
+}
+
+static void LockWhenTestWaitForIsCond(LockWhenTestStruct* s) {
+ s->mu1.LockWhen(absl::Condition(&LockWhenTestIsCond, s));
+ s->mu1.Unlock();
+}
+
+TEST(Mutex, LockWhen) {
+ LockWhenTestStruct s;
+
+ std::thread t(LockWhenTestWaitForIsCond, &s);
+ s.mu2.LockWhen(absl::Condition(&s.waiting));
+ s.mu2.Unlock();
+
+ s.mu1.Lock();
+ s.cond = true;
+ s.mu1.Unlock();
+
+ t.join();
+}
+
+// --------------------------------------------------------
+// The following test requires Mutex::ReaderLock to be a real shared
+// lock, which is not the case in all builds.
+#if !defined(ABSL_MUTEX_READER_LOCK_IS_EXCLUSIVE)
+
+// Test for fix of bug in UnlockSlow() that incorrectly decremented the reader
+// count when putting a thread to sleep waiting for a false condition when the
+// lock was not held.
+
+// For this bug to strike, we make a thread wait on a free mutex with no
+// waiters by causing its wakeup condition to be false. Then the
+// next two acquirers must be readers. The bug causes the lock
+// to be released when one reader unlocks, rather than both.
+
+struct ReaderDecrementBugStruct {
+ bool cond; // to delay first thread (under mu)
+ int done; // reference count (under mu)
+ absl::Mutex mu;
+
+ bool waiting_on_cond; // under mu2
+ bool have_reader_lock; // under mu2
+ bool complete; // under mu2
+ absl::Mutex mu2; // > mu
+};
+
+// L >= mu, L < mu_waiting_on_cond
+static bool IsCond(void *v) {
+ ReaderDecrementBugStruct *x = reinterpret_cast<ReaderDecrementBugStruct *>(v);
+ x->mu2.Lock();
+ x->waiting_on_cond = true;
+ x->mu2.Unlock();
+ return x->cond;
+}
+
+// L >= mu
+static bool AllDone(void *v) {
+ ReaderDecrementBugStruct *x = reinterpret_cast<ReaderDecrementBugStruct *>(v);
+ return x->done == 0;
+}
+
+// L={}
+static void WaitForCond(ReaderDecrementBugStruct *x) {
+ absl::Mutex dummy;
+ absl::MutexLock l(&dummy);
+ x->mu.LockWhen(absl::Condition(&IsCond, x));
+ x->done--;
+ x->mu.Unlock();
+}
+
+// L={}
+static void GetReadLock(ReaderDecrementBugStruct *x) {
+ x->mu.ReaderLock();
+ x->mu2.Lock();
+ x->have_reader_lock = true;
+ x->mu2.Await(absl::Condition(&x->complete));
+ x->mu2.Unlock();
+ x->mu.ReaderUnlock();
+ x->mu.Lock();
+ x->done--;
+ x->mu.Unlock();
+}
+
+// Test for reader counter being decremented incorrectly by waiter
+// with false condition.
+TEST(Mutex, MutexReaderDecrementBug) NO_THREAD_SAFETY_ANALYSIS {
+ ReaderDecrementBugStruct x;
+ x.cond = false;
+ x.waiting_on_cond = false;
+ x.have_reader_lock = false;
+ x.complete = false;
+ x.done = 2; // initial ref count
+
+ // Run WaitForCond() and wait for it to sleep
+ std::thread thread1(WaitForCond, &x);
+ x.mu2.LockWhen(absl::Condition(&x.waiting_on_cond));
+ x.mu2.Unlock();
+
+ // Run GetReadLock(), and wait for it to get the read lock
+ std::thread thread2(GetReadLock, &x);
+ x.mu2.LockWhen(absl::Condition(&x.have_reader_lock));
+ x.mu2.Unlock();
+
+ // Get the reader lock ourselves, and release it.
+ x.mu.ReaderLock();
+ x.mu.ReaderUnlock();
+
+ // The lock should be held in read mode by GetReadLock().
+ // If we have the bug, the lock will be free.
+ x.mu.AssertReaderHeld();
+
+ // Wake up all the threads.
+ x.mu2.Lock();
+ x.complete = true;
+ x.mu2.Unlock();
+
+ // TODO(delesley): turn on analysis once lock upgrading is supported.
+ // (This call upgrades the lock from shared to exclusive.)
+ x.mu.Lock();
+ x.cond = true;
+ x.mu.Await(absl::Condition(&AllDone, &x));
+ x.mu.Unlock();
+
+ thread1.join();
+ thread2.join();
+}
+#endif // !ABSL_MUTEX_READER_LOCK_IS_EXCLUSIVE
+
+// Test that we correctly handle the situation when a lock is
+// held and then destroyed (w/o unlocking).
+TEST(Mutex, LockedMutexDestructionBug) NO_THREAD_SAFETY_ANALYSIS {
+ for (int i = 0; i != 10; i++) {
+ // Create, lock and destroy 10 locks.
+ const int kNumLocks = 10;
+ auto mu = absl::make_unique<absl::Mutex[]>(kNumLocks);
+ for (int j = 0; j != kNumLocks; j++) {
+ if ((j % 2) == 0) {
+ mu[j].WriterLock();
+ } else {
+ mu[j].ReaderLock();
+ }
+ }
+ }
+}
+
+// --------------------------------------------------------
+// Test for bug with pattern of readers using a condvar. The bug was that if a
+// reader went to sleep on a condition variable while one or more other readers
+// held the lock, but there were no waiters, the reader count (held in the
+// mutex word) would be lost. (This is because Enqueue() had at one time
+// always placed the thread on the Mutex queue. Later (CL 4075610), to
+// tolerate re-entry into Mutex from a Condition predicate, Enqueue() was
+// changed so that it could also place a thread on a condition-variable. This
+// introduced the case where Enqueue() returned with an empty queue, and this
+// case was handled incorrectly in one place.)
+
+static void ReaderForReaderOnCondVar(absl::Mutex *mu, absl::CondVar *cv,
+ int *running) {
+ std::random_device dev;
+ std::mt19937 gen(dev());
+ std::uniform_int_distribution<int> random_millis(0, 15);
+ mu->ReaderLock();
+ while (*running == 3) {
+ absl::SleepFor(absl::Milliseconds(random_millis(gen)));
+ cv->WaitWithTimeout(mu, absl::Milliseconds(random_millis(gen)));
+ }
+ mu->ReaderUnlock();
+ mu->Lock();
+ (*running)--;
+ mu->Unlock();
+}
+
+struct True {
+ template <class... Args>
+ bool operator()(Args...) const {
+ return true;
+ }
+};
+
+struct DerivedTrue : True {};
+
+TEST(Mutex, FunctorCondition) {
+ { // Variadic
+ True f;
+ EXPECT_TRUE(absl::Condition(&f).Eval());
+ }
+
+ { // Inherited
+ DerivedTrue g;
+ EXPECT_TRUE(absl::Condition(&g).Eval());
+ }
+
+ { // lambda
+ int value = 3;
+ auto is_zero = [&value] { return value == 0; };
+ absl::Condition c(&is_zero);
+ EXPECT_FALSE(c.Eval());
+ value = 0;
+ EXPECT_TRUE(c.Eval());
+ }
+
+ { // bind
+ int value = 0;
+ auto is_positive = std::bind(std::less<int>(), 0, std::cref(value));
+ absl::Condition c(&is_positive);
+ EXPECT_FALSE(c.Eval());
+ value = 1;
+ EXPECT_TRUE(c.Eval());
+ }
+
+ { // std::function
+ int value = 3;
+ std::function<bool()> is_zero = [&value] { return value == 0; };
+ absl::Condition c(&is_zero);
+ EXPECT_FALSE(c.Eval());
+ value = 0;
+ EXPECT_TRUE(c.Eval());
+ }
+}
+
+static bool IntIsZero(int *x) { return *x == 0; }
+
+// Test for reader waiting condition variable when there are other readers
+// but no waiters.
+TEST(Mutex, TestReaderOnCondVar) {
+ auto tp = CreateDefaultPool();
+ absl::Mutex mu;
+ absl::CondVar cv;
+ int running = 3;
+ tp->Schedule(std::bind(&ReaderForReaderOnCondVar, &mu, &cv, &running));
+ tp->Schedule(std::bind(&ReaderForReaderOnCondVar, &mu, &cv, &running));
+ absl::SleepFor(absl::Seconds(2));
+ mu.Lock();
+ running--;
+ mu.Await(absl::Condition(&IntIsZero, &running));
+ mu.Unlock();
+}
+
+// --------------------------------------------------------
+struct AcquireFromConditionStruct {
+ absl::Mutex mu0; // protects value, done
+ int value; // times condition function is called; under mu0,
+ bool done; // done with test? under mu0
+ absl::Mutex mu1; // used to attempt to mess up state of mu0
+ absl::CondVar cv; // so the condition function can be invoked from
+ // CondVar::Wait().
+};
+
+static bool ConditionWithAcquire(AcquireFromConditionStruct *x) {
+ x->value++; // count times this function is called
+
+ if (x->value == 2 || x->value == 3) {
+ // On the second and third invocation of this function, sleep for 100ms,
+ // but with the side-effect of altering the state of a Mutex other than
+ // than one for which this is a condition. The spec now explicitly allows
+ // this side effect; previously it did not. it was illegal.
+ bool always_false = false;
+ x->mu1.LockWhenWithTimeout(absl::Condition(&always_false),
+ absl::Milliseconds(100));
+ x->mu1.Unlock();
+ }
+ ABSL_RAW_CHECK(x->value < 4, "should not be invoked a fourth time");
+
+ // We arrange for the condition to return true on only the 2nd and 3rd calls.
+ return x->value == 2 || x->value == 3;
+}
+
+static void WaitForCond2(AcquireFromConditionStruct *x) {
+ // wait for cond0 to become true
+ x->mu0.LockWhen(absl::Condition(&ConditionWithAcquire, x));
+ x->done = true;
+ x->mu0.Unlock();
+}
+
+// Test for Condition whose function acquires other Mutexes
+TEST(Mutex, AcquireFromCondition) {
+ auto tp = CreateDefaultPool();
+
+ AcquireFromConditionStruct x;
+ x.value = 0;
+ x.done = false;
+ tp->Schedule(
+ std::bind(&WaitForCond2, &x)); // run WaitForCond2() in a thread T
+ // T will hang because the first invocation of ConditionWithAcquire() will
+ // return false.
+ absl::SleepFor(absl::Milliseconds(500)); // allow T time to hang
+
+ x.mu0.Lock();
+ x.cv.WaitWithTimeout(&x.mu0, absl::Milliseconds(500)); // wake T
+ // T will be woken because the Wait() will call ConditionWithAcquire()
+ // for the second time, and it will return true.
+
+ x.mu0.Unlock();
+
+ // T will then acquire the lock and recheck its own condition.
+ // It will find the condition true, as this is the third invocation,
+ // but the use of another Mutex by the calling function will
+ // cause the old mutex implementation to think that the outer
+ // LockWhen() has timed out because the inner LockWhenWithTimeout() did.
+ // T will then check the condition a fourth time because it finds a
+ // timeout occurred. This should not happen in the new
+ // implementation that allows the Condition function to use Mutexes.
+
+ // It should also succeed, even though the Condition function
+ // is being invoked from CondVar::Wait, and thus this thread
+ // is conceptually waiting both on the condition variable, and on mu2.
+
+ x.mu0.LockWhen(absl::Condition(&x.done));
+ x.mu0.Unlock();
+}
+
+// The deadlock detector is not part of non-prod builds, so do not test it.
+#if !defined(ABSL_INTERNAL_USE_NONPROD_MUTEX)
+
+TEST(Mutex, DeadlockDetector) {
+ absl::SetMutexDeadlockDetectionMode(absl::OnDeadlockCycle::kAbort);
+
+ // check that we can call ForgetDeadlockInfo() on a lock with the lock held
+ absl::Mutex m1;
+ absl::Mutex m2;
+ absl::Mutex m3;
+ absl::Mutex m4;
+
+ m1.Lock(); // m1 gets ID1
+ m2.Lock(); // m2 gets ID2
+ m3.Lock(); // m3 gets ID3
+ m3.Unlock();
+ m2.Unlock();
+ // m1 still held
+ m1.ForgetDeadlockInfo(); // m1 loses ID
+ m2.Lock(); // m2 gets ID2
+ m3.Lock(); // m3 gets ID3
+ m4.Lock(); // m4 gets ID4
+ m3.Unlock();
+ m2.Unlock();
+ m4.Unlock();
+ m1.Unlock();
+}
+
+// Bazel has a test "warning" file that programs can write to if the
+// test should pass with a warning. This class disables the warning
+// file until it goes out of scope.
+class ScopedDisableBazelTestWarnings {
+ public:
+ ScopedDisableBazelTestWarnings() {
+#ifdef WIN32
+ char file[MAX_PATH];
+ if (GetEnvironmentVariable(kVarName, file, sizeof(file)) < sizeof(file)) {
+ warnings_output_file_ = file;
+ SetEnvironmentVariable(kVarName, nullptr);
+ }
+#else
+ const char *file = getenv(kVarName);
+ if (file != nullptr) {
+ warnings_output_file_ = file;
+ unsetenv(kVarName);
+ }
+#endif
+ }
+
+ ~ScopedDisableBazelTestWarnings() {
+ if (!warnings_output_file_.empty()) {
+#ifdef WIN32
+ SetEnvironmentVariable(kVarName, warnings_output_file_.c_str());
+#else
+ setenv(kVarName, warnings_output_file_.c_str(), 0);
+#endif
+ }
+ }
+
+ private:
+ static const char kVarName[];
+ std::string warnings_output_file_;
+};
+const char ScopedDisableBazelTestWarnings::kVarName[] =
+ "TEST_WARNINGS_OUTPUT_FILE";
+
+TEST(Mutex, DeadlockDetectorBazelWarning) {
+ absl::SetMutexDeadlockDetectionMode(absl::OnDeadlockCycle::kReport);
+
+ // Cause deadlock detection to detect something, if it's
+ // compiled in and enabled. But turn off the bazel warning.
+ ScopedDisableBazelTestWarnings disable_bazel_test_warnings;
+
+ absl::Mutex mu0;
+ absl::Mutex mu1;
+ bool got_mu0 = mu0.TryLock();
+ mu1.Lock(); // acquire mu1 while holding mu0
+ if (got_mu0) {
+ mu0.Unlock();
+ }
+ if (mu0.TryLock()) { // try lock shouldn't cause deadlock detector to fire
+ mu0.Unlock();
+ }
+ mu0.Lock(); // acquire mu0 while holding mu1; should get one deadlock
+ // report here
+ mu0.Unlock();
+ mu1.Unlock();
+
+ absl::SetMutexDeadlockDetectionMode(absl::OnDeadlockCycle::kAbort);
+}
+
+// This test is tagged with NO_THREAD_SAFETY_ANALYSIS because the
+// annotation-based static thread-safety analysis is not currently
+// predicate-aware and cannot tell if the two for-loops that acquire and
+// release the locks have the same predicates.
+TEST(Mutex, DeadlockDetectorStessTest) NO_THREAD_SAFETY_ANALYSIS {
+ // Stress test: Here we create a large number of locks and use all of them.
+ // If a deadlock detector keeps a full graph of lock acquisition order,
+ // it will likely be too slow for this test to pass.
+ const int n_locks = 1 << 17;
+ auto array_of_locks = absl::make_unique<absl::Mutex[]>(n_locks);
+ for (int i = 0; i < n_locks; i++) {
+ int end = std::min(n_locks, i + 5);
+ // acquire and then release locks i, i+1, ..., i+4
+ for (int j = i; j < end; j++) {
+ array_of_locks[j].Lock();
+ }
+ for (int j = i; j < end; j++) {
+ array_of_locks[j].Unlock();
+ }
+ }
+}
+
+TEST(Mutex, DeadlockIdBug) NO_THREAD_SAFETY_ANALYSIS {
+ // Test a scenario where a cached deadlock graph node id in the
+ // list of held locks is not invalidated when the corresponding
+ // mutex is deleted.
+ absl::SetMutexDeadlockDetectionMode(absl::OnDeadlockCycle::kAbort);
+ // Mutex that will be destroyed while being held
+ absl::Mutex *a = new absl::Mutex;
+ // Other mutexes needed by test
+ absl::Mutex b, c;
+
+ // Hold mutex.
+ a->Lock();
+
+ // Force deadlock id assignment by acquiring another lock.
+ b.Lock();
+ b.Unlock();
+
+ // Delete the mutex. The Mutex destructor tries to remove held locks,
+ // but the attempt isn't foolproof. It can fail if:
+ // (a) Deadlock detection is currently disabled.
+ // (b) The destruction is from another thread.
+ // We exploit (a) by temporarily disabling deadlock detection.
+ absl::SetMutexDeadlockDetectionMode(absl::OnDeadlockCycle::kIgnore);
+ delete a;
+ absl::SetMutexDeadlockDetectionMode(absl::OnDeadlockCycle::kAbort);
+
+ // Now acquire another lock which will force a deadlock id assignment.
+ // We should end up getting assigned the same deadlock id that was
+ // freed up when "a" was deleted, which will cause a spurious deadlock
+ // report if the held lock entry for "a" was not invalidated.
+ c.Lock();
+ c.Unlock();
+}
+#endif // !defined(ABSL_INTERNAL_USE_NONPROD_MUTEX)
+
+// --------------------------------------------------------
+// Test for timeouts/deadlines on condition waits that are specified using
+// absl::Duration and absl::Time. For each waiting function we test with
+// a timeout/deadline that has already expired/passed, one that is infinite
+// and so never expires/passes, and one that will expire/pass in the near
+// future.
+
+// Encapsulate a Mutex-protected bool with its associated Condition/CondVar.
+class Cond {
+ public:
+ explicit Cond(bool use_deadline) : use_deadline_(use_deadline), c_(&b_) {}
+
+ void Set(bool v) {
+ absl::MutexLock lock(&mu_);
+ b_ = v;
+ }
+
+ bool AwaitWithTimeout(absl::Duration timeout) {
+ absl::MutexLock lock(&mu_);
+ return use_deadline_ ? mu_.AwaitWithDeadline(c_, absl::Now() + timeout)
+ : mu_.AwaitWithTimeout(c_, timeout);
+ }
+
+ bool LockWhenWithTimeout(absl::Duration timeout) {
+ bool b = use_deadline_ ? mu_.LockWhenWithDeadline(c_, absl::Now() + timeout)
+ : mu_.LockWhenWithTimeout(c_, timeout);
+ mu_.Unlock();
+ return b;
+ }
+
+ bool ReaderLockWhenWithTimeout(absl::Duration timeout) {
+ bool b = use_deadline_
+ ? mu_.ReaderLockWhenWithDeadline(c_, absl::Now() + timeout)
+ : mu_.ReaderLockWhenWithTimeout(c_, timeout);
+ mu_.ReaderUnlock();
+ return b;
+ }
+
+ void Await() {
+ absl::MutexLock lock(&mu_);
+ mu_.Await(c_);
+ }
+
+ void Signal(bool v) {
+ absl::MutexLock lock(&mu_);
+ b_ = v;
+ cv_.Signal();
+ }
+
+ bool WaitWithTimeout(absl::Duration timeout) {
+ absl::MutexLock lock(&mu_);
+ absl::Time deadline = absl::Now() + timeout;
+ if (use_deadline_) {
+ while (!b_ && !cv_.WaitWithDeadline(&mu_, deadline)) {
+ }
+ } else {
+ while (!b_ && !cv_.WaitWithTimeout(&mu_, timeout)) {
+ timeout = deadline - absl::Now(); // recompute timeout
+ }
+ }
+ return b_;
+ }
+
+ void Wait() {
+ absl::MutexLock lock(&mu_);
+ while (!b_) cv_.Wait(&mu_);
+ }
+
+ private:
+ const bool use_deadline_;
+
+ bool b_;
+ absl::Condition c_;
+ absl::CondVar cv_;
+ absl::Mutex mu_;
+};
+
+class OperationTimer {
+ public:
+ OperationTimer() : start_(absl::Now()) {}
+ absl::Duration Get() const { return absl::Now() - start_; }
+
+ private:
+ const absl::Time start_;
+};
+
+static void CheckResults(bool exp_result, bool act_result,
+ absl::Duration exp_duration,
+ absl::Duration act_duration) {
+ ABSL_RAW_CHECK(exp_result == act_result, "CheckResults failed");
+ // Allow for some worse-case scheduling delay and clock skew.
+ if ((exp_duration - absl::Milliseconds(40) > act_duration) ||
+ (exp_duration + absl::Milliseconds(150) < act_duration)) {
+ ABSL_RAW_LOG(FATAL, "CheckResults failed: operation took %s, expected %s",
+ absl::FormatDuration(act_duration).c_str(),
+ absl::FormatDuration(exp_duration).c_str());
+ }
+}
+
+static void TestAwaitTimeout(Cond *cp, absl::Duration timeout, bool exp_result,
+ absl::Duration exp_duration) {
+ OperationTimer t;
+ bool act_result = cp->AwaitWithTimeout(timeout);
+ CheckResults(exp_result, act_result, exp_duration, t.Get());
+}
+
+static void TestLockWhenTimeout(Cond *cp, absl::Duration timeout,
+ bool exp_result, absl::Duration exp_duration) {
+ OperationTimer t;
+ bool act_result = cp->LockWhenWithTimeout(timeout);
+ CheckResults(exp_result, act_result, exp_duration, t.Get());
+}
+
+static void TestReaderLockWhenTimeout(Cond *cp, absl::Duration timeout,
+ bool exp_result,
+ absl::Duration exp_duration) {
+ OperationTimer t;
+ bool act_result = cp->ReaderLockWhenWithTimeout(timeout);
+ CheckResults(exp_result, act_result, exp_duration, t.Get());
+}
+
+static void TestWaitTimeout(Cond *cp, absl::Duration timeout, bool exp_result,
+ absl::Duration exp_duration) {
+ OperationTimer t;
+ bool act_result = cp->WaitWithTimeout(timeout);
+ CheckResults(exp_result, act_result, exp_duration, t.Get());
+}
+
+// Tests with a negative timeout (deadline in the past), which should
+// immediately return the current state of the condition.
+static void TestNegativeTimeouts(absl::synchronization_internal::ThreadPool *tp,
+ Cond *cp) {
+ const absl::Duration negative = -absl::InfiniteDuration();
+ const absl::Duration immediate = absl::ZeroDuration();
+
+ // The condition is already true:
+ cp->Set(true);
+ TestAwaitTimeout(cp, negative, true, immediate);
+ TestLockWhenTimeout(cp, negative, true, immediate);
+ TestReaderLockWhenTimeout(cp, negative, true, immediate);
+ TestWaitTimeout(cp, negative, true, immediate);
+
+ // The condition becomes true, but the timeout has already expired:
+ const absl::Duration delay = absl::Milliseconds(200);
+ cp->Set(false);
+ ScheduleAfter(tp, std::bind(&Cond::Set, cp, true), 3 * delay);
+ TestAwaitTimeout(cp, negative, false, immediate);
+ TestLockWhenTimeout(cp, negative, false, immediate);
+ TestReaderLockWhenTimeout(cp, negative, false, immediate);
+ cp->Await(); // wait for the scheduled Set() to complete
+ cp->Set(false);
+ ScheduleAfter(tp, std::bind(&Cond::Signal, cp, true), delay);
+ TestWaitTimeout(cp, negative, false, immediate);
+ cp->Wait(); // wait for the scheduled Signal() to complete
+
+ // The condition never becomes true:
+ cp->Set(false);
+ TestAwaitTimeout(cp, negative, false, immediate);
+ TestLockWhenTimeout(cp, negative, false, immediate);
+ TestReaderLockWhenTimeout(cp, negative, false, immediate);
+ TestWaitTimeout(cp, negative, false, immediate);
+}
+
+// Tests with an infinite timeout (deadline in the infinite future), which
+// should only return when the condition becomes true.
+static void TestInfiniteTimeouts(absl::synchronization_internal::ThreadPool *tp,
+ Cond *cp) {
+ const absl::Duration infinite = absl::InfiniteDuration();
+ const absl::Duration immediate = absl::ZeroDuration();
+
+ // The condition is already true:
+ cp->Set(true);
+ TestAwaitTimeout(cp, infinite, true, immediate);
+ TestLockWhenTimeout(cp, infinite, true, immediate);
+ TestReaderLockWhenTimeout(cp, infinite, true, immediate);
+ TestWaitTimeout(cp, infinite, true, immediate);
+
+ // The condition becomes true before the (infinite) expiry:
+ const absl::Duration delay = absl::Milliseconds(200);
+ cp->Set(false);
+ ScheduleAfter(tp, std::bind(&Cond::Set, cp, true), delay);
+ TestAwaitTimeout(cp, infinite, true, delay);
+ cp->Set(false);
+ ScheduleAfter(tp, std::bind(&Cond::Set, cp, true), delay);
+ TestLockWhenTimeout(cp, infinite, true, delay);
+ cp->Set(false);
+ ScheduleAfter(tp, std::bind(&Cond::Set, cp, true), delay);
+ TestReaderLockWhenTimeout(cp, infinite, true, delay);
+ cp->Set(false);
+ ScheduleAfter(tp, std::bind(&Cond::Signal, cp, true), delay);
+ TestWaitTimeout(cp, infinite, true, delay);
+}
+
+// Tests with a (small) finite timeout (deadline soon), with the condition
+// becoming true both before and after its expiry.
+static void TestFiniteTimeouts(absl::synchronization_internal::ThreadPool *tp,
+ Cond *cp) {
+ const absl::Duration finite = absl::Milliseconds(400);
+ const absl::Duration immediate = absl::ZeroDuration();
+
+ // The condition is already true:
+ cp->Set(true);
+ TestAwaitTimeout(cp, finite, true, immediate);
+ TestLockWhenTimeout(cp, finite, true, immediate);
+ TestReaderLockWhenTimeout(cp, finite, true, immediate);
+ TestWaitTimeout(cp, finite, true, immediate);
+
+ // The condition becomes true before the expiry:
+ const absl::Duration delay1 = finite / 2;
+ cp->Set(false);
+ ScheduleAfter(tp, std::bind(&Cond::Set, cp, true), delay1);
+ TestAwaitTimeout(cp, finite, true, delay1);
+ cp->Set(false);
+ ScheduleAfter(tp, std::bind(&Cond::Set, cp, true), delay1);
+ TestLockWhenTimeout(cp, finite, true, delay1);
+ cp->Set(false);
+ ScheduleAfter(tp, std::bind(&Cond::Set, cp, true), delay1);
+ TestReaderLockWhenTimeout(cp, finite, true, delay1);
+ cp->Set(false);
+ ScheduleAfter(tp, std::bind(&Cond::Signal, cp, true), delay1);
+ TestWaitTimeout(cp, finite, true, delay1);
+
+ // The condition becomes true, but the timeout has already expired:
+ const absl::Duration delay2 = finite * 2;
+ cp->Set(false);
+ ScheduleAfter(tp, std::bind(&Cond::Set, cp, true), 3 * delay2);
+ TestAwaitTimeout(cp, finite, false, finite);
+ TestLockWhenTimeout(cp, finite, false, finite);
+ TestReaderLockWhenTimeout(cp, finite, false, finite);
+ cp->Await(); // wait for the scheduled Set() to complete
+ cp->Set(false);
+ ScheduleAfter(tp, std::bind(&Cond::Signal, cp, true), delay2);
+ TestWaitTimeout(cp, finite, false, finite);
+ cp->Wait(); // wait for the scheduled Signal() to complete
+
+ // The condition never becomes true:
+ cp->Set(false);
+ TestAwaitTimeout(cp, finite, false, finite);
+ TestLockWhenTimeout(cp, finite, false, finite);
+ TestReaderLockWhenTimeout(cp, finite, false, finite);
+ TestWaitTimeout(cp, finite, false, finite);
+}
+
+TEST(Mutex, Timeouts) {
+ auto tp = CreateDefaultPool();
+ for (bool use_deadline : {false, true}) {
+ Cond cond(use_deadline);
+ TestNegativeTimeouts(tp.get(), &cond);
+ TestInfiniteTimeouts(tp.get(), &cond);
+ TestFiniteTimeouts(tp.get(), &cond);
+ }
+}
+
+TEST(Mutex, Logging) {
+ // Allow user to look at logging output
+ absl::Mutex logged_mutex;
+ logged_mutex.EnableDebugLog("fido_mutex");
+ absl::CondVar logged_cv;
+ logged_cv.EnableDebugLog("rover_cv");
+ logged_mutex.Lock();
+ logged_cv.WaitWithTimeout(&logged_mutex, absl::Milliseconds(20));
+ logged_mutex.Unlock();
+ logged_mutex.ReaderLock();
+ logged_mutex.ReaderUnlock();
+ logged_mutex.Lock();
+ logged_mutex.Unlock();
+ logged_cv.Signal();
+ logged_cv.SignalAll();
+}
+
+// --------------------------------------------------------
+
+// Generate the vector of thread counts for tests parameterized on thread count.
+static std::vector<int> AllThreadCountValues() {
+ if (kExtendedTest) {
+ return {2, 4, 8, 10, 16, 20, 24, 30, 32};
+ }
+ return {2, 4, 10};
+}
+
+// A test fixture parameterized by thread count.
+class MutexVariableThreadCountTest : public ::testing::TestWithParam<int> {};
+
+// Instantiate the above with AllThreadCountOptions().
+INSTANTIATE_TEST_CASE_P(ThreadCounts, MutexVariableThreadCountTest,
+ ::testing::ValuesIn(AllThreadCountValues()),
+ ::testing::PrintToStringParamName());
+
+// Reduces iterations by some factor for slow platforms
+// (determined empirically).
+static int ScaleIterations(int x) {
+ // ABSL_MUTEX_READER_LOCK_IS_EXCLUSIVE is set in the implementation
+ // of Mutex that uses either std::mutex or pthread_mutex_t. Use
+ // these as keys to determine the slow implementation.
+#if defined(ABSL_MUTEX_READER_LOCK_IS_EXCLUSIVE)
+ return x / 10;
+#else
+ return x;
+#endif
+}
+
+TEST_P(MutexVariableThreadCountTest, Mutex) {
+ int threads = GetParam();
+ int iterations = ScaleIterations(10000000) / threads;
+ int operations = threads * iterations;
+ EXPECT_EQ(RunTest(&TestMu, threads, iterations, operations), operations);
+#if !defined(ABSL_MUTEX_ENABLE_INVARIANT_DEBUGGING_NOT_IMPLEMENTED)
+ iterations = std::min(iterations, 10);
+ operations = threads * iterations;
+ EXPECT_EQ(RunTestWithInvariantDebugging(&TestMu, threads, iterations,
+ operations, CheckSumG0G1),
+ operations);
+#endif
+}
+
+TEST_P(MutexVariableThreadCountTest, Try) {
+ int threads = GetParam();
+ int iterations = 1000000 / threads;
+ int operations = iterations * threads;
+ EXPECT_EQ(RunTest(&TestTry, threads, iterations, operations), operations);
+#if !defined(ABSL_MUTEX_ENABLE_INVARIANT_DEBUGGING_NOT_IMPLEMENTED)
+ iterations = std::min(iterations, 10);
+ operations = threads * iterations;
+ EXPECT_EQ(RunTestWithInvariantDebugging(&TestTry, threads, iterations,
+ operations, CheckSumG0G1),
+ operations);
+#endif
+}
+
+TEST_P(MutexVariableThreadCountTest, R20ms) {
+ int threads = GetParam();
+ int iterations = 100;
+ int operations = iterations * threads;
+ EXPECT_EQ(RunTest(&TestR20ms, threads, iterations, operations), 0);
+}
+
+TEST_P(MutexVariableThreadCountTest, RW) {
+ int threads = GetParam();
+ int iterations = ScaleIterations(20000000) / threads;
+ int operations = iterations * threads;
+ EXPECT_EQ(RunTest(&TestRW, threads, iterations, operations), operations / 2);
+#if !defined(ABSL_MUTEX_ENABLE_INVARIANT_DEBUGGING_NOT_IMPLEMENTED)
+ iterations = std::min(iterations, 10);
+ operations = threads * iterations;
+ EXPECT_EQ(RunTestWithInvariantDebugging(&TestRW, threads, iterations,
+ operations, CheckSumG0G1),
+ operations / 2);
+#endif
+}
+
+TEST_P(MutexVariableThreadCountTest, Await) {
+ int threads = GetParam();
+ int iterations = ScaleIterations(500000);
+ int operations = iterations;
+ EXPECT_EQ(RunTest(&TestAwait, threads, iterations, operations), operations);
+}
+
+TEST_P(MutexVariableThreadCountTest, SignalAll) {
+ int threads = GetParam();
+ int iterations = 200000 / threads;
+ int operations = iterations;
+ EXPECT_EQ(RunTest(&TestSignalAll, threads, iterations, operations),
+ operations);
+}
+
+TEST(Mutex, Signal) {
+ int threads = 2; // TestSignal must use two threads
+ int iterations = 200000;
+ int operations = iterations;
+ EXPECT_EQ(RunTest(&TestSignal, threads, iterations, operations), operations);
+}
+
+TEST(Mutex, Timed) {
+ int threads = 10; // Use a fixed thread count of 10
+ int iterations = 1000;
+ int operations = iterations;
+ EXPECT_EQ(RunTest(&TestCVTimeout, threads, iterations, operations),
+ operations);
+}
+
+TEST(Mutex, CVTime) {
+ int threads = 10; // Use a fixed thread count of 10
+ int iterations = 1;
+ EXPECT_EQ(RunTest(&TestCVTime, threads, iterations, 1),
+ threads * iterations);
+}
+
+TEST(Mutex, MuTime) {
+ int threads = 10; // Use a fixed thread count of 10
+ int iterations = 1;
+ EXPECT_EQ(RunTest(&TestMuTime, threads, iterations, 1), threads * iterations);
+}
+
+} // namespace
http://git-wip-us.apache.org/repos/asf/marmotta/blob/0eb556da/libraries/ostrich/backend/3rdparty/abseil/absl/synchronization/notification.cc
----------------------------------------------------------------------
diff --git a/libraries/ostrich/backend/3rdparty/abseil/absl/synchronization/notification.cc b/libraries/ostrich/backend/3rdparty/abseil/absl/synchronization/notification.cc
new file mode 100644
index 0000000..ed8cc90
--- /dev/null
+++ b/libraries/ostrich/backend/3rdparty/abseil/absl/synchronization/notification.cc
@@ -0,0 +1,84 @@
+// Copyright 2017 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "absl/synchronization/notification.h"
+
+#include <atomic>
+
+#include "absl/base/attributes.h"
+#include "absl/base/internal/raw_logging.h"
+#include "absl/synchronization/mutex.h"
+#include "absl/time/time.h"
+
+namespace absl {
+void Notification::Notify() {
+ MutexLock l(&this->mutex_);
+
+#ifndef NDEBUG
+ if (ABSL_PREDICT_FALSE(notified_yet_.load(std::memory_order_relaxed))) {
+ ABSL_RAW_LOG(
+ FATAL,
+ "Notify() method called more than once for Notification object %p",
+ static_cast<void *>(this));
+ }
+#endif
+
+ notified_yet_.store(true, std::memory_order_release);
+}
+
+Notification::~Notification() {
+ // Make sure that the thread running Notify() exits before the object is
+ // destructed.
+ MutexLock l(&this->mutex_);
+}
+
+static inline bool HasBeenNotifiedInternal(
+ const std::atomic<bool> *notified_yet) {
+ return notified_yet->load(std::memory_order_acquire);
+}
+
+bool Notification::HasBeenNotified() const {
+ return HasBeenNotifiedInternal(&this->notified_yet_);
+}
+
+void Notification::WaitForNotification() const {
+ if (!HasBeenNotifiedInternal(&this->notified_yet_)) {
+ this->mutex_.LockWhen(Condition(&HasBeenNotifiedInternal,
+ &this->notified_yet_));
+ this->mutex_.Unlock();
+ }
+}
+
+bool Notification::WaitForNotificationWithTimeout(
+ absl::Duration timeout) const {
+ bool notified = HasBeenNotifiedInternal(&this->notified_yet_);
+ if (!notified) {
+ notified = this->mutex_.LockWhenWithTimeout(
+ Condition(&HasBeenNotifiedInternal, &this->notified_yet_), timeout);
+ this->mutex_.Unlock();
+ }
+ return notified;
+}
+
+bool Notification::WaitForNotificationWithDeadline(absl::Time deadline) const {
+ bool notified = HasBeenNotifiedInternal(&this->notified_yet_);
+ if (!notified) {
+ notified = this->mutex_.LockWhenWithDeadline(
+ Condition(&HasBeenNotifiedInternal, &this->notified_yet_), deadline);
+ this->mutex_.Unlock();
+ }
+ return notified;
+}
+
+} // namespace absl
http://git-wip-us.apache.org/repos/asf/marmotta/blob/0eb556da/libraries/ostrich/backend/3rdparty/abseil/absl/synchronization/notification.h
----------------------------------------------------------------------
diff --git a/libraries/ostrich/backend/3rdparty/abseil/absl/synchronization/notification.h b/libraries/ostrich/backend/3rdparty/abseil/absl/synchronization/notification.h
new file mode 100644
index 0000000..107932f
--- /dev/null
+++ b/libraries/ostrich/backend/3rdparty/abseil/absl/synchronization/notification.h
@@ -0,0 +1,112 @@
+// Copyright 2017 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// -----------------------------------------------------------------------------
+// notification.h
+// -----------------------------------------------------------------------------
+//
+// This header file defines a `Notification` abstraction, which allows threads
+// to receive notification of a single occurrence of a single event.
+//
+// The `Notification` object maintains a private boolean "notified" state that
+// transitions to `true` at most once. The `Notification` class provides the
+// following primary member functions:
+// * `HasBeenNotified() `to query its state
+// * `WaitForNotification*()` to have threads wait until the "notified" state
+// is `true`.
+// * `Notify()` to set the notification's "notified" state to `true` and
+// notify all waiting threads that the event has occurred.
+// This method may only be called once.
+//
+// Note that while `Notify()` may only be called once, it is perfectly valid to
+// call any of the `WaitForNotification*()` methods multiple times, from
+// multiple threads -- even after the notification's "notified" state has been
+// set -- in which case those methods will immediately return.
+//
+// Note that the lifetime of a `Notification` requires careful consideration;
+// it might not be safe to destroy a notification after calling `Notify()` since
+// it is still legal for other threads to call `WaitForNotification*()` methods
+// on the notification. However, observers responding to a "notified" state of
+// `true` can safely delete the notification without interfering with the call
+// to `Notify()` in the other thread.
+//
+// Memory ordering: For any threads X and Y, if X calls `Notify()`, then any
+// action taken by X before it calls `Notify()` is visible to thread Y after:
+// * Y returns from `WaitForNotification()`, or
+// * Y receives a `true` return value from either `HasBeenNotified()` or
+// `WaitForNotificationWithTimeout()`.
+
+#ifndef ABSL_SYNCHRONIZATION_NOTIFICATION_H_
+#define ABSL_SYNCHRONIZATION_NOTIFICATION_H_
+
+#include <atomic>
+
+#include "absl/synchronization/mutex.h"
+#include "absl/time/time.h"
+
+namespace absl {
+
+// -----------------------------------------------------------------------------
+// Notification
+// -----------------------------------------------------------------------------
+class Notification {
+ public:
+ // Initializes the "notified" state to unnotified.
+ Notification() : notified_yet_(false) {}
+ explicit Notification(bool prenotify) : notified_yet_(prenotify) {}
+ Notification(const Notification&) = delete;
+ Notification& operator=(const Notification&) = delete;
+ ~Notification();
+
+ // Notification::HasBeenNotified()
+ //
+ // Returns the value of the notification's internal "notified" state.
+ bool HasBeenNotified() const;
+
+ // Notification::WaitForNotification()
+ //
+ // Blocks the calling thread until the notification's "notified" state is
+ // `true`. Note that if `Notify()` has been previously called on this
+ // notification, this function will immediately return.
+ void WaitForNotification() const;
+
+ // Notification::WaitForNotificationWithTimeout()
+ //
+ // Blocks until either the notification's "notified" state is `true` (which
+ // may occur immediately) or the timeout has elapsed, returning the value of
+ // its "notified" state in either case.
+ bool WaitForNotificationWithTimeout(absl::Duration timeout) const;
+
+ // Notification::WaitForNotificationWithDeadline()
+ //
+ // Blocks until either the notification's "notified" state is `true` (which
+ // may occur immediately) or the deadline has expired, returning the value of
+ // its "notified" state in either case.
+ bool WaitForNotificationWithDeadline(absl::Time deadline) const;
+
+ // Notification::Notify()
+ //
+ // Sets the "notified" state of this notification to `true` and wakes waiting
+ // threads. Note: do not call `Notify()` multiple times on the same
+ // `Notification`; calling `Notify()` more than once on the same notification
+ // results in undefined behavior.
+ void Notify();
+
+ private:
+ mutable Mutex mutex_;
+ std::atomic<bool> notified_yet_; // written under mutex_
+};
+
+} // namespace absl
+#endif // ABSL_SYNCHRONIZATION_NOTIFICATION_H_
http://git-wip-us.apache.org/repos/asf/marmotta/blob/0eb556da/libraries/ostrich/backend/3rdparty/abseil/absl/synchronization/notification_test.cc
----------------------------------------------------------------------
diff --git a/libraries/ostrich/backend/3rdparty/abseil/absl/synchronization/notification_test.cc b/libraries/ostrich/backend/3rdparty/abseil/absl/synchronization/notification_test.cc
new file mode 100644
index 0000000..9b3b6a5
--- /dev/null
+++ b/libraries/ostrich/backend/3rdparty/abseil/absl/synchronization/notification_test.cc
@@ -0,0 +1,124 @@
+// Copyright 2017 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "absl/synchronization/notification.h"
+
+#include <thread> // NOLINT(build/c++11)
+#include <vector>
+
+#include "gtest/gtest.h"
+#include "absl/synchronization/mutex.h"
+
+namespace absl {
+
+// A thread-safe class that holds a counter.
+class ThreadSafeCounter {
+ public:
+ ThreadSafeCounter() : count_(0) {}
+
+ void Increment() {
+ MutexLock lock(&mutex_);
+ ++count_;
+ }
+
+ int Get() const {
+ MutexLock lock(&mutex_);
+ return count_;
+ }
+
+ void WaitUntilGreaterOrEqual(int n) {
+ MutexLock lock(&mutex_);
+ auto cond = [this, n]() { return count_ >= n; };
+ mutex_.Await(Condition(&cond));
+ }
+
+ private:
+ mutable Mutex mutex_;
+ int count_;
+};
+
+// Runs the |i|'th worker thread for the tests in BasicTests(). Increments the
+// |ready_counter|, waits on the |notification|, and then increments the
+// |done_counter|.
+static void RunWorker(int i, ThreadSafeCounter* ready_counter,
+ Notification* notification,
+ ThreadSafeCounter* done_counter) {
+ ready_counter->Increment();
+ notification->WaitForNotification();
+ done_counter->Increment();
+}
+
+// Tests that the |notification| properly blocks and awakens threads. Assumes
+// that the |notification| is not yet triggered. If |notify_before_waiting| is
+// true, the |notification| is triggered before any threads are created, so the
+// threads never block in WaitForNotification(). Otherwise, the |notification|
+// is triggered at a later point when most threads are likely to be blocking in
+// WaitForNotification().
+static void BasicTests(bool notify_before_waiting, Notification* notification) {
+ EXPECT_FALSE(notification->HasBeenNotified());
+ EXPECT_FALSE(
+ notification->WaitForNotificationWithTimeout(absl::Milliseconds(0)));
+ EXPECT_FALSE(notification->WaitForNotificationWithDeadline(absl::Now()));
+
+ absl::Time start = absl::Now();
+ EXPECT_FALSE(
+ notification->WaitForNotificationWithTimeout(absl::Milliseconds(50)));
+ EXPECT_LE(start + absl::Milliseconds(50), absl::Now());
+
+ ThreadSafeCounter ready_counter;
+ ThreadSafeCounter done_counter;
+
+ if (notify_before_waiting) {
+ notification->Notify();
+ }
+
+ // Create a bunch of threads that increment the |done_counter| after being
+ // notified.
+ const int kNumThreads = 10;
+ std::vector<std::thread> workers;
+ for (int i = 0; i < kNumThreads; ++i) {
+ workers.push_back(std::thread(&RunWorker, i, &ready_counter, notification,
+ &done_counter));
+ }
+
+ if (!notify_before_waiting) {
+ ready_counter.WaitUntilGreaterOrEqual(kNumThreads);
+
+ // Workers have not been notified yet, so the |done_counter| should be
+ // unmodified.
+ EXPECT_EQ(0, done_counter.Get());
+
+ notification->Notify();
+ }
+
+ // After notifying and then joining the workers, both counters should be
+ // fully incremented.
+ notification->WaitForNotification(); // should exit immediately
+ EXPECT_TRUE(notification->HasBeenNotified());
+ EXPECT_TRUE(notification->WaitForNotificationWithTimeout(absl::Seconds(0)));
+ EXPECT_TRUE(notification->WaitForNotificationWithDeadline(absl::Now()));
+ for (std::thread& worker : workers) {
+ worker.join();
+ }
+ EXPECT_EQ(kNumThreads, ready_counter.Get());
+ EXPECT_EQ(kNumThreads, done_counter.Get());
+}
+
+TEST(NotificationTest, SanityTest) {
+ Notification local_notification1, local_notification2;
+ BasicTests(false, &local_notification1);
+ BasicTests(true, &local_notification2);
+}
+
+} // namespace absl
http://git-wip-us.apache.org/repos/asf/marmotta/blob/0eb556da/libraries/ostrich/backend/3rdparty/abseil/absl/time/BUILD.bazel
----------------------------------------------------------------------
diff --git a/libraries/ostrich/backend/3rdparty/abseil/absl/time/BUILD.bazel b/libraries/ostrich/backend/3rdparty/abseil/absl/time/BUILD.bazel
new file mode 100644
index 0000000..d4f653f
--- /dev/null
+++ b/libraries/ostrich/backend/3rdparty/abseil/absl/time/BUILD.bazel
@@ -0,0 +1,97 @@
+#
+# Copyright 2017 The Abseil Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+load(
+ "//absl:copts.bzl",
+ "ABSL_DEFAULT_COPTS",
+ "ABSL_TEST_COPTS",
+)
+
+package(default_visibility = ["//visibility:public"])
+
+licenses(["notice"]) # Apache 2.0
+
+cc_library(
+ name = "time",
+ srcs = [
+ "clock.cc",
+ "duration.cc",
+ "format.cc",
+ "internal/get_current_time_ios.inc",
+ "internal/get_current_time_posix.inc",
+ "internal/get_current_time_windows.inc",
+ "time.cc",
+ ],
+ hdrs = [
+ "clock.h",
+ "time.h",
+ ],
+ copts = ABSL_DEFAULT_COPTS,
+ deps = [
+ "//absl/base",
+ "//absl/base:core_headers",
+ "//absl/numeric:int128",
+ "//absl/time/internal/cctz:civil_time",
+ "//absl/time/internal/cctz:time_zone",
+ ],
+)
+
+cc_library(
+ name = "test_util",
+ srcs = [
+ "internal/test_util.cc",
+ "internal/zoneinfo.inc",
+ ],
+ hdrs = ["internal/test_util.h"],
+ copts = ABSL_DEFAULT_COPTS,
+ visibility = [
+ "//absl/time:__pkg__",
+ ],
+ deps = [
+ ":time",
+ "//absl/base",
+ "//absl/time/internal/cctz:time_zone",
+ ],
+)
+
+cc_test(
+ name = "time_test",
+ srcs = [
+ "clock_test.cc",
+ "duration_test.cc",
+ "format_test.cc",
+ "time_norm_test.cc",
+ "time_test.cc",
+ "time_zone_test.cc",
+ ],
+ copts = ABSL_TEST_COPTS,
+ tags = [
+ "no_test_android_arm",
+ "no_test_android_arm64",
+ "no_test_android_x86",
+ "no_test_ios_x86_64",
+ "no_test_loonix",
+ ],
+ deps = [
+ ":test_util",
+ ":time",
+ "//absl/base",
+ "//absl/base:config",
+ "//absl/base:core_headers",
+ "//absl/time/internal/cctz:time_zone",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
http://git-wip-us.apache.org/repos/asf/marmotta/blob/0eb556da/libraries/ostrich/backend/3rdparty/abseil/absl/time/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/libraries/ostrich/backend/3rdparty/abseil/absl/time/CMakeLists.txt b/libraries/ostrich/backend/3rdparty/abseil/absl/time/CMakeLists.txt
new file mode 100644
index 0000000..72bb4d2
--- /dev/null
+++ b/libraries/ostrich/backend/3rdparty/abseil/absl/time/CMakeLists.txt
@@ -0,0 +1,96 @@
+#
+# Copyright 2017 The Abseil Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+list(APPEND TIME_PUBLIC_HEADERS
+ "clock.h"
+ "time.h"
+)
+
+
+list(APPEND TIME_INTERNAL_HEADERS
+ "internal/test_util.h"
+ "internal/cctz/include/cctz/civil_time.h"
+ "internal/cctz/include/cctz/civil_time_detail.h"
+ "internal/cctz/include/cctz/time_zone.h"
+ "internal/cctz/include/cctz/zone_info_source.h"
+)
+
+list(APPEND TIME_SRC
+ "time.cc"
+ "clock.cc"
+ "duration.cc"
+ "format.cc"
+ "internal/cctz/src/civil_time_detail.cc"
+ "internal/cctz/src/time_zone_fixed.cc"
+ "internal/cctz/src/time_zone_fixed.h"
+ "internal/cctz/src/time_zone_format.cc"
+ "internal/cctz/src/time_zone_if.cc"
+ "internal/cctz/src/time_zone_if.h"
+ "internal/cctz/src/time_zone_impl.cc"
+ "internal/cctz/src/time_zone_impl.h"
+ "internal/cctz/src/time_zone_info.cc"
+ "internal/cctz/src/time_zone_info.h"
+ "internal/cctz/src/time_zone_libc.cc"
+ "internal/cctz/src/time_zone_libc.h"
+ "internal/cctz/src/time_zone_lookup.cc"
+ "internal/cctz/src/time_zone_posix.cc"
+ "internal/cctz/src/time_zone_posix.h"
+ "internal/cctz/src/tzfile.h"
+ "internal/cctz/src/zone_info_source.cc"
+ ${TIME_PUBLIC_HEADERS}
+ ${TIME_INTERNAL_HEADERS}
+)
+set(TIME_PUBLIC_LIBRARIES absl::base absl::stacktrace absl::int128)
+
+absl_library(
+ TARGET
+ absl_time
+ SOURCES
+ ${TIME_SRC}
+ PUBLIC_LIBRARIES
+ ${TIME_PUBLIC_LIBRARIES}
+ EXPORT_NAME
+ time
+)
+
+
+
+#
+## TESTS
+#
+
+# test time_test
+list(APPEND TIME_TEST_SRC
+ "time_test.cc"
+ "clock_test.cc"
+ "duration_test.cc"
+ "format_test.cc"
+ "time_norm_test.cc"
+ "time_test.cc"
+ "time_zone_test.cc"
+ "internal/test_util.cc"
+)
+set(TIME_TEST_PUBLIC_LIBRARIES absl::time)
+
+absl_test(
+ TARGET
+ time_test
+ SOURCES
+ ${TIME_TEST_SRC}
+ PUBLIC_LIBRARIES
+ ${TIME_TEST_PUBLIC_LIBRARIES}
+)
+