910e62b5创建于 1月15日历史提交
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "content/browser/scheduler/browser_ui_thread_scheduler.h"

#include <memory>
#include <utility>
#include <vector>

#include "base/feature_list.h"
#include "base/functional/callback_helpers.h"
#include "base/run_loop.h"
#include "base/task/single_thread_task_runner.h"
#include "base/task/thread_pool/thread_pool_instance.h"
#include "base/test/bind.h"
#include "base/test/mock_callback.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/test_future.h"
#include "content/common/features.h"
#include "content/public/browser/browser_thread.h"
#include "partition_alloc/extended_api.h"
#include "partition_alloc/partition_alloc_for_testing.h"
#include "partition_alloc/scheduler_loop_quarantine_support.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace content {
namespace {

using StrictMockTask =
    testing::StrictMock<base::MockCallback<base::RepeatingCallback<void()>>>;

base::OnceClosure RunOnDestruction(base::OnceClosure task) {
  return base::BindOnce(
      [](std::unique_ptr<base::ScopedClosureRunner>) {},
      std::make_unique<base::ScopedClosureRunner>(std::move(task)));
}

base::OnceClosure PostOnDestruction(
    scoped_refptr<base::SingleThreadTaskRunner> task_queue,
    base::OnceClosure task) {
  return RunOnDestruction(base::BindOnce(
      [](base::OnceClosure task,
         scoped_refptr<base::SingleThreadTaskRunner> task_queue) {
        task_queue->PostTask(FROM_HERE, std::move(task));
      },
      std::move(task), task_queue));
}

TEST(BrowserUIThreadSchedulerTest, DestructorPostChainDuringShutdown) {
  auto browser_ui_thread_scheduler_ =
      std::make_unique<BrowserUIThreadScheduler>();
  browser_ui_thread_scheduler_->GetHandle()->OnStartupComplete();
  auto task_queue =
      browser_ui_thread_scheduler_->GetHandle()->GetBrowserTaskRunner(
          BrowserUIThreadScheduler::QueueType::kDefault);

  bool run = false;
  task_queue->PostTask(
      FROM_HERE,
      PostOnDestruction(
          task_queue,
          PostOnDestruction(task_queue,
                            RunOnDestruction(base::BindOnce(
                                [](bool* run) { *run = true; }, &run)))));

  EXPECT_FALSE(run);
  browser_ui_thread_scheduler_.reset();

  EXPECT_TRUE(run);
}

class BrowserUIThreadSchedulerLoopQuarantineTest : public testing::Test {
 private:
  base::test::ScopedFeatureList scoped_feature_list_{
      features::
          kPartitionAllocSchedulerLoopQuarantineTaskObserverForBrowserUIThread};
};

TEST_F(BrowserUIThreadSchedulerLoopQuarantineTest,
       TestAllocationGetPurgedFromQuarantineAfterTaskCompletion) {
#if defined(MEMORY_TOOL_REPLACES_ALLOCATOR)
  GTEST_SKIP() << "This test does not work with memory tools.";
#elif !PA_BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) || \
    !PA_CONFIG(THREAD_CACHE_SUPPORTED)
  GTEST_SKIP() << "This test requires PA-E and ThreadCache.";
#else
  std::unique_ptr<BrowserUIThreadScheduler> browser_ui_thread_scheduler =
      BrowserUIThreadScheduler::CreateForTesting();
  browser_ui_thread_scheduler->GetHandle()->OnStartupComplete();

  // Pick up a queue.
  auto task_queue =
      browser_ui_thread_scheduler->GetHandle()->GetBrowserTaskRunner(
          BrowserUIThreadScheduler::QueueType::kUserBlocking);

  // Prepare PA root for testing.
  partition_alloc::PartitionOptions opts;
  opts.scheduler_loop_quarantine_thread_local_config.enable_quarantine = true;
  opts.scheduler_loop_quarantine_thread_local_config.branch_capacity_in_bytes =
      4096;
  partition_alloc::PartitionAllocatorForTesting allocator(opts);
  partition_alloc::PartitionRoot& root = *allocator.root();

  // Disables ThreadCache for the default allocator and enables it for the
  // testing allocator.
  partition_alloc::internal::ThreadCacheProcessScopeForTesting tcache_scope(
      &root);

  partition_alloc::internal::
      ScopedSchedulerLoopQuarantineBranchAccessorForTesting branch_accessor(
          &root);

  void* ptr = root.Alloc(16);

  base::test::TestFuture<void> future;
  task_queue->PostTaskAndReply(
      FROM_HERE, base::BindLambdaForTesting([&]() {
        EXPECT_FALSE(branch_accessor.IsQuarantined(ptr));
        root.Free<
            partition_alloc::internal::FreeFlags::kSchedulerLoopQuarantine>(
            ptr);
        EXPECT_TRUE(branch_accessor.IsQuarantined(ptr));
      }),
      future.GetCallback());
  EXPECT_TRUE(future.Wait());

  // `ptr` must not be in the quarantine as the scheduler finished its loop.
  EXPECT_FALSE(branch_accessor.IsQuarantined(ptr));
#endif
}

}  // namespace

}  // namespace content