chromium/third_party/abseil-cpp/absl/synchronization/mutex_test.cc

// 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
//
//      https://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 <type_traits>
#include <vector>

#include "gtest/gtest.h"
#include "absl/base/attributes.h"
#include "absl/base/config.h"
#include "absl/base/internal/sysinfo.h"
#include "absl/log/check.h"
#include "absl/log/log.h"
#include "absl/memory/memory.h"
#include "absl/synchronization/internal/create_thread_identity.h"
#include "absl/synchronization/internal/thread_pool.h"
#include "absl/time/clock.h"
#include "absl/time/time.h"

#ifdef ABSL_HAVE_PTHREAD_GETSCHEDPARAM
#include <pthread.h>
#include <string.h>
#endif

namespace {

// TODO(dmauro): Replace with a commandline flag.
static constexpr bool kExtendedTest =;

std::unique_ptr<absl::synchronization_internal::ThreadPool> CreatePool(
    int threads) {}

std::unique_ptr<absl::synchronization_internal::ThreadPool>
CreateDefaultPool() {}

// 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,
                          absl::Duration after,
                          const std::function<void()> &func) {}

struct ScopedInvariantDebugging {};

struct TestContext {};

// To test whether the invariant check call occurs
static std::atomic<bool> invariant_checked;

static bool GetInvariantChecked() {}

static void SetInvariantChecked(bool new_value) {}

static void CheckSumG0G1(void *v) {}

static void TestMu(TestContext *cxt, int c) {}

static void TestTry(TestContext *cxt, int c) {}

static void TestR20ms(TestContext *cxt, int c) {}

static void TestRW(TestContext *cxt, int c) {}

struct MyContext {};

bool MyContext::MyTurn() {}

static void TestAwait(TestContext *cxt, int c) {}

static void TestSignalAll(TestContext *cxt, int c) {}

static void TestSignal(TestContext *cxt, int c) {}

static void TestCVTimeout(TestContext *cxt, int c) {}

static bool G0GE2(TestContext *cxt) {}

static void TestTime(TestContext *cxt, int c, bool use_cv) {}

static void TestMuTime(TestContext *cxt, int c) {}

static void TestCVTime(TestContext *cxt, int c) {}

static void EndTest(int *c0, int *c1, absl::Mutex *mu, absl::CondVar *cv,
                    const std::function<void(int)> &cb) {}

// Code common to RunTest() and RunTestWithInvariantDebugging().
static int RunTestCommon(TestContext *cxt, void (*test)(TestContext *cxt, int),
                         int threads, int iterations, int operations) {}

// Basis for the parameterized tests configured below.
static int RunTest(void (*test)(TestContext *cxt, int), int threads,
                   int iterations, int 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 *)) {}
#endif

// --------------------------------------------------------
// Test for fix of bug in TryRemove()
struct TimeoutBugStruct {};

static void WaitForA(TimeoutBugStruct *x) {}

static bool NoAWaiters(TimeoutBugStruct *x) {}

// Test that a CondVar.Wait(&mutex) can un-block a call to mutex.Await() in
// another thread.
TEST(Mutex, CondVarWaitSignalsAwait) {}

// Test that a CondVar.WaitWithTimeout(&mutex) can un-block a call to
// mutex.Await() in another thread.
TEST(Mutex, CondVarWaitWithTimeoutSignalsAwait) {}

// Test for regression of a bug in loop of TryRemove()
TEST(Mutex, MutexTimeoutBug) {}

struct CondVarWaitDeadlock : testing::TestWithParam<int> {};

// 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) {}

INSTANTIATE_TEST_SUITE_P();

// --------------------------------------------------------
// 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 {};

// Test for regression of a bug in loop of DequeueAllWakeable()
static void AcquireAsReader(DequeueAllWakeableBugStruct *x) {}

// Test for regression of a bug in loop of DequeueAllWakeable()
TEST(Mutex, MutexReaderWakeupBug) {}

struct LockWhenTestStruct {};

static bool LockWhenTestIsCond(LockWhenTestStruct *s) {}

static void LockWhenTestWaitForIsCond(LockWhenTestStruct *s) {}

TEST(Mutex, LockWhen) {}

TEST(Mutex, LockWhenGuard) {}

// --------------------------------------------------------
// 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 {};

// L >= mu, L < mu_waiting_on_cond
static bool IsCond(void *v) {}

// L >= mu
static bool AllDone(void *v) {}

// L={}
static void WaitForCond(ReaderDecrementBugStruct *x) {}

// L={}
static void GetReadLock(ReaderDecrementBugStruct *x) {}

