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