blob: da138961d059399f4488cee4fe13deda13b0c8b2 [file] [log] [blame]
// Copyright 2023 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/internal/waiter.h"
#include <ctime>
#include <iostream>
#include <ostream>
#include "absl/base/config.h"
#include "absl/random/random.h"
#include "absl/synchronization/internal/create_thread_identity.h"
#include "absl/synchronization/internal/futex_waiter.h"
#include "absl/synchronization/internal/kernel_timeout.h"
#include "absl/synchronization/internal/pthread_waiter.h"
#include "absl/synchronization/internal/sem_waiter.h"
#include "absl/synchronization/internal/stdcpp_waiter.h"
#include "absl/synchronization/internal/thread_pool.h"
#include "absl/synchronization/internal/win32_waiter.h"
#include "absl/time/clock.h"
#include "absl/time/time.h"
#include "gtest/gtest.h"
// Test go/btm support by randomizing the value of clock_gettime() for
// CLOCK_MONOTONIC. This works by overriding a weak symbol in glibc.
// We should be resistant to this randomization when !SupportsSteadyClock().
#if defined(__GOOGLE_GRTE_VERSION__) && \
!defined(ABSL_HAVE_ADDRESS_SANITIZER) && \
!defined(ABSL_HAVE_MEMORY_SANITIZER) && \
!defined(ABSL_HAVE_THREAD_SANITIZER)
extern "C" int __clock_gettime(clockid_t c, struct timespec* ts);
extern "C" int clock_gettime(clockid_t c, struct timespec* ts) {
if (c == CLOCK_MONOTONIC &&
!absl::synchronization_internal::KernelTimeout::SupportsSteadyClock()) {
thread_local absl::BitGen gen; // NOLINT
ts->tv_sec = absl::Uniform(gen, 0, 1'000'000'000);
ts->tv_nsec = absl::Uniform(gen, 0, 1'000'000'000);
return 0;
}
return __clock_gettime(c, ts);
}
#endif
namespace {
TEST(Waiter, PrintPlatformImplementation) {
// Allows us to verify that the platform is using the expected implementation.
std::cout << absl::synchronization_internal::Waiter::kName << std::endl;
}
template <typename T>
class WaiterTest : public ::testing::Test {
public:
// Waiter implementations assume that a ThreadIdentity has already been
// created.
WaiterTest() {
absl::synchronization_internal::GetOrCreateCurrentThreadIdentity();
}
};
TYPED_TEST_SUITE_P(WaiterTest);
absl::Duration WithTolerance(absl::Duration d) { return d * 0.95; }
TYPED_TEST_P(WaiterTest, WaitNoTimeout) {
absl::synchronization_internal::ThreadPool tp(1);
TypeParam waiter;
tp.Schedule([&]() {
// Include some `Poke()` calls to ensure they don't cause `waiter` to return
// from `Wait()`.
waiter.Poke();
absl::SleepFor(absl::Seconds(1));
waiter.Poke();
absl::SleepFor(absl::Seconds(1));
waiter.Post();
});
absl::Time start = absl::Now();
EXPECT_TRUE(
waiter.Wait(absl::synchronization_internal::KernelTimeout::Never()));
absl::Duration waited = absl::Now() - start;
EXPECT_GE(waited, WithTolerance(absl::Seconds(2)));
}
TYPED_TEST_P(WaiterTest, WaitDurationWoken) {
absl::synchronization_internal::ThreadPool tp(1);
TypeParam waiter;
tp.Schedule([&]() {
// Include some `Poke()` calls to ensure they don't cause `waiter` to return
// from `Wait()`.
waiter.Poke();
absl::SleepFor(absl::Milliseconds(500));
waiter.Post();
});
absl::Time start = absl::Now();
EXPECT_TRUE(waiter.Wait(
absl::synchronization_internal::KernelTimeout(absl::Seconds(10))));
absl::Duration waited = absl::Now() - start;
EXPECT_GE(waited, WithTolerance(absl::Milliseconds(500)));
EXPECT_LT(waited, absl::Seconds(2));
}
TYPED_TEST_P(WaiterTest, WaitTimeWoken) {
absl::synchronization_internal::ThreadPool tp(1);
TypeParam waiter;
tp.Schedule([&]() {
// Include some `Poke()` calls to ensure they don't cause `waiter` to return
// from `Wait()`.
waiter.Poke();
absl::SleepFor(absl::Milliseconds(500));
waiter.Post();
});
absl::Time start = absl::Now();
EXPECT_TRUE(waiter.Wait(absl::synchronization_internal::KernelTimeout(
start + absl::Seconds(10))));
absl::Duration waited = absl::Now() - start;
EXPECT_GE(waited, WithTolerance(absl::Milliseconds(500)));
EXPECT_LT(waited, absl::Seconds(2));
}
TYPED_TEST_P(WaiterTest, WaitDurationReached) {
TypeParam waiter;
absl::Time start = absl::Now();
EXPECT_FALSE(waiter.Wait(
absl::synchronization_internal::KernelTimeout(absl::Milliseconds(500))));
absl::Duration waited = absl::Now() - start;
EXPECT_GE(waited, WithTolerance(absl::Milliseconds(500)));
EXPECT_LT(waited, absl::Seconds(1));
}
TYPED_TEST_P(WaiterTest, WaitTimeReached) {
TypeParam waiter;
absl::Time start = absl::Now();
EXPECT_FALSE(waiter.Wait(absl::synchronization_internal::KernelTimeout(
start + absl::Milliseconds(500))));
absl::Duration waited = absl::Now() - start;
EXPECT_GE(waited, WithTolerance(absl::Milliseconds(500)));
EXPECT_LT(waited, absl::Seconds(1));
}
REGISTER_TYPED_TEST_SUITE_P(WaiterTest,
WaitNoTimeout,
WaitDurationWoken,
WaitTimeWoken,
WaitDurationReached,
WaitTimeReached);
#ifdef ABSL_INTERNAL_HAVE_FUTEX_WAITER
INSTANTIATE_TYPED_TEST_SUITE_P(Futex, WaiterTest,
absl::synchronization_internal::FutexWaiter);
#endif
#ifdef ABSL_INTERNAL_HAVE_PTHREAD_WAITER
INSTANTIATE_TYPED_TEST_SUITE_P(Pthread, WaiterTest,
absl::synchronization_internal::PthreadWaiter);
#endif
#ifdef ABSL_INTERNAL_HAVE_SEM_WAITER
INSTANTIATE_TYPED_TEST_SUITE_P(Sem, WaiterTest,
absl::synchronization_internal::SemWaiter);
#endif
#ifdef ABSL_INTERNAL_HAVE_WIN32_WAITER
INSTANTIATE_TYPED_TEST_SUITE_P(Win32, WaiterTest,
absl::synchronization_internal::Win32Waiter);
#endif
#ifdef ABSL_INTERNAL_HAVE_STDCPP_WAITER
INSTANTIATE_TYPED_TEST_SUITE_P(Stdcpp, WaiterTest,
absl::synchronization_internal::StdcppWaiter);
#endif
} // namespace