// Test for reader counter being decremented incorrectly by waiter
// with false condition.
TEST(Mutex, MutexReaderDecrementBug) ABSL_NO_THREAD_SAFETY_ANALYSIS {}
#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).
#ifdef ABSL_HAVE_THREAD_SANITIZER
// TSAN reports errors when locked Mutexes are destroyed.
TEST(Mutex, DISABLED_LockedMutexDestructionBug) ABSL_NO_THREAD_SAFETY_ANALYSIS {
#else
TEST(Mutex, LockedMutexDestructionBug) ABSL_NO_THREAD_SAFETY_ANALYSIS {}

// Some functions taking pointers to non-const.
bool Equals42(int *p) {}
bool Equals43(int *p) {}

// Some functions taking pointers to const.
bool ConstEquals42(const int *p) {}
bool ConstEquals43(const int *p) {}

// Some function templates taking pointers. Note it's possible for `T` to be
// deduced as non-const or const, which creates the potential for ambiguity,
// but which the implementation is careful to avoid.
template <typename T>
bool TemplateEquals42(T *p) {}
template <typename T>
bool TemplateEquals43(T *p) {}

TEST(Mutex, FunctionPointerCondition) {}

// Example base and derived class for use in predicates and test below. Not a
// particularly realistic example, but it suffices for testing purposes.
struct Base {};
struct Derived : Base {};

// Some functions taking pointer to non-const `Base`.
bool BaseEquals42(Base *p) {}
bool BaseEquals43(Base *p) {}

// Some functions taking pointer to const `Base`.
bool ConstBaseEquals42(const Base *p) {}
bool ConstBaseEquals43(const Base *p) {}

TEST(Mutex, FunctionPointerConditionWithDerivedToBaseConversion) {}

struct Constable {};

TEST(Mutex, FunctionPointerConditionWithConstMethod) {}

struct True {};

struct DerivedTrue : True {};

TEST(Mutex, FunctorCondition) {}

TEST(Mutex, ConditionSwap) {}

// --------------------------------------------------------
// 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) {}

static bool IntIsZero(int *x) {}

// Test for reader waiting condition variable when there are other readers
// but no waiters.
TEST(Mutex, TestReaderOnCondVar) {}

// --------------------------------------------------------
struct AcquireFromConditionStruct {};

static bool ConditionWithAcquire(AcquireFromConditionStruct *x) {}

static void WaitForCond2(AcquireFromConditionStruct *x) {}

// Test for Condition whose function acquires other Mutexes
TEST(Mutex, AcquireFromCondition) {}

TEST(Mutex, DeadlockDetector) {}

// 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 {};
const char ScopedDisableBazelTestWarnings::kVarName[] =;

#ifdef ABSL_HAVE_THREAD_SANITIZER
// This test intentionally creates deadlocks to test the deadlock detector.
TEST(Mutex, DISABLED_DeadlockDetectorBazelWarning) {
#else
TEST(Mutex, DeadlockDetectorBazelWarning) {}

TEST(Mutex, DeadlockDetectorLongCycle) {}

// 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, DeadlockDetectorStressTest) ABSL_NO_THREAD_SAFETY_ANALYSIS {}

#ifdef ABSL_HAVE_THREAD_SANITIZER
// TSAN reports errors when locked Mutexes are destroyed.
TEST(Mutex, DISABLED_DeadlockIdBug) ABSL_NO_THREAD_SAFETY_ANALYSIS {
#else
TEST(Mutex, DeadlockIdBug) ABSL_NO_THREAD_SAFETY_ANALYSIS {}

// --------------------------------------------------------
// 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.

static absl::Duration TimeoutTestAllowedSchedulingDelay() {}

// Returns true if `actual_delay` is close enough to `expected_delay` to pass
// the timeouts/deadlines test.  Otherwise, logs warnings and returns false.
ABSL_MUST_USE_RESULT
static bool DelayIsWithinBounds(absl::Duration expected_delay,
                                absl::Duration actual_delay) {}

// Parameters for TimeoutTest, below.
struct TimeoutTestParam {};

// Print a `TimeoutTestParam` to a debug log.
std::ostream &operator<<(std::ostream &os, const TimeoutTestParam &param) {}

// Like `thread::Executor::ScheduleAt` except:
// a) Delays zero or negative are executed immediately in the current thread.
// b) Infinite delays are never scheduled.
// c) Calls this test's `ScheduleAt` helper instead of using `pool` directly.
static void RunAfterDelay(absl::Duration delay,
                          absl::synchronization_internal::ThreadPool *pool,
                          const std::function<void()> &callback) {}

class TimeoutTest : public ::testing::Test,
                    public ::testing::WithParamInterface<TimeoutTestParam> {};

std::vector<TimeoutTestParam> MakeTimeoutTestParamValues() {}

// Instantiate `TimeoutTest` with `MakeTimeoutTestParamValues()`.
INSTANTIATE_TEST_SUITE_P();

TEST_P(TimeoutTest, Await) {}

TEST_P(TimeoutTest, LockWhen) {}

TEST_P(TimeoutTest, ReaderLockWhen) {}

TEST_P(TimeoutTest, Wait) {}

TEST(Mutex, Logging) {}

TEST(Mutex, LoggingAddressReuse) {}

TEST(Mutex, LoggingBankrupcy) {}

TEST(Mutex, SynchEventRace) {}

// --------------------------------------------------------

// Generate the vector of thread counts for tests parameterized on thread count.
static std::vector<int> AllThreadCountValues() {}

// A test fixture parameterized by thread count.
class MutexVariableThreadCountTest : public ::testing::TestWithParam<int> {};

// Instantiate the above with AllThreadCountOptions().
INSTANTIATE_TEST_SUITE_P();

// Reduces iterations by some factor for slow platforms
// (determined empirically).
static int ScaleIterations(int x) {}

TEST_P(MutexVariableThreadCountTest, Mutex) {}

TEST_P(MutexVariableThreadCountTest, Try) {}

TEST_P(MutexVariableThreadCountTest, R20ms) {}

TEST_P(MutexVariableThreadCountTest, RW) {}

TEST_P(MutexVariableThreadCountTest, Await) {}

TEST_P(MutexVariableThreadCountTest, SignalAll) {}

TEST(Mutex, Signal) {}

TEST(Mutex, Timed) {}

TEST(Mutex, CVTime) {}

TEST(Mutex, MuTime) {}

TEST(Mutex, SignalExitedThread) {}

TEST(Mutex, WriterPriority) {}

#ifdef ABSL_HAVE_PTHREAD_GETSCHEDPARAM
TEST(Mutex, CondVarPriority) {}
#endif

TEST(Mutex, LockWhenWithTimeoutResult) {}

}  // namespace