1 // Copyright 2021 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14
15 #include "pw_sync/inline_borrowable.h"
16
17 #include <array>
18 #include <chrono>
19 #include <tuple>
20
21 #include "pw_sync/interrupt_spin_lock.h"
22 #include "pw_sync/lock_annotations.h"
23 #include "pw_sync/mutex.h"
24 #include "pw_unit_test/framework.h"
25
26 namespace pw::sync {
27 namespace {
28
29 using namespace std::chrono_literals;
30
31 // A trivial type that is copyable and movable.
32 struct TrivialType {
yespw::sync::__anoneb6ea4cd0111::TrivialType33 bool yes() const { return true; }
34 };
35
36 // A custom type that is neither copyable nor movable.
37 class CustomType {
38 public:
CustomType(int z)39 explicit constexpr CustomType(int z) : x_(z), y_(-z) {}
CustomType(int x,int y)40 constexpr CustomType(int x, int y) : x_(x), y_(y) {}
41
42 CustomType(const CustomType&) = delete;
43 CustomType& operator=(const CustomType&) = delete;
44 CustomType(CustomType&&) = delete;
45 CustomType&& operator=(CustomType&&) = delete;
46
data() const47 std::pair<int, int> data() const { return std::make_pair(x_, y_); }
48
49 private:
50 int x_, y_;
51 };
52
53 // A custom lockable interface.
54 class PW_LOCKABLE("VirtualCustomLocakble") VirtualCustomLockable {
55 public:
~VirtualCustomLockable()56 virtual ~VirtualCustomLockable() {}
57
58 virtual void lock() PW_EXCLUSIVE_LOCK_FUNCTION() = 0;
59 virtual void unlock() PW_UNLOCK_FUNCTION() = 0;
60 };
61
62 // A custom mutex type that requires a constructor parameter.
63 class PW_LOCKABLE("VirtualCustomMutex") VirtualCustomMutex
64 : public VirtualCustomLockable {
65 public:
VirtualCustomMutex(int id)66 explicit VirtualCustomMutex(int id) : mutex_{}, id_{id} {}
67
lock()68 void lock() override PW_EXCLUSIVE_LOCK_FUNCTION() { mutex_.lock(); }
unlock()69 void unlock() override PW_UNLOCK_FUNCTION() { mutex_.unlock(); }
70
id() const71 int id() const { return id_; }
72
73 private:
74 pw::sync::Mutex mutex_;
75 int id_;
76 };
77
TEST(InlineBorrowableTest,TestTrivialType)78 TEST(InlineBorrowableTest, TestTrivialType) {
79 InlineBorrowable<TrivialType> trivial;
80 EXPECT_TRUE(trivial.acquire()->yes());
81 }
82
TEST(InlineBorrowableTest,TestCustomTypeInPlace1Arg)83 TEST(InlineBorrowableTest, TestCustomTypeInPlace1Arg) {
84 InlineBorrowable<CustomType> custom(std::in_place, 1);
85 EXPECT_EQ(custom.acquire()->data(), std::make_pair(1, -1));
86 }
87
TEST(InlineBorrowableTest,TestCustomTypeInPlace1ArgLValue)88 TEST(InlineBorrowableTest, TestCustomTypeInPlace1ArgLValue) {
89 int x = 1;
90 InlineBorrowable<CustomType> custom(std::in_place, x);
91 EXPECT_EQ(custom.acquire()->data(), std::make_pair(x, -x));
92 }
93
TEST(InlineBorrowableTest,TestCustomTypeInPlace2Arg)94 TEST(InlineBorrowableTest, TestCustomTypeInPlace2Arg) {
95 InlineBorrowable<CustomType> custom(std::in_place, 1, 2);
96 EXPECT_EQ(custom.acquire()->data(), std::make_pair(1, 2));
97 }
98
TEST(InlineBorrowableTest,TestCustomTypeFromTuple)99 TEST(InlineBorrowableTest, TestCustomTypeFromTuple) {
100 InlineBorrowable<CustomType> custom(std::make_tuple(1, 2));
101 EXPECT_EQ(custom.acquire()->data(), std::make_pair(1, 2));
102 }
103
TEST(InlineBorrowableTest,TestCustomTypeFromFactory)104 TEST(InlineBorrowableTest, TestCustomTypeFromFactory) {
105 InlineBorrowable<CustomType> custom([] { return CustomType(1, 2); });
106 EXPECT_EQ(custom.acquire()->data(), std::make_pair(1, 2));
107 }
108
TEST(InlineBorrowableTest,TestCustomTypeFromMutableFactory)109 TEST(InlineBorrowableTest, TestCustomTypeFromMutableFactory) {
110 int i = 0;
111 auto factory = [&i]() mutable {
112 i++;
113 return CustomType(1, 2);
114 };
115 InlineBorrowable<CustomType> custom(factory);
116 EXPECT_EQ(custom.acquire()->data(), std::make_pair(1, 2));
117 }
118
TEST(InlineBorrowableTest,TestTrivialTypeWithInterruptSpinLock)119 TEST(InlineBorrowableTest, TestTrivialTypeWithInterruptSpinLock) {
120 InlineBorrowable<TrivialType, VirtualInterruptSpinLock>
121 trivial_interrupt_safe;
122 EXPECT_TRUE(trivial_interrupt_safe.acquire()->yes());
123 }
124
TEST(InlineBorrowableTest,TestCustomTypeWithInterruptSpinLock)125 TEST(InlineBorrowableTest, TestCustomTypeWithInterruptSpinLock) {
126 InlineBorrowable<CustomType, VirtualInterruptSpinLock> custom_interrupt_safe(
127 std::in_place, 1, 2);
128 EXPECT_EQ(custom_interrupt_safe.acquire()->data(), std::make_pair(1, 2));
129 }
130
TEST(InlineBorrowableTest,TestCustomTypeWithCustomMutexFromTuple)131 TEST(InlineBorrowableTest, TestCustomTypeWithCustomMutexFromTuple) {
132 InlineBorrowable<CustomType, VirtualCustomMutex, VirtualCustomLockable>
133 custom_mutex(std::make_tuple(1, 2), std::make_tuple(42));
134 EXPECT_EQ(custom_mutex.acquire()->data(), std::make_pair(1, 2));
135 }
136
TEST(InlineBorrowableTest,TestCustomTypeWithCustomMutexFromFactory)137 TEST(InlineBorrowableTest, TestCustomTypeWithCustomMutexFromFactory) {
138 InlineBorrowable<CustomType, VirtualCustomMutex, VirtualCustomLockable>
139 custom_mutex([] { return CustomType(1, 2); },
140 [] { return VirtualCustomMutex(42); });
141 EXPECT_EQ(custom_mutex.acquire()->data(), std::make_pair(1, 2));
142 }
143
TEST(InlineBorrowableTest,TestArrayAggregateInitializationInPlace)144 TEST(InlineBorrowableTest, TestArrayAggregateInitializationInPlace) {
145 using ArrayAggregate = std::array<int, 2>;
146 InlineBorrowable<ArrayAggregate> aggregate{std::in_place, 1, 2};
147 EXPECT_EQ((*aggregate.acquire())[0], 1);
148 EXPECT_EQ((*aggregate.acquire())[1], 2);
149 }
150
151 struct StructAggregate {
152 int a;
153 int b;
154 };
155
TEST(InlineBorrowableTest,TestStructAggregateInitializationInPlace)156 TEST(InlineBorrowableTest, TestStructAggregateInitializationInPlace) {
157 InlineBorrowable<StructAggregate> aggregate{std::in_place, 1, 2};
158 EXPECT_EQ(aggregate.acquire()->a, 1);
159 EXPECT_EQ(aggregate.acquire()->b, 2);
160 }
161
TEST(InlineBorrowableTest,TestStructAggregateInitializationFromFactory)162 TEST(InlineBorrowableTest, TestStructAggregateInitializationFromFactory) {
163 InlineBorrowable<StructAggregate> aggregate(
164 []() -> StructAggregate { return {.a = 1, .b = 2}; });
165 EXPECT_EQ(aggregate.acquire()->a, 1);
166 EXPECT_EQ(aggregate.acquire()->b, 2);
167 }
168
TEST(InlineBorrowableTest,TestStructAggregateInitializationFromMutableFactory)169 TEST(InlineBorrowableTest,
170 TestStructAggregateInitializationFromMutableFactory) {
171 int i = 0;
172 auto factory = [&i]() mutable -> StructAggregate {
173 i++;
174 return {.a = 1, .b = 2};
175 };
176 InlineBorrowable<StructAggregate> aggregate(factory);
177 EXPECT_EQ(aggregate.acquire()->a, 1);
178 EXPECT_EQ(aggregate.acquire()->b, 2);
179 }
180
181 struct ReferenceTypes {
ReferenceTypespw::sync::__anoneb6ea4cd0111::ReferenceTypes182 ReferenceTypes(const int& a, int& b, BorrowedPointer<int>&& c)
183 : in(a), out(b), borrowed(std::move(c)) {}
184 const int& in;
185 int& out;
186 BorrowedPointer<int> borrowed; // move-only type
187 };
188
189 class InlineBorrowableReferenceTypesTest : public ::testing::Test {
190 protected:
191 int input_ = 1;
192 int output_ = 2;
193 InlineBorrowable<int> borrowable_{std::in_place, 3};
194
Validate(BorrowedPointer<ReferenceTypes> && references)195 void Validate(BorrowedPointer<ReferenceTypes>&& references) {
196 EXPECT_EQ(references->in, 1);
197 EXPECT_EQ(references->out, 2);
198 EXPECT_EQ(*references->borrowed, 3);
199
200 references->out = -2;
201 EXPECT_EQ(output_, -2);
202 }
203 };
204
TEST_F(InlineBorrowableReferenceTypesTest,TestInPlace)205 TEST_F(InlineBorrowableReferenceTypesTest, TestInPlace) {
206 InlineBorrowable<ReferenceTypes> references(
207 std::in_place, input_, output_, borrowable_.acquire());
208 Validate(references.acquire());
209 }
210
TEST_F(InlineBorrowableReferenceTypesTest,TestFromTuple)211 TEST_F(InlineBorrowableReferenceTypesTest, TestFromTuple) {
212 InlineBorrowable<ReferenceTypes> references(
213 std::forward_as_tuple(input_, output_, borrowable_.acquire()));
214 Validate(references.acquire());
215 }
216
TEST_F(InlineBorrowableReferenceTypesTest,TestFromFactory)217 TEST_F(InlineBorrowableReferenceTypesTest, TestFromFactory) {
218 InlineBorrowable<ReferenceTypes> references(
219 [&] { return ReferenceTypes(input_, output_, borrowable_.acquire()); });
220 Validate(references.acquire());
221 }
222
223 } // namespace
224 } // namespace pw::sync
225