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), &copy, 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), &copy, 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), &copy, 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(&copy, &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