1 /*
2 * Copyright (c) Facebook, Inc. and its affiliates.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <folly/synchronization/detail/InlineFunctionRef.h>
18
19 #include <array>
20 #include <cstring>
21
22 #include <folly/portability/GTest.h>
23
24 namespace folly {
25 namespace detail {
26
27 class InlineFunctionRefTest : public ::testing::Test {
28 public:
29 template <typename InlineFRef>
storage(InlineFRef & fref)30 static auto& storage(InlineFRef& fref) {
31 return fref.storage_;
32 }
33
34 template <typename InlineFRef>
call(InlineFRef & fref)35 static auto& call(InlineFRef& fref) {
36 return fref.call_;
37 }
38 };
39
TEST_F(InlineFunctionRefTest,BasicInvoke)40 TEST_F(InlineFunctionRefTest, BasicInvoke) {
41 {
42 auto func = [dummy = int{0}](auto integer) { return integer + dummy; };
43 auto copy = func;
44 auto fref =
45 InlineFunctionRef<int(int), 2 * sizeof(uintptr_t)>{std::move(func)};
46 EXPECT_EQ(fref(1), 1);
47 EXPECT_EQ(fref(2), 2);
48 EXPECT_EQ(fref(3), 3);
49
50 static_assert(sizeof(copy) == sizeof(int), "Make sure no padding");
51 EXPECT_EQ(std::memcmp(&storage(fref), ©, sizeof(copy)), 0);
52 }
53 }
54
TEST_F(InlineFunctionRefTest,InvokeWithCapture)55 TEST_F(InlineFunctionRefTest, InvokeWithCapture) {
56 {
57 auto data = std::uint64_t{1};
58 auto func = [&](auto integer) { return integer + data; };
59 auto copy = func;
60 auto fref = InlineFunctionRef<int(int), 24>{std::move(func)};
61
62 EXPECT_EQ(fref(1), 2);
63 EXPECT_EQ(fref(2), 3);
64 EXPECT_EQ(fref(3), 4);
65
66 data = 2;
67 EXPECT_EQ(fref(1), 3);
68 EXPECT_EQ(fref(2), 4);
69 EXPECT_EQ(fref(3), 5);
70
71 data = 3;
72 EXPECT_EQ(fref(1), 4);
73 EXPECT_EQ(fref(2), 5);
74 EXPECT_EQ(fref(3), 6);
75
76 EXPECT_EQ(sizeof(copy), 8);
77 EXPECT_EQ(std::memcmp(&storage(fref), ©, 8), 0);
78 }
79 }
80
TEST_F(InlineFunctionRefTest,InvokeWithFunctionPointer)81 TEST_F(InlineFunctionRefTest, InvokeWithFunctionPointer) {
82 {
83 using FPtr = int (*)(int);
84 auto func = FPtr{[](auto integer) { return integer; }};
85 auto copy = func;
86
87 // we move into InlineFunctionRef but the move doesn't actually do anything
88 // destructive to the parameter
89 auto fref = InlineFunctionRef<int(int), 24>{std::move(func)};
90
91 EXPECT_EQ(fref(1), 1);
92 EXPECT_EQ(fref(2), 2);
93 EXPECT_EQ(fref(3), 3);
94
95 EXPECT_EQ(sizeof(func), 8);
96 EXPECT_EQ(std::memcmp(&storage(fref), ©, 8), 0);
97 }
98 }
99
TEST_F(InlineFunctionRefTest,InvokeWithBigLambda)100 TEST_F(InlineFunctionRefTest, InvokeWithBigLambda) {
101 {
102 auto data = std::array<std::uint8_t, 128>{};
103 for (auto i = std::size_t{0}; i < data.size(); ++i) {
104 data[i] = i;
105 }
106 auto func = [data](auto integer) { return integer + data[integer]; };
107 auto copy = func;
108 auto address = &func;
109 auto fref = InlineFunctionRef<int(int), 24>{std::move(func)};
110
111 EXPECT_EQ(fref(1), 2);
112 EXPECT_EQ(fref(2), 4);
113 EXPECT_EQ(fref(3), 6);
114
115 EXPECT_EQ(sizeof(copy), 128);
116 EXPECT_EQ(sizeof(copy), sizeof(func));
117 EXPECT_EQ(std::memcmp(&storage(fref), &address, 8), 0);
118 EXPECT_EQ(std::memcmp(©, &func, sizeof(copy)), 0);
119 }
120 }
121
TEST_F(InlineFunctionRefTest,Nullability)122 TEST_F(InlineFunctionRefTest, Nullability) {
123 auto fref = InlineFunctionRef<void(), 24>{nullptr};
124 EXPECT_FALSE(fref);
125 }
126
TEST_F(InlineFunctionRefTest,CopyConstruction)127 TEST_F(InlineFunctionRefTest, CopyConstruction) {
128 {
129 auto data = std::uint64_t{1};
130 auto func = [&](auto integer) { return integer + data; };
131 auto one = InlineFunctionRef<int(int), 24>{std::move(func)};
132 auto two = one;
133 EXPECT_EQ(std::memcmp(&one, &two, sizeof(one)), 0);
134
135 EXPECT_EQ(two(1), 2);
136 EXPECT_EQ(two(2), 3);
137 EXPECT_EQ(two(3), 4);
138
139 data = 2;
140 EXPECT_EQ(two(1), 3);
141 EXPECT_EQ(two(2), 4);
142 EXPECT_EQ(two(3), 5);
143
144 data = 3;
145 EXPECT_EQ(two(1), 4);
146 EXPECT_EQ(two(2), 5);
147 EXPECT_EQ(two(3), 6);
148 }
149
150 {
151 auto data = std::array<std::uint8_t, 128>{};
152 for (auto i = std::size_t{0}; i < data.size(); ++i) {
153 data[i] = i;
154 }
155 auto func = [data](auto integer) { return integer + data[integer]; };
156 auto one = InlineFunctionRef<int(int), 24>{std::move(func)};
157 auto two = one;
158 EXPECT_EQ(std::memcmp(&one, &two, sizeof(one)), 0);
159
160 EXPECT_EQ(two(1), 2);
161 EXPECT_EQ(two(2), 4);
162 EXPECT_EQ(two(3), 6);
163
164 EXPECT_EQ(sizeof(func), 128);
165 auto address = &func;
166 EXPECT_EQ(std::memcmp(&storage(two), &address, 8), 0);
167 }
168 }
169
TEST_F(InlineFunctionRefTest,TestTriviality)170 TEST_F(InlineFunctionRefTest, TestTriviality) {
171 {
172 auto lambda = []() {};
173 auto fref = InlineFunctionRef<void(), 24>{std::move(lambda)};
174 EXPECT_TRUE(std::is_trivially_destructible<decltype(fref)>{});
175 EXPECT_TRUE(folly::is_trivially_copyable<decltype(fref)>{});
176 }
177 {
178 auto integer = std::uint64_t{0};
179 auto lambda = [&]() { static_cast<void>(integer); };
180 auto fref = InlineFunctionRef<void(), 24>{std::move(lambda)};
181 EXPECT_TRUE(std::is_trivially_destructible<decltype(fref)>{});
182 EXPECT_TRUE(folly::is_trivially_copyable<decltype(fref)>{});
183 }
184 {
185 auto data = std::array<std::uint8_t, 128>{};
186 auto lambda = [data]() { static_cast<void>(data); };
187 auto fref = InlineFunctionRef<void(), 24>{std::move(lambda)};
188 EXPECT_TRUE(std::is_trivially_destructible<decltype(fref)>{});
189 EXPECT_TRUE(folly::is_trivially_copyable<decltype(fref)>{});
190 }
191 }
192
193 namespace {
194 template <typename Data>
195 class ConstQualifiedFunctor {
196 public:
operator ()()197 int operator()() { return 0; }
operator ()() const198 int operator()() const { return 1; }
199
200 Data data_;
201 };
202 } // namespace
203
TEST_F(InlineFunctionRefTest,CallConstQualifiedMethod)204 TEST_F(InlineFunctionRefTest, CallConstQualifiedMethod) {
205 {
206 auto small = ConstQualifiedFunctor<std::uint8_t>{};
207 auto fref = InlineFunctionRef<int(), 24>{std::move(small)};
208 EXPECT_EQ(fref(), 1);
209 }
210 {
211 const auto small = ConstQualifiedFunctor<std::uint8_t>{};
212 auto fref = InlineFunctionRef<int(), 24>{std::move(small)};
213 EXPECT_EQ(fref(), 1);
214 }
215 {
216 auto big = ConstQualifiedFunctor<std::array<std::uint8_t, 128>>{};
217 auto fref = InlineFunctionRef<int(), 24>{std::move(big)};
218 EXPECT_EQ(fref(), 1);
219 }
220 {
221 const auto big = ConstQualifiedFunctor<std::array<std::uint8_t, 128>>{};
222 auto fref = InlineFunctionRef<int(), 24>{std::move(big)};
223 EXPECT_EQ(fref(), 1);
224 }
225 }
226
227 namespace {
228 template <std::size_t Size>
229 using InlineFRef = InlineFunctionRef<void(), Size>;
230 } // namespace
231
TEST_F(InlineFunctionRefTest,TestSizeAlignment)232 TEST_F(InlineFunctionRefTest, TestSizeAlignment) {
233 EXPECT_EQ(sizeof(storage(std::declval<InlineFRef<16>&>())), 8);
234 EXPECT_EQ(alignof(decltype(storage(std::declval<InlineFRef<16>&>()))), 8);
235
236 EXPECT_EQ(sizeof(storage(std::declval<InlineFRef<24>&>())), 16);
237 EXPECT_EQ(alignof(decltype(storage(std::declval<InlineFRef<24>&>()))), 8);
238
239 EXPECT_EQ(sizeof(storage(std::declval<InlineFRef<32>&>())), 24);
240 EXPECT_EQ(alignof(decltype(storage(std::declval<InlineFRef<32>&>()))), 8);
241 }
242
243 namespace {
foo(int integer)244 int foo(int integer) {
245 return integer;
246 }
247 } // namespace
248
TEST_F(InlineFunctionRefTest,TestFunctionPointer)249 TEST_F(InlineFunctionRefTest, TestFunctionPointer) {
250 auto fref = InlineFunctionRef<int(int), 24>{&foo};
251 EXPECT_EQ(fref(1), 1);
252 EXPECT_EQ(fref(2), 2);
253 EXPECT_EQ(fref(3), 3);
254 }
255
256 namespace {
257 class alignas(16) MaxAligned {};
258 class alignas(32) MoreThanMaxAligned {};
259 } // namespace
260
TEST_F(InlineFunctionRefTest,TestMaxAlignment)261 TEST_F(InlineFunctionRefTest, TestMaxAlignment) {
262 {
263 auto aligned = MaxAligned{};
264 auto func = [aligned]() { static_cast<void>(aligned); };
265 auto fref = InlineFunctionRef<void(), 24>{std::move(func)};
266 auto address = &func;
267 EXPECT_EQ(std::memcmp(&storage(fref), &address, 8), 0);
268 }
269 {
270 auto aligned = MoreThanMaxAligned{};
271 auto func = [aligned]() { static_cast<void>(aligned); };
272 auto fref = InlineFunctionRef<void(), 24>{std::move(func)};
273 auto address = &func;
274 EXPECT_EQ(std::memcmp(&storage(fref), &address, 8), 0);
275 }
276 }
277
TEST_F(InlineFunctionRefTest,TestLValueConstructibility)278 TEST_F(InlineFunctionRefTest, TestLValueConstructibility) {
279 auto lambda = []() {};
280 EXPECT_TRUE((!std::is_constructible<
281 InlineFunctionRef<void(), 24>,
282 decltype(lambda)&>{}));
283 }
284
285 } // namespace detail
286 } // namespace folly
287