brintos

brintos / llvm-project-archived public Read only

0
0
Text · 4.4 KiB · 600ce63 Raw
147 lines · cpp
1//===-- ThreadingTests.cpp --------------------------------------*- C++ -*-===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "support/Threading.h"10#include "llvm/ADT/DenseMap.h"11#include "gmock/gmock.h"12#include "gtest/gtest.h"13#include <chrono>14#include <mutex>15 16namespace clang {17namespace clangd {18class ThreadingTest : public ::testing::Test {};19 20TEST_F(ThreadingTest, TaskRunner) {21  const int TasksCnt = 100;22  // This should be const, but MSVC does not allow to use const vars in lambdas23  // without capture. On the other hand, clang gives a warning that capture of24  // const var is not required.25  // Making it non-const makes both compilers happy.26  int IncrementsPerTask = 1000;27 28  std::mutex Mutex;29  int Counter(0); /* GUARDED_BY(Mutex) */30  {31    AsyncTaskRunner Tasks;32    auto ScheduleIncrements = [&]() {33      for (int TaskI = 0; TaskI < TasksCnt; ++TaskI) {34        Tasks.runAsync("task", [&Counter, &Mutex, IncrementsPerTask]() {35          for (int Increment = 0; Increment < IncrementsPerTask; ++Increment) {36            std::lock_guard<std::mutex> Lock(Mutex);37            ++Counter;38          }39        });40      }41    };42 43    {44      // Make sure runAsync is not running tasks synchronously on the same45      // thread by locking the Mutex used for increments.46      std::lock_guard<std::mutex> Lock(Mutex);47      ScheduleIncrements();48    }49 50    Tasks.wait();51    {52      std::lock_guard<std::mutex> Lock(Mutex);53      ASSERT_EQ(Counter, TasksCnt * IncrementsPerTask);54    }55 56    {57      std::lock_guard<std::mutex> Lock(Mutex);58      Counter = 0;59      ScheduleIncrements();60    }61  }62  // Check that destructor has waited for tasks to finish.63  std::lock_guard<std::mutex> Lock(Mutex);64  ASSERT_EQ(Counter, TasksCnt * IncrementsPerTask);65}66 67TEST_F(ThreadingTest, Memoize) {68  const unsigned NumThreads = 5;69  const unsigned NumKeys = 100;70  const unsigned NumIterations = 100;71 72  Memoize<llvm::DenseMap<int, int>> Cache;73  std::atomic<unsigned> ComputeCount(0);74  std::atomic<int> ComputeResult[NumKeys];75  std::fill(std::begin(ComputeResult), std::end(ComputeResult), -1);76 77  AsyncTaskRunner Tasks;78  for (unsigned I = 0; I < NumThreads; ++I)79    Tasks.runAsync("worker" + std::to_string(I), [&] {80      for (unsigned J = 0; J < NumIterations; J++)81        for (unsigned K = 0; K < NumKeys; K++) {82          int Result = Cache.get(K, [&] { return ++ComputeCount; });83          EXPECT_THAT(ComputeResult[K].exchange(Result),84                      testing::AnyOf(-1, Result))85              << "Got inconsistent results from memoize";86        }87    });88  Tasks.wait();89  EXPECT_GE(ComputeCount, NumKeys) << "Computed each key once";90  EXPECT_LE(ComputeCount, NumThreads * NumKeys)91      << "Worst case, computed each key in every thread";92  for (int Result : ComputeResult)93    EXPECT_GT(Result, 0) << "All results in expected domain";94}95 96TEST_F(ThreadingTest, MemoizeDeterministic) {97  Memoize<llvm::DenseMap<int, char>> Cache;98 99  // Spawn two parallel computations, A and B.100  // Force concurrency: neither can finish until both have started.101  // Verify that cache returns consistent results.102  AsyncTaskRunner Tasks;103  std::atomic<char> ValueA(0), ValueB(0);104  Notification ReleaseA, ReleaseB;105  Tasks.runAsync("A", [&] {106    ValueA = Cache.get(0, [&] {107      ReleaseB.notify();108      ReleaseA.wait();109      return 'A';110    });111  });112  Tasks.runAsync("A", [&] {113    ValueB = Cache.get(0, [&] {114      ReleaseA.notify();115      ReleaseB.wait();116      return 'B';117    });118  });119  Tasks.wait();120 121  ASSERT_EQ(ValueA, ValueB);122  ASSERT_THAT(ValueA.load(), testing::AnyOf('A', 'B'));123}124 125// It's hard to write a real test of this class, std::chrono is awkward to mock.126// But test some degenerate cases at least.127TEST(PeriodicThrottlerTest, Minimal) {128  PeriodicThrottler Once(std::chrono::hours(24));129  EXPECT_TRUE(Once());130  EXPECT_FALSE(Once());131  EXPECT_FALSE(Once());132 133  PeriodicThrottler Later(std::chrono::hours(24),134                          /*Delay=*/std::chrono::hours(24));135  EXPECT_FALSE(Later());136  EXPECT_FALSE(Later());137  EXPECT_FALSE(Later());138 139  PeriodicThrottler Always(std::chrono::seconds(0));140  EXPECT_TRUE(Always());141  EXPECT_TRUE(Always());142  EXPECT_TRUE(Always());143}144 145} // namespace clangd146} // namespace clang147