1/*
2 * Copyright 2016-present Facebook, Inc.
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/CachelinePadded.h>
18
19#include <type_traits>
20
21#include <folly/lang/Align.h>
22#include <folly/portability/GTest.h>
23
24using folly::CachelinePadded;
25
26static_assert(
27 std::is_standard_layout<CachelinePadded<int>>::value,
28 "CachelinePadded<T> must be standard-layout if T is.");
29
30static constexpr int kCachelineSize =
31 folly::hardware_destructive_interference_size;
32
33template <size_t dataSize, size_t alignment = alignof(void*)>
34struct alignas(alignment) SizedData {
35 SizedData() {
36 size_t i = 0;
37 for (auto& datum : data) {
38 datum = i++;
39 }
40 }
41
42 void doModifications() {
43 size_t i = 0;
44 for (auto& datum : data) {
45 EXPECT_EQ(static_cast<unsigned char>(i), datum);
46 ++i;
47 ++datum;
48 }
49 }
50
51 ~SizedData() {
52 size_t i = 1;
53 for (auto& datum : data) {
54 EXPECT_EQ(static_cast<unsigned char>(i), datum);
55 ++i;
56 }
57 }
58
59 unsigned char data[dataSize];
60};
61
62template <typename T, size_t N = 1>
63using SizedDataMimic = SizedData<N * sizeof(T), alignof(T)>;
64
65template <typename T>
66struct CachelinePaddedTests : ::testing::Test {};
67
68using CachelinePaddedTypes = ::testing::Types<
69 SizedData<kCachelineSize>,
70 SizedData<2 * kCachelineSize>,
71 SizedData<kCachelineSize / 2>,
72 SizedData<kCachelineSize + kCachelineSize / 2>,
73 // Mimic single basic types:
74 SizedDataMimic<folly::max_align_t>,
75 SizedDataMimic<void*>,
76 SizedDataMimic<long double>,
77 SizedDataMimic<double>,
78 SizedDataMimic<float>,
79 SizedDataMimic<long long>,
80 SizedDataMimic<long>,
81 SizedDataMimic<int>,
82 SizedDataMimic<short>,
83 SizedDataMimic<char>,
84 // Mimic small arrays of basic types:
85 SizedDataMimic<folly::max_align_t, 3>,
86 SizedDataMimic<void*, 3>,
87 SizedDataMimic<long double, 3>,
88 SizedDataMimic<double, 3>,
89 SizedDataMimic<float, 3>,
90 SizedDataMimic<long long, 3>,
91 SizedDataMimic<long, 3>,
92 SizedDataMimic<int, 3>,
93 SizedDataMimic<short, 3>,
94 SizedDataMimic<char, 3>,
95 // Mimic large arrays of basic types:
96 SizedDataMimic<folly::max_align_t, kCachelineSize + 3>,
97 SizedDataMimic<void*, kCachelineSize + 3>,
98 SizedDataMimic<long double, kCachelineSize + 3>,
99 SizedDataMimic<double, kCachelineSize + 3>,
100 SizedDataMimic<float, kCachelineSize + 3>,
101 SizedDataMimic<long long, kCachelineSize + 3>,
102 SizedDataMimic<long, kCachelineSize + 3>,
103 SizedDataMimic<int, kCachelineSize + 3>,
104 SizedDataMimic<short, kCachelineSize + 3>,
105 SizedDataMimic<char, kCachelineSize + 3>>;
106TYPED_TEST_CASE(CachelinePaddedTests, CachelinePaddedTypes);
107
108TYPED_TEST(CachelinePaddedTests, alignment) {
109 EXPECT_EQ(alignof(TypeParam), alignof(CachelinePadded<TypeParam>));
110}
111
112TYPED_TEST(CachelinePaddedTests, integrity) {
113 CachelinePadded<TypeParam> item;
114 item.get()->doModifications();
115}
116
117TYPED_TEST(CachelinePaddedTests, size) {
118 EXPECT_GT(
119 sizeof(TypeParam) + 2 * kCachelineSize,
120 sizeof(CachelinePadded<TypeParam>));
121 size_t const rawSize = sizeof(TypeParam);
122 size_t const rawAlign = alignof(TypeParam);
123 size_t const expectedPadding = kCachelineSize - (rawAlign % kCachelineSize);
124 size_t const expectedPaddedSize = rawSize + 2 * expectedPadding;
125 EXPECT_EQ(expectedPaddedSize, sizeof(CachelinePadded<TypeParam>));
126}
127
128TEST(CachelinePadded, PtrOperator) {
129 CachelinePadded<int> padded;
130 EXPECT_TRUE(padded.get() == padded.operator->());
131 EXPECT_TRUE(&*padded == padded.get());
132 const auto constPadded = CachelinePadded<int>{};
133 EXPECT_TRUE(constPadded.get() == constPadded.operator->());
134 EXPECT_TRUE(constPadded.get() == &*constPadded.get());
135}
136
137TEST(CachelinePadded, PropagatesConstness) {
138 struct OverloadedOnConst {
139 void assign(int* dst) {
140 *dst = 31415;
141 }
142 void assign(int* dst) const {
143 *dst = 271828;
144 }
145 };
146
147 CachelinePadded<OverloadedOnConst> padded;
148
149 int i = 0;
150 padded->assign(&i);
151 EXPECT_EQ(31415, i);
152
153 const auto constPadded = CachelinePadded<OverloadedOnConst>{};
154 constPadded->assign(&i);
155 EXPECT_EQ(271828, i);
156}
157
158TEST(CachelinePadded, ConstructsAndDestructs) {
159 enum LifetimeStatus {
160 kNone,
161 kConstructed,
162 kDestroyed,
163 };
164 struct WriteOnLifetimeOp {
165 explicit WriteOnLifetimeOp(LifetimeStatus* dst) : dst_(dst) {
166 *dst = kConstructed;
167 }
168 ~WriteOnLifetimeOp() {
169 *dst_ = kDestroyed;
170 }
171 LifetimeStatus* dst_;
172 };
173 LifetimeStatus status = kNone;
174 CachelinePadded<WriteOnLifetimeOp>* ptr =
175 new CachelinePadded<WriteOnLifetimeOp>(&status);
176 EXPECT_EQ(kConstructed, status);
177 delete ptr;
178 EXPECT_EQ(kDestroyed, status);
179}
180
181TEST(CachelinePadded, ConstructsAndDestructsArrays) {
182 static thread_local int numConstructions;
183 static thread_local int numDestructions;
184 numConstructions = 0;
185 numDestructions = 0;
186 struct LifetimeCountingClass {
187 LifetimeCountingClass() {
188 ++numConstructions;
189 }
190 ~LifetimeCountingClass() {
191 ++numDestructions;
192 }
193 };
194 const static int kNumItems = 123;
195 CachelinePadded<LifetimeCountingClass>* ptr =
196 new CachelinePadded<LifetimeCountingClass>[kNumItems];
197 EXPECT_EQ(kNumItems, numConstructions);
198 delete[] ptr;
199 EXPECT_EQ(kNumItems, numDestructions);
200}
201
202TEST(CachelinePadded, ForwardsCorrectly) {
203 struct RvalueOverloadedConstructor {
204 RvalueOverloadedConstructor(int* dst, int& /* ignored */) {
205 *dst = 0;
206 }
207 RvalueOverloadedConstructor(int* dst, int&& /* ignored */) {
208 *dst = 1;
209 }
210 };
211 int shouldBeZero = 12345;
212 int shouldBeOne = 67890;
213 {
214 int ignored = 42;
215 CachelinePadded<RvalueOverloadedConstructor> padded1(
216 &shouldBeZero, ignored);
217 CachelinePadded<RvalueOverloadedConstructor> padded2(
218 &shouldBeOne, static_cast<int&&>(ignored));
219 }
220 EXPECT_EQ(0, shouldBeZero);
221 EXPECT_EQ(1, shouldBeOne);
222}
223