add missing include to ThreadId.h
[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 const int kCachelineSize = folly::detail::CacheLocality::kFalseSharingRange;
30
31 template <int dataSize>
32 struct SizedData {
33   SizedData() {
34     for (unsigned i = 0; i < dataSize; ++i) {
35       data[i] = i;
36     }
37   }
38
39   void doModifications() {
40     for (unsigned i = 0; i < dataSize; ++i) {
41       EXPECT_EQ(static_cast<unsigned char>(i), data[i]);
42       ++data[i];
43     }
44   }
45
46   ~SizedData() {
47     for (unsigned i = 0; i < dataSize; ++i) {
48       EXPECT_EQ(static_cast<unsigned char>(i + 1), data[i]);
49     }
50   }
51
52   unsigned char data[dataSize];
53 };
54
55 using ExactlyCachelineSized = SizedData<kCachelineSize>;
56 using DoubleCachelineSized = SizedData<2 * kCachelineSize>;
57 using BelowCachelineSized = SizedData<kCachelineSize / 2>;
58 using AboveCachelineSized = SizedData<kCachelineSize + kCachelineSize / 2>;
59
60 TEST(CachelinePadded, Exact) {
61   EXPECT_EQ(kCachelineSize, sizeof(CachelinePadded<ExactlyCachelineSized>));
62   CachelinePadded<ExactlyCachelineSized> item;
63   item.get()->doModifications();
64   EXPECT_TRUE(
65       reinterpret_cast<CachelinePadded<ExactlyCachelineSized>*>(item.get()) ==
66       &item);
67 }
68
69 TEST(CachelinePadded, Double) {
70   EXPECT_EQ(2 * kCachelineSize, sizeof(CachelinePadded<DoubleCachelineSized>));
71   CachelinePadded<DoubleCachelineSized> item;
72   item.get()->doModifications();
73   EXPECT_TRUE(
74       reinterpret_cast<CachelinePadded<DoubleCachelineSized>*>(item.get()) ==
75       &item);
76 }
77
78 TEST(CachelinePadded, Below) {
79   EXPECT_EQ(kCachelineSize, sizeof(CachelinePadded<BelowCachelineSized>));
80   CachelinePadded<BelowCachelineSized> item;
81   item.get()->doModifications();
82   EXPECT_TRUE(
83       reinterpret_cast<CachelinePadded<BelowCachelineSized>*>(item.get()) ==
84       &item);
85 }
86
87 TEST(CachelinePadded, Above) {
88   EXPECT_EQ(2 * kCachelineSize, sizeof(CachelinePadded<AboveCachelineSized>));
89   CachelinePadded<AboveCachelineSized> item;
90   item.get()->doModifications();
91   EXPECT_TRUE(
92       reinterpret_cast<CachelinePadded<AboveCachelineSized>*>(item.get()) ==
93       &item);
94 }
95
96 TEST(CachelinePadded, CanBeCastedBack) {
97   CachelinePadded<int> padded;
98   CachelinePadded<int>* ptr =
99       reinterpret_cast<CachelinePadded<int>*>(padded.get());
100   EXPECT_EQ(&padded, ptr);
101 }
102
103 TEST(CachelinePadded, PtrOperator) {
104   CachelinePadded<int> padded;
105   EXPECT_TRUE(padded.get() == padded.operator->());
106   EXPECT_TRUE(&*padded == padded.get());
107   const CachelinePadded<int> constPadded;
108   EXPECT_TRUE(constPadded.get() == constPadded.operator->());
109   EXPECT_TRUE(constPadded.get() == &*constPadded.get());
110 }
111
112 TEST(CachelinePadded, PropagatesConstness) {
113   struct OverloadedOnConst {
114     void assign(int* dst) {
115       *dst = 31415;
116     }
117     void assign(int* dst) const {
118       *dst = 271828;
119     }
120   };
121
122   CachelinePadded<OverloadedOnConst> padded;
123
124   int i = 0;
125   padded->assign(&i);
126   EXPECT_EQ(31415, i);
127
128   const CachelinePadded<OverloadedOnConst> constPadded;
129   constPadded->assign(&i);
130   EXPECT_EQ(271828, i);
131 }
132
133 TEST(CachelinePadded, ConstructsAndDestructs) {
134   enum LifetimeStatus {
135     kNone,
136     kConstructed,
137     kDestroyed,
138   };
139   struct WriteOnLifetimeOp {
140     explicit WriteOnLifetimeOp(LifetimeStatus* dst) : dst_(dst) {
141       *dst = kConstructed;
142     }
143     ~WriteOnLifetimeOp() {
144       *dst_ = kDestroyed;
145     }
146     LifetimeStatus* dst_;
147   };
148   LifetimeStatus status = kNone;
149   CachelinePadded<WriteOnLifetimeOp>* ptr =
150       new CachelinePadded<WriteOnLifetimeOp>(&status);
151   EXPECT_EQ(kConstructed, status);
152   delete ptr;
153   EXPECT_EQ(kDestroyed, status);
154 }
155
156 TEST(CachelinePadded, ConstructsAndDestructsArrays) {
157   static thread_local int numConstructions;
158   static thread_local int numDestructions;
159   numConstructions = 0;
160   numDestructions = 0;
161   struct LifetimeCountingClass {
162     LifetimeCountingClass() {
163       ++numConstructions;
164     }
165     ~LifetimeCountingClass() {
166       ++numDestructions;
167     }
168   };
169   const static int kNumItems = 123;
170   CachelinePadded<LifetimeCountingClass>* ptr =
171       new CachelinePadded<LifetimeCountingClass>[kNumItems];
172   EXPECT_EQ(kNumItems, numConstructions);
173   delete[] ptr;
174   EXPECT_EQ(kNumItems, numDestructions);
175 }
176
177 TEST(CachelinePadded, ForwardsCorrectly) {
178   struct RvalueOverloadedConstructor {
179     RvalueOverloadedConstructor(int* dst, int& /* ignored */) {
180       *dst = 0;
181     }
182     RvalueOverloadedConstructor(int* dst, int&& /* ignored */) {
183       *dst = 1;
184     }
185   };
186   int shouldBeZero = 12345;
187   int shouldBeOne = 67890;
188   {
189     int ignored = 42;
190     CachelinePadded<RvalueOverloadedConstructor> padded1(
191         &shouldBeZero, ignored);
192     CachelinePadded<RvalueOverloadedConstructor> padded2(
193         &shouldBeOne, static_cast<int&&>(ignored));
194   }
195   EXPECT_EQ(0, shouldBeZero);
196   EXPECT_EQ(1, shouldBeOne);
197 }