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