1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #include "mozilla/Assertions.h"
8 #include "mozilla/AtomicBitfields.h"
9 
10 // This is a big macro mess, so let's summarize what's in here right up front:
11 //
12 // |TestDocumentationExample| is intended to be a copy-paste of the example
13 // in the macro's documentation, to make sure it's correct.
14 //
15 //
16 // |TestJammedWithFlags| tests using every bit of the type for bool flags.
17 // 64-bit isn't tested due to macro limitations.
18 //
19 //
20 // |TestLopsided| tests an instance with the following configuration:
21 //
22 // * a 1-bit boolean
23 // * an (N-1)-bit uintN_t
24 //
25 // It tests both orderings of these fields.
26 //
27 // Hopefully these are enough to cover all the nasty boundary conditions
28 // (that still compile).
29 
30 // ==================== TestDocumentationExample ========================
31 
32 struct MyType {
33   MOZ_ATOMIC_BITFIELDS(mAtomicFields, 8,
34                        ((bool, IsDownloaded, 1), (uint32_t, SomeData, 2),
35                         (uint8_t, OtherData, 5)))
36 
37   int32_t aNormalInteger;
38 
MyTypeMyType39   explicit MyType(uint32_t aSomeData) : aNormalInteger(7) {
40     StoreSomeData(aSomeData);
41     // Other bitfields were already default initialized to 0/false
42   }
43 };
44 
TestDocumentationExample()45 void TestDocumentationExample() {
46   MyType val(3);
47 
48   if (!val.LoadIsDownloaded()) {
49     val.StoreOtherData(2);
50     val.StoreIsDownloaded(true);
51   }
52 }
53 
54 // ====================== TestJammedWithFlags =========================
55 
56 #define TIMES_8(aFunc, aSeparator, aArgs) \
57   MOZ_FOR_EACH_SEPARATED(aFunc, aSeparator, aArgs, (1, 2, 3, 4, 5, 6, 7, 8))
58 #define TIMES_16(aFunc, aSeparator, aArgs) \
59   MOZ_FOR_EACH_SEPARATED(                  \
60       aFunc, aSeparator, aArgs,            \
61       (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16))
62 #define TIMES_32(aFunc, aSeparator, aArgs)                                    \
63   MOZ_FOR_EACH_SEPARATED(                                                     \
64       aFunc, aSeparator, aArgs,                                               \
65       (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, \
66        21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32))
67 
68 #define CHECK_BOOL(aIndex)                     \
69   MOZ_ASSERT(val.LoadFlag##aIndex() == false); \
70   val.StoreFlag##aIndex(true);                 \
71   MOZ_ASSERT(val.LoadFlag##aIndex() == true);  \
72   val.StoreFlag##aIndex(false);                \
73   MOZ_ASSERT(val.LoadFlag##aIndex() == false);
74 
75 #define GENERATE_TEST_JAMMED_WITH_FLAGS(aSize) \
76   void TestJammedWithFlags##aSize() {          \
77     JammedWithFlags##aSize val;                \
78     TIMES_##aSize(CHECK_BOOL, (;), ());        \
79   }
80 
81 #define TEST_JAMMED_WITH_FLAGS(aSize) TestJammedWithFlags##aSize();
82 
83 // ========================= TestLopsided ===========================
84 
85 #define GENERATE_TEST_LOPSIDED_FUNC(aSide, aSize)          \
86   void TestLopsided##aSide##aSize() {                      \
87     Lopsided##aSide##aSize val;                            \
88     MOZ_ASSERT(val.LoadHappyLittleBit() == false);         \
89     MOZ_ASSERT(val.LoadLargeAndInCharge() == 0);           \
90     val.StoreHappyLittleBit(true);                         \
91     MOZ_ASSERT(val.LoadHappyLittleBit() == true);          \
92     MOZ_ASSERT(val.LoadLargeAndInCharge() == 0);           \
93     val.StoreLargeAndInCharge(1);                          \
94     MOZ_ASSERT(val.LoadHappyLittleBit() == true);          \
95     MOZ_ASSERT(val.LoadLargeAndInCharge() == 1);           \
96     val.StoreLargeAndInCharge(0);                          \
97     MOZ_ASSERT(val.LoadHappyLittleBit() == true);          \
98     MOZ_ASSERT(val.LoadLargeAndInCharge() == 0);           \
99     uint##aSize##_t size = aSize;                          \
100     uint##aSize##_t int_max = (~(1ull << (size - 1))) - 1; \
101     val.StoreLargeAndInCharge(int_max);                    \
102     MOZ_ASSERT(val.LoadHappyLittleBit() == true);          \
103     MOZ_ASSERT(val.LoadLargeAndInCharge() == int_max);     \
104     val.StoreHappyLittleBit(false);                        \
105     MOZ_ASSERT(val.LoadHappyLittleBit() == false);         \
106     MOZ_ASSERT(val.LoadLargeAndInCharge() == int_max);     \
107     val.StoreLargeAndInCharge(int_max);                    \
108     MOZ_ASSERT(val.LoadHappyLittleBit() == false);         \
109     MOZ_ASSERT(val.LoadLargeAndInCharge() == int_max);     \
110   }
111 
112 #define GENERATE_TEST_LOPSIDED(aSize)                                        \
113   struct LopsidedA##aSize {                                                  \
114     MOZ_ATOMIC_BITFIELDS(mAtomicFields, aSize,                               \
115                          ((bool, HappyLittleBit, 1),                         \
116                           (uint##aSize##_t, LargeAndInCharge, ((aSize)-1)))) \
117   };                                                                         \
118   struct LopsidedB##aSize {                                                  \
119     MOZ_ATOMIC_BITFIELDS(mAtomicFields, aSize,                               \
120                          ((uint##aSize##_t, LargeAndInCharge, ((aSize)-1)),  \
121                           (bool, HappyLittleBit, 1)))                        \
122   };                                                                         \
123   GENERATE_TEST_LOPSIDED_FUNC(A, aSize);                                     \
124   GENERATE_TEST_LOPSIDED_FUNC(B, aSize);
125 
126 #define TEST_LOPSIDED(aSize) \
127   TestLopsidedA##aSize();    \
128   TestLopsidedB##aSize();
129 
130 // ==================== generate and run the tests ======================
131 
132 // There's an unknown bug in clang-cl-9 (used for win64-ccov) that makes
133 // generating these with the TIMES_N macro not work. So these are written out
134 // explicitly to unbork CI.
135 struct JammedWithFlags8 {
136   MOZ_ATOMIC_BITFIELDS(mAtomicFields, 8,
137                        ((bool, Flag1, 1), (bool, Flag2, 1), (bool, Flag3, 1),
138                         (bool, Flag4, 1), (bool, Flag5, 1), (bool, Flag6, 1),
139                         (bool, Flag7, 1), (bool, Flag8, 1)))
140 };
141 
142 struct JammedWithFlags16 {
143   MOZ_ATOMIC_BITFIELDS(mAtomicFields, 16,
144                        ((bool, Flag1, 1), (bool, Flag2, 1), (bool, Flag3, 1),
145                         (bool, Flag4, 1), (bool, Flag5, 1), (bool, Flag6, 1),
146                         (bool, Flag7, 1), (bool, Flag8, 1), (bool, Flag9, 1),
147                         (bool, Flag10, 1), (bool, Flag11, 1), (bool, Flag12, 1),
148                         (bool, Flag13, 1), (bool, Flag14, 1), (bool, Flag15, 1),
149                         (bool, Flag16, 1)))
150 };
151 
152 struct JammedWithFlags32 {
153   MOZ_ATOMIC_BITFIELDS(mAtomicFields, 32,
154                        ((bool, Flag1, 1), (bool, Flag2, 1), (bool, Flag3, 1),
155                         (bool, Flag4, 1), (bool, Flag5, 1), (bool, Flag6, 1),
156                         (bool, Flag7, 1), (bool, Flag8, 1), (bool, Flag9, 1),
157                         (bool, Flag10, 1), (bool, Flag11, 1), (bool, Flag12, 1),
158                         (bool, Flag13, 1), (bool, Flag14, 1), (bool, Flag15, 1),
159                         (bool, Flag16, 1), (bool, Flag17, 1), (bool, Flag18, 1),
160                         (bool, Flag19, 1), (bool, Flag20, 1), (bool, Flag21, 1),
161                         (bool, Flag22, 1), (bool, Flag23, 1), (bool, Flag24, 1),
162                         (bool, Flag25, 1), (bool, Flag26, 1), (bool, Flag27, 1),
163                         (bool, Flag28, 1), (bool, Flag29, 1), (bool, Flag30, 1),
164                         (bool, Flag31, 1), (bool, Flag32, 1)))
165 };
166 
167 GENERATE_TEST_JAMMED_WITH_FLAGS(8)
168 GENERATE_TEST_JAMMED_WITH_FLAGS(16)
169 GENERATE_TEST_JAMMED_WITH_FLAGS(32)
170 // MOZ_FOR_EACH_64 doesn't exist :(
171 
172 GENERATE_TEST_LOPSIDED(8)
173 GENERATE_TEST_LOPSIDED(16)
174 GENERATE_TEST_LOPSIDED(32)
175 GENERATE_TEST_LOPSIDED(64)
176 
main()177 int main() {
178   TestDocumentationExample();
179 
180   TEST_JAMMED_WITH_FLAGS(8);
181   TEST_JAMMED_WITH_FLAGS(16);
182   TEST_JAMMED_WITH_FLAGS(32);
183 
184   TEST_LOPSIDED(8);
185   TEST_LOPSIDED(16);
186   TEST_LOPSIDED(32);
187   TEST_LOPSIDED(64);
188   return 0;
189 }
190