chromium/base/threading/platform_thread_unittest.cc

// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/40284755): Remove this and spanify to fix the errors.
#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 {

// Trivial tests that thread runs and doesn't crash on create, join, or detach -

namespace {

class TrivialThread : public PlatformThread::Delegate {};

}  // namespace

TEST(PlatformThreadTest, TrivialJoin) {}

TEST(PlatformThreadTest, TrivialJoinTimesTen) {}

// The following detach tests are by nature racy. The run_event approximates the
// end and termination of the thread, but threads could persist shortly after
// the test completes.
TEST(PlatformThreadTest, TrivialDetach) {}

TEST(PlatformThreadTest, TrivialDetachTimesTen) {}

// Tests of basic thread functions ---------------------------------------------

namespace {

class FunctionTestThread : public PlatformThread::Delegate {};

}  // namespace

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

}  // namespace

// Test changing a created thread's type.
TEST(PlatformThreadTest, SetCurrentThreadType) {}

#if BUILDFLAG(IS_WIN)
// Test changing a created thread's priority in an IDLE_PRIORITY_CLASS process
// (regression test for https://crbug.com/901483).
TEST(PlatformThreadTest,
     SetCurrentThreadTypeWithThreadModeBackgroundIdleProcess) {
  ::SetPriorityClass(Process::Current().Handle(), IDLE_PRIORITY_CLASS);
  TestSetCurrentThreadType();
  ::SetPriorityClass(Process::Current().Handle(), NORMAL_PRIORITY_CLASS);
}
#endif  // BUILDFLAG(IS_WIN)

// Ideally PlatformThread::CanChangeThreadType() would be true on all
// platforms for all priorities. This not being the case. This test documents
// and hardcodes what we know. Please inform [email protected] if this
// proprerty changes for a given platform.
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_; }

  // Verifies the realtime thead configuration.
  void RunTest() override {
    EXPECT_EQ(PlatformThread::GetCurrentThreadType(),
              ThreadType::kRealtimeAudio);

    mach_port_t mach_thread_id = pthread_mach_thread_np(
        PlatformThread::CurrentHandle().platform_handle());

    // |count| and |get_default| chosen impirically so that
    // time_constraints_buffer[0] would store the last constraints that were
    // applied.
    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 {
      // Old-style empirical values.
      const double kTimeQuantum = 2.9;
      const double kAudioTimeNeeded = 0.75 * kTimeQuantum;
      const double kMaxTimeAllowed = 0.85 * kTimeQuantum;

      // Get the conversion factor from milliseconds to absolute time
      // which is what the time-constraints returns.
      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))));

}  // namespace

#endif  // BUILDFLAG(IS_APPLE)

#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)

namespace {

bool IsTidCacheCorrect() {}

void* CheckTidCacheCorrectWrapper(void*) {}

void CreatePthreadToCheckCache() {}

// This test must use raw pthreads and fork() to avoid calls from //base to
// PlatformThread::CurrentId(), as the ordering of calls is important to the
// test.
void TestTidCacheCorrect(bool main_thread_accesses_cache_first) {}

TEST(PlatformThreadTidCacheTest, MainThreadFirst) {}

TEST(PlatformThreadTidCacheTest, MainThreadSecond) {}

}  // namespace

#endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)

}  // namespace base