//===-- sanitizer_thread_registry_test.cpp --------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file is a part of shared sanitizer runtime.
//
//===----------------------------------------------------------------------===//
#include "sanitizer_common/sanitizer_thread_arg_retval.h"
#include "gtest/gtest.h"
#include "sanitizer_mutex.h"
namespace __sanitizer {
static int t;
static void* arg = &t;
static void* (*routine)(void*) = [](void*) { return arg; };
static void* retval = (&t) + 1;
TEST(ThreadArgRetvalTest, CreateFail) {
ThreadArgRetval td;
td.Create(false, {routine, arg}, []() { return 0; });
EXPECT_EQ(0u, td.size());
}
TEST(ThreadArgRetvalTest, CreateRunJoin) {
ThreadArgRetval td;
td.Create(false, {routine, arg}, []() { return 1; });
EXPECT_EQ(1u, td.size());
EXPECT_EQ(arg, td.GetArgs(1).arg_retval);
EXPECT_EQ(routine, td.GetArgs(1).routine);
EXPECT_EQ(1u, td.size());
td.Finish(1, retval);
EXPECT_EQ(1u, td.size());
td.Join(1, []() { return false; });
EXPECT_EQ(1u, td.size());
td.Join(1, []() { return true; });
EXPECT_EQ(0u, td.size());
}
TEST(ThreadArgRetvalTest, CreateJoinRun) {
ThreadArgRetval td;
td.Create(false, {routine, arg}, []() { return 1; });
EXPECT_EQ(1u, td.size());
td.Join(1, []() { return false; });
EXPECT_EQ(1u, td.size());
td.Join(1, [&]() {
// Expected to happen on another thread.
EXPECT_EQ(1u, td.size());
EXPECT_EQ(arg, td.GetArgs(1).arg_retval);
EXPECT_EQ(routine, td.GetArgs(1).routine);
EXPECT_EQ(1u, td.size());
td.Finish(1, retval);
EXPECT_EQ(1u, td.size());
return true;
});
EXPECT_EQ(0u, td.size());
}
TEST(ThreadArgRetvalTest, CreateRunDetach) {
ThreadArgRetval td;
td.Create(false, {routine, arg}, []() { return 1; });
EXPECT_EQ(1u, td.size());
EXPECT_EQ(arg, td.GetArgs(1).arg_retval);
EXPECT_EQ(routine, td.GetArgs(1).routine);
EXPECT_EQ(1u, td.size());
td.Finish(1, retval);
EXPECT_EQ(1u, td.size());
td.Detach(1, []() { return false; });
EXPECT_EQ(1u, td.size());
td.Detach(1, []() { return true; });
EXPECT_EQ(0u, td.size());
}
TEST(ThreadArgRetvalTest, CreateDetachRun) {
ThreadArgRetval td;
td.Create(false, {routine, arg}, []() { return 1; });
EXPECT_EQ(1u, td.size());
td.Detach(1, []() { return true; });
EXPECT_EQ(1u, td.size());
EXPECT_EQ(arg, td.GetArgs(1).arg_retval);
EXPECT_EQ(routine, td.GetArgs(1).routine);
EXPECT_EQ(1u, td.size());
td.Finish(1, retval);
EXPECT_EQ(0u, td.size());
}
TEST(ThreadArgRetvalTest, CreateRunJoinReuse) {
ThreadArgRetval td;
td.Create(false, {routine, arg}, []() { return 1; });
EXPECT_EQ(1u, td.size());
td.Finish(1, retval);
EXPECT_EQ(1u, td.size());
td.Join(1, [&]() {
// Reuse thread id.
td.Create(false, {routine, arg}, []() { return 1; });
EXPECT_EQ(1u, td.size());
return true;
});
// Even if JoinFn succeeded, we can't erase mismatching thread.
EXPECT_EQ(1u, td.size());
// Now we can join another one.
td.Join(1, []() { return true; });
EXPECT_EQ(0u, td.size());
}
TEST(ThreadArgRetvalTest, GetAllPtrsLocked) {
ThreadArgRetval td;
td.Create(false, {routine, arg}, []() { return 1; });
{
GenericScopedLock<ThreadArgRetval> lock(&td);
InternalMmapVector<uptr> ptrs;
td.GetAllPtrsLocked(&ptrs);
EXPECT_EQ(1u, ptrs.size());
EXPECT_EQ((uptr)arg, ptrs[0]);
}
td.Finish(1, retval);
{
GenericScopedLock<ThreadArgRetval> lock(&td);
InternalMmapVector<uptr> ptrs;
td.GetAllPtrsLocked(&ptrs);
EXPECT_EQ(1u, ptrs.size());
EXPECT_EQ((uptr)retval, ptrs[0]);
}
td.Join(1, []() { return true; });
{
GenericScopedLock<ThreadArgRetval> lock(&td);
InternalMmapVector<uptr> ptrs;
td.GetAllPtrsLocked(&ptrs);
EXPECT_TRUE(ptrs.empty());
}
}
} // namespace __sanitizer