//===- LockedAccessTest.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 // //===----------------------------------------------------------------------===// // // Tests for orc-rt's LockedAccess.h APIs. // //===----------------------------------------------------------------------===// #include "orc-rt/LockedAccess.h" #include "gtest/gtest.h" #include #include #include using namespace orc_rt; namespace { template struct Foo { std::mutex M; T Val{}; Foo() = default; Foo(T Val) : Val(std::move(Val)) {} auto get() { return LockedAccess(Val, M); } auto getConst() { return LockedAccess(std::as_const(Val), M); } }; } // anonymous namespace TEST(LockedAccessTest, ArrowRead) { Foo> F({42, 7}); EXPECT_EQ(F.get()->first, 42); EXPECT_EQ(F.get()->second, 7); } TEST(LockedAccessTest, ArrowWrite) { Foo> F; F.get()->first = 10; F.get()->second = 20; EXPECT_EQ(F.Val.first, 10); EXPECT_EQ(F.Val.second, 20); } TEST(LockedAccessTest, ConstArrowReturnsConstPtr) { Foo F(42); static_assert(std::is_const_v< std::remove_pointer_t())>>, "const overload should return const pointer"); } TEST(LockedAccessTest, DerefRead) { Foo F(42); EXPECT_EQ(*F.get(), 42); } TEST(LockedAccessTest, DerefWrite) { Foo F; *F.get() = 7; EXPECT_EQ(F.Val, 7); } TEST(LockedAccessTest, DerefPassToFunction) { Foo F(42); auto timesTwo = [](int &X) { return X * 2; }; EXPECT_EQ(timesTwo(*F.get()), 84); } TEST(LockedAccessTest, ConstDerefReturnsConstRef) { Foo F(42); static_assert( std::is_const_v>, "const overload should return const reference"); } TEST(LockedAccessTest, WithRefRead) { Foo F(42); F.get().with_ref([](int &Y) { EXPECT_EQ(Y, 42); }); } TEST(LockedAccessTest, WithRefWrite) { Foo F; F.get().with_ref([](int &Y) { Y = 7; }); EXPECT_EQ(F.Val, 7); } TEST(LockedAccessTest, WithRefReturnValue) { Foo F(42); int Result = F.get().with_ref([](int &Y) { return Y + 1; }); EXPECT_EQ(Result, 43); } TEST(LockedAccessTest, WithRefReturnReference) { Foo> F({1, 2}); int &Ref = F.get().with_ref( [](std::pair &P) -> int & { return P.second; }); EXPECT_EQ(&Ref, &F.Val.second); } TEST(LockedAccessTest, ConstWithRefGetsConstReference) { Foo F(42); F.getConst().with_ref([](const int &Y) { EXPECT_EQ(Y, 42); }); } TEST(LockedAccessTest, WithRefMultiStatement) { Foo> F; F.get().with_ref([](std::pair &P) { P.first = 10; P.second = P.first + 5; }); EXPECT_EQ(F.Val.first, 10); EXPECT_EQ(F.Val.second, 15); } TEST(LockedAccessTest, HoldsLockDuringArrow) { struct Checker { Foo *F; bool isLocked() { return !F->M.try_lock(); } }; Foo F({&F}); EXPECT_TRUE(F.get()->isLocked()); } TEST(LockedAccessTest, HoldsLockDuringWithRef) { Foo F; EXPECT_TRUE(F.get().with_ref([&](int &) { return !F.M.try_lock(); })); } TEST(LockedAccessTest, ProtectsAcrossThreads) { Foo F; const int Iters = 10000; auto Increment = [&]() { for (int I = 0; I < Iters; ++I) F.get().with_ref([](int &C) { ++C; }); }; std::thread T1(Increment); std::thread T2(Increment); T1.join(); T2.join(); EXPECT_EQ(F.Val, 2 * Iters); }