#ifdef UNSAFE_BUFFERS_BUILD
#pragma allow_unsafe_buffers
#endif
#include "base/threading/platform_thread.h"
#include <stddef.h>
#include "base/compiler_specific.h"
#include "base/process/process.h"
#include "base/synchronization/waitable_event.h"
#include "base/test/scoped_feature_list.h"
#include "base/threading/thread.h"
#include "base/threading/threading_features.h"
#include "build/blink_buildflags.h"
#include "build/build_config.h"
#include "testing/gtest/include/gtest/gtest.h"
#if BUILDFLAG(IS_POSIX)
#include "base/threading/platform_thread_internal_posix.h"
#elif BUILDFLAG(IS_WIN)
#include <windows.h>
#include "base/threading/platform_thread_win.h"
#endif
#if BUILDFLAG(IS_APPLE)
#include <mach/mach.h>
#include <mach/mach_time.h>
#include <mach/thread_policy.h>
#include "base/mac/mac_util.h"
#include "base/metrics/field_trial_params.h"
#include "base/time/time.h"
#endif
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
#include <pthread.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#endif
namespace base {
namespace {
class TrivialThread : public PlatformThread::Delegate { … };
}
TEST(PlatformThreadTest, TrivialJoin) { … }
TEST(PlatformThreadTest, TrivialJoinTimesTen) { … }
TEST(PlatformThreadTest, TrivialDetach) { … }
TEST(PlatformThreadTest, TrivialDetachTimesTen) { … }
namespace {
class FunctionTestThread : public PlatformThread::Delegate { … };
}
TEST(PlatformThreadTest, Function) { … }
TEST(PlatformThreadTest, FunctionTimesTen) { … }
namespace {
constexpr ThreadType kAllThreadTypes[] = …;
class ThreadTypeTestThread : public FunctionTestThread { … };
class ThreadPriorityTestThread : public FunctionTestThread { … };
void TestSetCurrentThreadType() { … }
void TestPriorityResultingFromThreadType(ThreadType thread_type,
ThreadPriorityForTest priority) { … }
}
TEST(PlatformThreadTest, SetCurrentThreadType) { … }
#if BUILDFLAG(IS_WIN)
TEST(PlatformThreadTest,
SetCurrentThreadTypeWithThreadModeBackgroundIdleProcess) {
::SetPriorityClass(Process::Current().Handle(), IDLE_PRIORITY_CLASS);
TestSetCurrentThreadType();
::SetPriorityClass(Process::Current().Handle(), NORMAL_PRIORITY_CLASS);
}
#endif
TEST(PlatformThreadTest, CanChangeThreadType) { … }
TEST(PlatformThreadTest, SetCurrentThreadTypeTest) { … }
TEST(PlatformThreadTest, SetHugeThreadName) { … }
TEST(PlatformThreadTest, GetDefaultThreadStackSize) { … }
#if BUILDFLAG(IS_APPLE)
namespace {
class RealtimeTestThread : public FunctionTestThread {
public:
explicit RealtimeTestThread(TimeDelta realtime_period)
: realtime_period_(realtime_period) {}
~RealtimeTestThread() override = default;
private:
RealtimeTestThread(const RealtimeTestThread&) = delete;
RealtimeTestThread& operator=(const RealtimeTestThread&) = delete;
TimeDelta GetRealtimePeriod() final { return realtime_period_; }
void RunTest() override {
EXPECT_EQ(PlatformThread::GetCurrentThreadType(),
ThreadType::kRealtimeAudio);
mach_port_t mach_thread_id = pthread_mach_thread_np(
PlatformThread::CurrentHandle().platform_handle());
const int kPolicyCount = 32;
thread_time_constraint_policy_data_t time_constraints_buffer[kPolicyCount];
mach_msg_type_number_t count = kPolicyCount;
boolean_t get_default = 0;
kern_return_t result = thread_policy_get(
mach_thread_id, THREAD_TIME_CONSTRAINT_POLICY,
reinterpret_cast<thread_policy_t>(time_constraints_buffer), &count,
&get_default);
EXPECT_EQ(result, KERN_SUCCESS);
const thread_time_constraint_policy_data_t& time_constraints =
time_constraints_buffer[0];
mach_timebase_info_data_t tb_info;
mach_timebase_info(&tb_info);
if (FeatureList::IsEnabled(kOptimizedRealtimeThreadingMac) &&
!realtime_period_.is_zero()) {
uint32_t abs_realtime_period = saturated_cast<uint32_t>(
realtime_period_.InNanoseconds() *
(static_cast<double>(tb_info.denom) / tb_info.numer));
EXPECT_EQ(time_constraints.period, abs_realtime_period);
EXPECT_EQ(
time_constraints.computation,
static_cast<uint32_t>(abs_realtime_period *
kOptimizedRealtimeThreadingMacBusy.Get()));
EXPECT_EQ(
time_constraints.constraint,
static_cast<uint32_t>(abs_realtime_period *
kOptimizedRealtimeThreadingMacBusyLimit.Get()));
EXPECT_EQ(time_constraints.preemptible,
kOptimizedRealtimeThreadingMacPreemptible.Get());
} else {
const double kTimeQuantum = 2.9;
const double kAudioTimeNeeded = 0.75 * kTimeQuantum;
const double kMaxTimeAllowed = 0.85 * kTimeQuantum;
double ms_to_abs_time = double(tb_info.denom) / tb_info.numer * 1000000;
EXPECT_EQ(time_constraints.period,
saturated_cast<uint32_t>(kTimeQuantum * ms_to_abs_time));
EXPECT_EQ(time_constraints.computation,
saturated_cast<uint32_t>(kAudioTimeNeeded * ms_to_abs_time));
EXPECT_EQ(time_constraints.constraint,
saturated_cast<uint32_t>(kMaxTimeAllowed * ms_to_abs_time));
EXPECT_FALSE(time_constraints.preemptible);
}
}
const TimeDelta realtime_period_;
};
class RealtimePlatformThreadTest
: public testing::TestWithParam<
std::tuple<bool, FieldTrialParams, TimeDelta>> {
protected:
void VerifyRealtimeConfig(TimeDelta period) {
RealtimeTestThread thread(period);
PlatformThreadHandle handle;
ASSERT_FALSE(thread.IsRunning());
ASSERT_TRUE(PlatformThread::CreateWithType(0, &thread, &handle,
ThreadType::kRealtimeAudio));
thread.WaitForTerminationReady();
ASSERT_TRUE(thread.IsRunning());
thread.MarkForTermination();
PlatformThread::Join(handle);
ASSERT_FALSE(thread.IsRunning());
}
};
TEST_P(RealtimePlatformThreadTest, RealtimeAudioConfigMac) {
test::ScopedFeatureList feature_list;
if (std::get<0>(GetParam())) {
feature_list.InitAndEnableFeatureWithParameters(
kOptimizedRealtimeThreadingMac, std::get<1>(GetParam()));
} else {
feature_list.InitAndDisableFeature(kOptimizedRealtimeThreadingMac);
}
PlatformThread::InitializeFeatures();
VerifyRealtimeConfig(std::get<2>(GetParam()));
}
INSTANTIATE_TEST_SUITE_P(
RealtimePlatformThreadTest,
RealtimePlatformThreadTest,
testing::Combine(
testing::Bool(),
testing::Values(
FieldTrialParams{
{kOptimizedRealtimeThreadingMacPreemptible.name, "true"}},
FieldTrialParams{
{kOptimizedRealtimeThreadingMacPreemptible.name, "false"}},
FieldTrialParams{
{kOptimizedRealtimeThreadingMacBusy.name, "0.5"},
{kOptimizedRealtimeThreadingMacBusyLimit.name, "0.75"}},
FieldTrialParams{
{kOptimizedRealtimeThreadingMacBusy.name, "0.7"},
{kOptimizedRealtimeThreadingMacBusyLimit.name, "0.7"}},
FieldTrialParams{
{kOptimizedRealtimeThreadingMacBusy.name, "0.5"},
{kOptimizedRealtimeThreadingMacBusyLimit.name, "1.0"}}),
testing::Values(TimeDelta(),
Seconds(256.0 / 48000),
Milliseconds(5),
Milliseconds(10),
Seconds(1024.0 / 44100),
Seconds(1024.0 / 16000))));
}
#endif
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
namespace {
bool IsTidCacheCorrect() { … }
void* CheckTidCacheCorrectWrapper(void*) { … }
void CreatePthreadToCheckCache() { … }
void TestTidCacheCorrect(bool main_thread_accesses_cache_first) { … }
TEST(PlatformThreadTidCacheTest, MainThreadFirst) { … }
TEST(PlatformThreadTidCacheTest, MainThreadSecond) { … }
}
#endif
}