#include "llvm/Support/ThreadPool.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SetVector.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Config/llvm-config.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Program.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/Support/Threading.h"
#include "llvm/TargetParser/Host.h"
#include "llvm/TargetParser/Triple.h"
#ifdef _WIN32
#include "llvm/Support/Windows/WindowsSupport.h"
#endif
#include <chrono>
#include <thread>
#include "gtest/gtest.h"
namespace testing {
namespace internal {
#if LLVM_ENABLE_THREADS
template <> std::string GetTypeName<llvm::StdThreadPool>() { … }
#endif
template <> std::string GetTypeName<llvm::SingleThreadExecutor>() { … }
}
}
usingnamespacellvm;
template <typename ThreadPoolImpl> class ThreadPoolTest : public testing::Test { … };
ThreadPoolImpls;
TYPED_TEST_SUITE(…);
#define CHECK_UNSUPPORTED() …
TYPED_TEST(ThreadPoolTest, AsyncBarrier) { … }
static void TestFunc(std::atomic_int &checked_in, int i) { … }
TYPED_TEST(ThreadPoolTest, AsyncBarrierArgs) { … }
TYPED_TEST(ThreadPoolTest, Async) { … }
TYPED_TEST(ThreadPoolTest, GetFuture) { … }
TYPED_TEST(ThreadPoolTest, GetFutureWithResult) { … }
TYPED_TEST(ThreadPoolTest, GetFutureWithResultAndArgs) { … }
TYPED_TEST(ThreadPoolTest, PoolDestruction) { … }
TYPED_TEST(ThreadPoolTest, Groups) { … }
TYPED_TEST(ThreadPoolTest, RecursiveGroups) { … }
TYPED_TEST(ThreadPoolTest, RecursiveWaitDeadlock) { … }
#if LLVM_ENABLE_THREADS == 1
#ifdef _WIN32
template <typename ThreadPoolImpl>
SmallVector<llvm::BitVector, 0>
ThreadPoolTest<ThreadPoolImpl>::RunOnAllSockets(ThreadPoolStrategy S) {
llvm::SetVector<llvm::BitVector> ThreadsUsed;
std::mutex Lock;
{
std::condition_variable AllThreads;
std::mutex AllThreadsLock;
unsigned Active = 0;
DefaultThreadPool Pool(S);
for (size_t I = 0; I < S.compute_thread_count(); ++I) {
Pool.async([&] {
{
std::lock_guard<std::mutex> Guard(AllThreadsLock);
++Active;
AllThreads.notify_one();
}
this->waitForMainThread();
std::lock_guard<std::mutex> Guard(Lock);
auto Mask = llvm::get_thread_affinity_mask();
ThreadsUsed.insert(Mask);
});
}
EXPECT_EQ(true, ThreadsUsed.empty());
{
std::unique_lock<std::mutex> Guard(AllThreadsLock);
AllThreads.wait(Guard,
[&]() { return Active == S.compute_thread_count(); });
}
this->setMainThreadReady();
}
return ThreadsUsed.takeVector();
}
TYPED_TEST(ThreadPoolTest, AllThreads_UseAllRessources) {
CHECK_UNSUPPORTED();
if (llvm::RunningWindows11OrGreater())
GTEST_SKIP();
auto ThreadsUsed = this->RunOnAllSockets({});
ASSERT_EQ(llvm::get_cpus(), ThreadsUsed.size());
}
TYPED_TEST(ThreadPoolTest, AllThreads_OneThreadPerCore) {
CHECK_UNSUPPORTED();
if (llvm::RunningWindows11OrGreater())
GTEST_SKIP();
auto ThreadsUsed =
this->RunOnAllSockets(llvm::heavyweight_hardware_concurrency());
ASSERT_EQ(llvm::get_cpus(), ThreadsUsed.size());
}
extern const char *TestMainArgv0;
static cl::opt<std::string> ThreadPoolTestStringArg1("thread-pool-string-arg1");
#ifdef _WIN32
#define setenv …
#endif
TYPED_TEST(ThreadPoolTest, AffinityMask) {
CHECK_UNSUPPORTED();
if (llvm::hardware_concurrency().compute_thread_count() < 4)
GTEST_SKIP();
using namespace llvm::sys;
if (getenv("LLVM_THREADPOOL_AFFINITYMASK")) {
auto ThreadsUsed = this->RunOnAllSockets({});
assert(llvm::all_of(ThreadsUsed,
[](auto &T) { return T.getData().front() < 16UL; }) &&
"Threads ran on more CPUs than expected! The affinity mask does not "
"seem to work.");
GTEST_SKIP();
}
std::string Executable =
sys::fs::getMainExecutable(TestMainArgv0, &ThreadPoolTestStringArg1);
StringRef argv[] = {Executable, "--gtest_filter=ThreadPoolTest.AffinityMask"};
int Res = setenv("LLVM_THREADPOOL_AFFINITYMASK", "1", false);
ASSERT_EQ(Res, 0);
std::string Error;
bool ExecutionFailed;
BitVector Affinity;
Affinity.resize(4);
Affinity.set(0, 4);
int Ret = sys::ExecuteAndWait(Executable, argv, {}, {}, 0, 0, &Error,
&ExecutionFailed, nullptr, &Affinity);
ASSERT_EQ(0, Ret);
}
#endif
#endif