AtomicBitfields.h (21825B)
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 #ifndef mozilla_AtomicBitfields_h 8 #define mozilla_AtomicBitfields_h 9 10 #include "mozilla/Assertions.h" 11 #include "mozilla/MacroArgs.h" 12 #include "mozilla/MacroForEach.h" 13 14 #include <cstdint> 15 #include <type_traits> 16 17 #ifdef __wasi__ 18 # include "mozilla/WasiAtomic.h" 19 #else 20 # include <atomic> 21 #endif // __wasi__ 22 23 namespace mozilla { 24 25 // Creates a series of atomic bitfields. 26 // 27 // |aBitfields| is the name of the underlying storage for the bitfields. 28 // |aBitFieldsSize| is the size of the underlying storage (8, 16, 32, or 64). 29 // 30 // Bitfields are specified as a triplet of (type, name, size), which mirrors 31 // the way you declare native C++ bitfields (bool mMyField1: 1). Trailing 32 // commas are not supported in the list of bitfields. 33 // 34 // Signed integer types are not supported by this Macro to avoid dealing with 35 // packing/unpacking the sign bit and C++'s general messiness around signed 36 // integer representations not being fully defined. 37 // 38 // You cannot request a single field that's the 39 // size of the the entire bitfield storage. Just use a normal atomic integer! 40 // 41 // 42 // ========================== SEMANTICS AND SAFETY ============================ 43 // 44 // All fields are default-initialized to 0. 45 // 46 // In debug builds, storing a value to a bitfield that's larger than its bits 47 // can fit will trigger an assertion. In release builds, the value will just be 48 // masked off. 49 // 50 // If you request anything unsupported by this macro it should result in 51 // a compile-time error (either a static assert or just weird macro errors). 52 // For instance, this macro will statically prevent using more bits than 53 // |aBitFieldsSize|, so specifying the size is just to prevent accidentally 54 // making the storage bigger. 55 // 56 // Each field will get a Load$NAME and Store$Name method which will atomically 57 // load and store the requested value with a Sequentially Consistent memory 58 // order (to be on the safe side). Storing a field requires a compare-exchange, 59 // so a thread may get stalled if there's a lot of contention on the bitfields. 60 // 61 // 62 // ============================== MOTIVATION ================================== 63 // 64 // You might be wondering: why would I need atomic bitfields? Well as it turns 65 // out, bitfields and concurrency mess a lot of people up! 66 // 67 // CPUs don't have operations to write to a handful of bits -- they generally 68 // only have the precision of a byte. So when you use C++'s native bitfields, 69 // the compiler generates code to mask and shift the values in for you. This 70 // means writing to a single field will actually overwrite all the other 71 // bitfields that are packed in with it! 72 // 73 // In single-threaded code this is fine; the old values are loaded and written 74 // back by the compiler's generated code. But in concurrent code, it means 75 // that accessing two different fields can be an unexpected Data Race (which is 76 // Undefined Behavior!). 77 // 78 // By using MOZ_ATOMIC_BITFIELDS, you protect yourself from these Data Races, 79 // and don't have to worry about writes getting lost. 80 // 81 // 82 // ================================ EXAMPLE =================================== 83 // 84 // #include "mozilla/AtomicBitfields.h" 85 // #include <stdint.h> 86 // 87 // 88 // struct MyType { 89 // MOZ_ATOMIC_BITFIELDS(mAtomicFields, 8, ( 90 // (bool, IsDownloaded, 1), 91 // (uint32_t, SomeData, 2), 92 // (uint8_t, OtherData, 5) 93 // )) 94 // 95 // int32_t aNormalInteger; 96 // 97 // explicit MyType(uint32_t aSomeData): aNormalInteger(7) { 98 // StoreSomeData(aSomeData); 99 // // Other bitfields were already default initialized to 0/false 100 // } 101 // }; 102 // 103 // 104 // int main() { 105 // MyType val(3); 106 // 107 // if (!val.LoadIsDownloaded()) { 108 // val.StoreOtherData(2); 109 // val.StoreIsDownloaded(true); 110 // } 111 // } 112 // 113 // 114 // ============================== GENERATED =================================== 115 // 116 // This macro is a real mess to read because, well, it's a macro. So for the 117 // sake of anyone who has to review or modify its internals, here's a rough 118 // sketch of what the above example would expand to: 119 // 120 // struct MyType { 121 // // The actual storage of the bitfields, initialized to 0. 122 // std::atomic_uint8_t mAtomicFields{0}; 123 // 124 // // How many bits were actually used (in this case, all of them). 125 // static const size_t mAtomicFields_USED_BITS = 8; 126 // 127 // // The offset values for each field. 128 // static const size_t mAtomicFieldsIsDownloaded = 0; 129 // static const size_t mAtomicFieldsSomeData = 1; 130 // static const size_t mAtomicFieldsOtherData = 3; 131 // 132 // // Quick safety guard to prevent capacity overflow. 133 // static_assert(mAtomicFields_USED_BITS <= 8); 134 // 135 // // Asserts that fields are reasonable. 136 // static_assert(8>1, "mAtomicFields: MOZ_ATOMIC_BITFIELDS field too big"); 137 // static_assert(std::is_unsigned<bool>(), "mAtomicFields: 138 // MOZ_ATOMIC_BITFIELDS doesn't support signed payloads"); 139 // // ...and so on 140 // 141 // // Load/Store methods for all the fields. 142 // 143 // bool LoadIsDownloaded() { ... } 144 // void StoreIsDownloaded(bool aValue) { ... } 145 // 146 // uint32_t LoadSomeData() { ... } 147 // void StoreSomeData(uint32_t aValue) { ... } 148 // 149 // uint8_t LoadOtherData() { ... } 150 // void StoreOtherData(uint8_t aValue) { ... } 151 // 152 // 153 // // Remainder of the struct body continues normally. 154 // int32_t aNormalInteger; 155 // explicit MyType(uint32_t aSomeData): aNormalInteger(7) { 156 // StoreSomeData(aSomeData); 157 // // Other bitfields were already default initialized to 0/false. 158 // } 159 // } 160 // 161 // Also if you're wondering why there's so many MOZ_CONCAT's -- it's because 162 // the preprocessor sometimes gets confused if we use ## on certain arguments. 163 // MOZ_CONCAT reliably kept the preprocessor happy, sorry it's so ugly! 164 // 165 // 166 // ==================== FIXMES / FUTURE WORK ================================== 167 // 168 // * It would be nice if LoadField could be IsField for booleans. 169 // 170 // * For the case of setting something to all 1's or 0's, we can use 171 // |fetch_or| or |fetch_and| instead of |compare_exchange_weak|. Is this 172 // worth providing? (Possibly for 1-bit boolean fields?) 173 // 174 // * Try harder to hide the atomic/enum/array internals from 175 // the outer struct? 176 // 177 #define MOZ_ATOMIC_BITFIELDS(aBitfields, aBitfieldsSize, aFields) \ 178 std::atomic_uint##aBitfieldsSize##_t aBitfields{0}; \ 179 \ 180 static const size_t MOZ_CONCAT(aBitfields, _USED_BITS) = \ 181 MOZ_FOR_EACH_SEPARATED(MOZ_ATOMIC_BITFIELDS_FIELD_SIZE, (+), (), \ 182 aFields); \ 183 \ 184 MOZ_ROLL_EACH(MOZ_ATOMIC_BITFIELDS_OFFSET_HELPER1, (aBitfields, ), aFields) \ 185 \ 186 static_assert(MOZ_CONCAT(aBitfields, _USED_BITS) <= aBitfieldsSize, \ 187 #aBitfields ": Maximum bits (" #aBitfieldsSize \ 188 ") exceeded for MOZ_ATOMIC_BITFIELDS instance"); \ 189 \ 190 MOZ_FOR_EACH(MOZ_ATOMIC_BITFIELDS_FIELD_HELPER, \ 191 (aBitfields, aBitfieldsSize, ), aFields) 192 193 // Just a helper to unpack the head of the list. 194 #define MOZ_ATOMIC_BITFIELDS_OFFSET_HELPER1(aBitfields, aFields) \ 195 MOZ_ATOMIC_BITFIELDS_OFFSET_HELPER2(aBitfields, MOZ_ARG_1 aFields, aFields); 196 197 // Just a helper to unpack the name and call the real function. 198 #define MOZ_ATOMIC_BITFIELDS_OFFSET_HELPER2(aBitfields, aField, aFields) \ 199 MOZ_ATOMIC_BITFIELDS_OFFSET(aBitfields, MOZ_ARG_2 aField, aFields) 200 201 // To compute the offset of a field, why sum up all the offsets after it 202 // (inclusive) and subtract that from the total sum itself. We do this to swap 203 // the rolling sum that |MOZ_ROLL_EACH| gets us from descending to ascending. 204 #define MOZ_ATOMIC_BITFIELDS_OFFSET(aBitfields, aFieldName, aFields) \ 205 static const size_t MOZ_CONCAT(aBitfields, aFieldName) = \ 206 MOZ_CONCAT(aBitfields, _USED_BITS) - \ 207 (MOZ_FOR_EACH_SEPARATED(MOZ_ATOMIC_BITFIELDS_FIELD_SIZE, (+), (), \ 208 aFields)); 209 210 // Just a more clearly named way of unpacking the size. 211 #define MOZ_ATOMIC_BITFIELDS_FIELD_SIZE(aArgs) MOZ_ARG_3 aArgs 212 213 // Just a helper to unpack the tuple and call the real function. 214 #define MOZ_ATOMIC_BITFIELDS_FIELD_HELPER(aBitfields, aBitfieldsSize, aArgs) \ 215 MOZ_ATOMIC_BITFIELDS_FIELD(aBitfields, aBitfieldsSize, MOZ_ARG_1 aArgs, \ 216 MOZ_ARG_2 aArgs, MOZ_ARG_3 aArgs) 217 218 // We need to disable this with coverity because it doesn't like checking that 219 // booleans are < 2 (because they always are). 220 #ifdef __COVERITY__ 221 # define MOZ_ATOMIC_BITFIELDS_STORE_GUARD(aValue, aFieldSize) 222 #else 223 # define MOZ_ATOMIC_BITFIELDS_STORE_GUARD(aValue, aFieldSize) \ 224 MOZ_ASSERT(((uint64_t)aValue) < (1ull << aFieldSize), \ 225 "Stored value exceeded capacity of bitfield!") 226 #endif 227 228 // Generates the Load and Store methods for each field. 229 // 230 // Some comments here because inline macro comments are a pain in the neck: 231 // 232 // Most of the locals are forward declared to minimize messy macroified 233 // type declaration. Also a lot of locals are used to try to make things 234 // a little more clear, while also avoiding integer promotion issues. 235 // This is why some locals are literally just copying a value we already have: 236 // to force it to the right size. 237 // 238 // There's an annoying overflow case where a bitfields instance has a field 239 // that is the same size as the bitfields. Rather than trying to handle that, 240 // we just static_assert against it. 241 // 242 // 243 // BITMATH EXPLAINED: 244 // 245 // For |Load$Name|: 246 // 247 // mask = ((1 << fieldSize) - 1) << offset 248 // 249 // If you subtract 1 from a value with 1 bit set you get all 1's below that bit. 250 // This is perfect for ANDing out |fieldSize| bits. We shift by |offset| to get 251 // it in the right place. 252 // 253 // value = (aBitfields.load() & mask) >> offset 254 // 255 // This sets every bit we're not interested in to 0. Shifting the result by 256 // |offset| converts the value back to its native format, ready to be cast 257 // up to an integer type. 258 // 259 // 260 // For |Store$Name|: 261 // 262 // packedValue = (resizedValue << offset) & mask 263 // 264 // This converts a native value to the packed format. If the value is in bounds, 265 // the AND will do nothing. If it's out of bounds (not checked in release), 266 // then it will cause the value to wrap around by modulo 2^aFieldSize, just like 267 // a normal uint. 268 // 269 // clearedValue = oldValue & ~mask; 270 // 271 // This clears the bits where our field is stored on our bitfield storage by 272 // ANDing it with an inverted (NOTed) mask. 273 // 274 // newValue = clearedValue | packedValue; 275 // 276 // Once we have |packedValue| and |clearedValue| they just need to be ORed 277 // together to merge the new field value with the old values of all the other 278 // fields. 279 // 280 // This last step is done in a while loop because someone else can modify 281 // the bits before we have a chance to. If we didn't guard against this, 282 // our write would undo the write the other thread did. |compare_exchange_weak| 283 // is specifically designed to handle this. We give it what we expect the 284 // current value to be, and what we want it to be. If someone else modifies 285 // the bitfields before us, then we will reload the value and try again. 286 // 287 // Note that |compare_exchange_weak| writes back the actual value to the 288 // "expected" argument (it's passed by-reference), so we don't need to do 289 // another load in the body of the loop when we fail to write our result. 290 #define MOZ_ATOMIC_BITFIELDS_FIELD(aBitfields, aBitfieldsSize, aFieldType, \ 291 aFieldName, aFieldSize) \ 292 static_assert(aBitfieldsSize > aFieldSize, \ 293 #aBitfields ": MOZ_ATOMIC_BITFIELDS field too big"); \ 294 static_assert(std::is_unsigned<aFieldType>(), #aBitfields \ 295 ": MOZ_ATOMIC_BITFIELDS doesn't support signed payloads"); \ 296 \ 297 aFieldType MOZ_CONCAT(Load, aFieldName)() const { \ 298 uint##aBitfieldsSize##_t fieldSize, mask, masked, value; \ 299 size_t offset = MOZ_CONCAT(aBitfields, aFieldName); \ 300 fieldSize = aFieldSize; \ 301 mask = ((1ull << fieldSize) - 1ull) << offset; \ 302 masked = aBitfields.load() & mask; \ 303 value = (masked >> offset); \ 304 return value; \ 305 } \ 306 \ 307 void MOZ_CONCAT(Store, aFieldName)(aFieldType aValue) { \ 308 MOZ_ATOMIC_BITFIELDS_STORE_GUARD(aValue, aFieldSize); \ 309 uint##aBitfieldsSize##_t fieldSize, mask, resizedValue, packedValue, \ 310 oldValue, clearedValue, newValue; \ 311 size_t offset = MOZ_CONCAT(aBitfields, aFieldName); \ 312 fieldSize = aFieldSize; \ 313 mask = ((1ull << fieldSize) - 1ull) << offset; \ 314 resizedValue = aValue; \ 315 packedValue = (resizedValue << offset) & mask; \ 316 oldValue = aBitfields.load(); \ 317 do { \ 318 clearedValue = oldValue & ~mask; \ 319 newValue = clearedValue | packedValue; \ 320 } while (!aBitfields.compare_exchange_weak(oldValue, newValue)); \ 321 } 322 323 // OK SO THIS IS A GROSS HACK. GCC 10.2 (and below) has a bug[1] where it 324 // doesn't allow a static array to reference itself in its initializer, so we 325 // need to create a hacky way to produce a rolling sum of all the offsets. 326 // 327 // To do this, we make a tweaked version of |MOZ_FOR_EACH| which instead of 328 // passing just one argument to |aMacro| it passes the remaining values of 329 // |aArgs|. 330 // 331 // This allows us to expand an input (a, b, c, d) quadratically to: 332 // 333 // int sum1 = a + b + c + d; 334 // int sum2 = b + c + d; 335 // int sum3 = c + d; 336 // int sum4 = d; 337 // 338 // So all of this is a copy-paste of |MOZ_FOR_EACH| except the definition 339 // of |MOZ_FOR_EACH_HELPER| no longer extracts an argument with |MOZ_ARG_1|. 340 // Also this is restricted to 32 arguments just to reduce footprint a little. 341 // 342 // If the GCC bug is ever fixed, then this hack can be removed, and we can 343 // use the non-quadratic version that was originally written[2]. In case 344 // that link dies, a brief summary of that implementation: 345 // 346 // * Associate each field with an index by creating an `enum class` with 347 // entries for each field (an existing gecko patten). 348 // 349 // * Calculate offsets with a constexpr static array whose initializer 350 // self-referentially adds the contents of the previous index to the 351 // compute the current one. 352 // 353 // * Index into this array with the enum. 354 // 355 // [1] https://gcc.gnu.org/bugzilla/show_bug.cgi?id=97234 356 // [2]: https://phabricator.services.mozilla.com/D91622?id=346499 357 #define MOZ_ROLL_EACH_EXPAND_HELPER(...) __VA_ARGS__ 358 #define MOZ_ROLL_EACH_GLUE(a, b) a b 359 #define MOZ_ROLL_EACH_SEPARATED(aMacro, aSeparator, aFixedArgs, aArgs) \ 360 MOZ_ROLL_EACH_GLUE(MOZ_PASTE_PREFIX_AND_ARG_COUNT( \ 361 MOZ_ROLL_EACH_, MOZ_ROLL_EACH_EXPAND_HELPER aArgs), \ 362 (aMacro, aSeparator, aFixedArgs, aArgs)) 363 #define MOZ_ROLL_EACH(aMacro, aFixedArgs, aArgs) \ 364 MOZ_ROLL_EACH_SEPARATED(aMacro, (), aFixedArgs, aArgs) 365 366 #define MOZ_ROLL_EACH_HELPER_GLUE(a, b) a b 367 #define MOZ_ROLL_EACH_HELPER(aMacro, aFixedArgs, aArgs) \ 368 MOZ_ROLL_EACH_HELPER_GLUE(aMacro, \ 369 (MOZ_ROLL_EACH_EXPAND_HELPER aFixedArgs aArgs)) 370 371 #define MOZ_ROLL_EACH_0(m, s, fa, a) 372 #define MOZ_ROLL_EACH_1(m, s, fa, a) MOZ_ROLL_EACH_HELPER(m, fa, a) 373 #define MOZ_ROLL_EACH_2(m, s, fa, a) \ 374 MOZ_ROLL_EACH_HELPER(m, fa, a) \ 375 MOZ_ROLL_EACH_EXPAND_HELPER s MOZ_ROLL_EACH_1(m, s, fa, (MOZ_ARGS_AFTER_1 a)) 376 #define MOZ_ROLL_EACH_3(m, s, fa, a) \ 377 MOZ_ROLL_EACH_HELPER(m, fa, a) \ 378 MOZ_ROLL_EACH_EXPAND_HELPER s MOZ_ROLL_EACH_2(m, s, fa, (MOZ_ARGS_AFTER_1 a)) 379 #define MOZ_ROLL_EACH_4(m, s, fa, a) \ 380 MOZ_ROLL_EACH_HELPER(m, fa, a) \ 381 MOZ_ROLL_EACH_EXPAND_HELPER s MOZ_ROLL_EACH_3(m, s, fa, (MOZ_ARGS_AFTER_1 a)) 382 #define MOZ_ROLL_EACH_5(m, s, fa, a) \ 383 MOZ_ROLL_EACH_HELPER(m, fa, a) \ 384 MOZ_ROLL_EACH_EXPAND_HELPER s MOZ_ROLL_EACH_4(m, s, fa, (MOZ_ARGS_AFTER_1 a)) 385 #define MOZ_ROLL_EACH_6(m, s, fa, a) \ 386 MOZ_ROLL_EACH_HELPER(m, fa, a) \ 387 MOZ_ROLL_EACH_EXPAND_HELPER s MOZ_ROLL_EACH_5(m, s, fa, (MOZ_ARGS_AFTER_1 a)) 388 #define MOZ_ROLL_EACH_7(m, s, fa, a) \ 389 MOZ_ROLL_EACH_HELPER(m, fa, a) \ 390 MOZ_ROLL_EACH_EXPAND_HELPER s MOZ_ROLL_EACH_6(m, s, fa, (MOZ_ARGS_AFTER_1 a)) 391 #define MOZ_ROLL_EACH_8(m, s, fa, a) \ 392 MOZ_ROLL_EACH_HELPER(m, fa, a) \ 393 MOZ_ROLL_EACH_EXPAND_HELPER s MOZ_ROLL_EACH_7(m, s, fa, (MOZ_ARGS_AFTER_1 a)) 394 #define MOZ_ROLL_EACH_9(m, s, fa, a) \ 395 MOZ_ROLL_EACH_HELPER(m, fa, a) \ 396 MOZ_ROLL_EACH_EXPAND_HELPER s MOZ_ROLL_EACH_8(m, s, fa, (MOZ_ARGS_AFTER_1 a)) 397 #define MOZ_ROLL_EACH_10(m, s, fa, a) \ 398 MOZ_ROLL_EACH_HELPER(m, fa, a) \ 399 MOZ_ROLL_EACH_EXPAND_HELPER s MOZ_ROLL_EACH_9(m, s, fa, (MOZ_ARGS_AFTER_1 a)) 400 #define MOZ_ROLL_EACH_11(m, s, fa, a) \ 401 MOZ_ROLL_EACH_HELPER(m, fa, a) \ 402 MOZ_ROLL_EACH_EXPAND_HELPER s MOZ_ROLL_EACH_10(m, s, fa, (MOZ_ARGS_AFTER_1 a)) 403 #define MOZ_ROLL_EACH_12(m, s, fa, a) \ 404 MOZ_ROLL_EACH_HELPER(m, fa, a) \ 405 MOZ_ROLL_EACH_EXPAND_HELPER s MOZ_ROLL_EACH_11(m, s, fa, (MOZ_ARGS_AFTER_1 a)) 406 #define MOZ_ROLL_EACH_13(m, s, fa, a) \ 407 MOZ_ROLL_EACH_HELPER(m, fa, a) \ 408 MOZ_ROLL_EACH_EXPAND_HELPER s MOZ_ROLL_EACH_12(m, s, fa, (MOZ_ARGS_AFTER_1 a)) 409 #define MOZ_ROLL_EACH_14(m, s, fa, a) \ 410 MOZ_ROLL_EACH_HELPER(m, fa, a) \ 411 MOZ_ROLL_EACH_EXPAND_HELPER s MOZ_ROLL_EACH_13(m, s, fa, (MOZ_ARGS_AFTER_1 a)) 412 #define MOZ_ROLL_EACH_15(m, s, fa, a) \ 413 MOZ_ROLL_EACH_HELPER(m, fa, a) \ 414 MOZ_ROLL_EACH_EXPAND_HELPER s MOZ_ROLL_EACH_14(m, s, fa, (MOZ_ARGS_AFTER_1 a)) 415 #define MOZ_ROLL_EACH_16(m, s, fa, a) \ 416 MOZ_ROLL_EACH_HELPER(m, fa, a) \ 417 MOZ_ROLL_EACH_EXPAND_HELPER s MOZ_ROLL_EACH_15(m, s, fa, (MOZ_ARGS_AFTER_1 a)) 418 #define MOZ_ROLL_EACH_17(m, s, fa, a) \ 419 MOZ_ROLL_EACH_HELPER(m, fa, a) \ 420 MOZ_ROLL_EACH_EXPAND_HELPER s MOZ_ROLL_EACH_16(m, s, fa, (MOZ_ARGS_AFTER_1 a)) 421 #define MOZ_ROLL_EACH_18(m, s, fa, a) \ 422 MOZ_ROLL_EACH_HELPER(m, fa, a) \ 423 MOZ_ROLL_EACH_EXPAND_HELPER s MOZ_ROLL_EACH_17(m, s, fa, (MOZ_ARGS_AFTER_1 a)) 424 #define MOZ_ROLL_EACH_19(m, s, fa, a) \ 425 MOZ_ROLL_EACH_HELPER(m, fa, a) \ 426 MOZ_ROLL_EACH_EXPAND_HELPER s MOZ_ROLL_EACH_18(m, s, fa, (MOZ_ARGS_AFTER_1 a)) 427 #define MOZ_ROLL_EACH_20(m, s, fa, a) \ 428 MOZ_ROLL_EACH_HELPER(m, fa, a) \ 429 MOZ_ROLL_EACH_EXPAND_HELPER s MOZ_ROLL_EACH_19(m, s, fa, (MOZ_ARGS_AFTER_1 a)) 430 #define MOZ_ROLL_EACH_21(m, s, fa, a) \ 431 MOZ_ROLL_EACH_HELPER(m, fa, a) \ 432 MOZ_ROLL_EACH_EXPAND_HELPER s MOZ_ROLL_EACH_20(m, s, fa, (MOZ_ARGS_AFTER_1 a)) 433 #define MOZ_ROLL_EACH_22(m, s, fa, a) \ 434 MOZ_ROLL_EACH_HELPER(m, fa, a) \ 435 MOZ_ROLL_EACH_EXPAND_HELPER s MOZ_ROLL_EACH_21(m, s, fa, (MOZ_ARGS_AFTER_1 a)) 436 #define MOZ_ROLL_EACH_23(m, s, fa, a) \ 437 MOZ_ROLL_EACH_HELPER(m, fa, a) \ 438 MOZ_ROLL_EACH_EXPAND_HELPER s MOZ_ROLL_EACH_22(m, s, fa, (MOZ_ARGS_AFTER_1 a)) 439 #define MOZ_ROLL_EACH_24(m, s, fa, a) \ 440 MOZ_ROLL_EACH_HELPER(m, fa, a) \ 441 MOZ_ROLL_EACH_EXPAND_HELPER s MOZ_ROLL_EACH_23(m, s, fa, (MOZ_ARGS_AFTER_1 a)) 442 #define MOZ_ROLL_EACH_25(m, s, fa, a) \ 443 MOZ_ROLL_EACH_HELPER(m, fa, a) \ 444 MOZ_ROLL_EACH_EXPAND_HELPER s MOZ_ROLL_EACH_24(m, s, fa, (MOZ_ARGS_AFTER_1 a)) 445 #define MOZ_ROLL_EACH_26(m, s, fa, a) \ 446 MOZ_ROLL_EACH_HELPER(m, fa, a) \ 447 MOZ_ROLL_EACH_EXPAND_HELPER s MOZ_ROLL_EACH_25(m, s, fa, (MOZ_ARGS_AFTER_1 a)) 448 #define MOZ_ROLL_EACH_27(m, s, fa, a) \ 449 MOZ_ROLL_EACH_HELPER(m, fa, a) \ 450 MOZ_ROLL_EACH_EXPAND_HELPER s MOZ_ROLL_EACH_26(m, s, fa, (MOZ_ARGS_AFTER_1 a)) 451 #define MOZ_ROLL_EACH_28(m, s, fa, a) \ 452 MOZ_ROLL_EACH_HELPER(m, fa, a) \ 453 MOZ_ROLL_EACH_EXPAND_HELPER s MOZ_ROLL_EACH_27(m, s, fa, (MOZ_ARGS_AFTER_1 a)) 454 #define MOZ_ROLL_EACH_29(m, s, fa, a) \ 455 MOZ_ROLL_EACH_HELPER(m, fa, a) \ 456 MOZ_ROLL_EACH_EXPAND_HELPER s MOZ_ROLL_EACH_28(m, s, fa, (MOZ_ARGS_AFTER_1 a)) 457 #define MOZ_ROLL_EACH_30(m, s, fa, a) \ 458 MOZ_ROLL_EACH_HELPER(m, fa, a) \ 459 MOZ_ROLL_EACH_EXPAND_HELPER s MOZ_ROLL_EACH_29(m, s, fa, (MOZ_ARGS_AFTER_1 a)) 460 #define MOZ_ROLL_EACH_31(m, s, fa, a) \ 461 MOZ_ROLL_EACH_HELPER(m, fa, a) \ 462 MOZ_ROLL_EACH_EXPAND_HELPER s MOZ_ROLL_EACH_30(m, s, fa, (MOZ_ARGS_AFTER_1 a)) 463 #define MOZ_ROLL_EACH_32(m, s, fa, a) \ 464 MOZ_ROLL_EACH_HELPER(m, fa, a) \ 465 MOZ_ROLL_EACH_EXPAND_HELPER s MOZ_ROLL_EACH_31(m, s, fa, (MOZ_ARGS_AFTER_1 a)) 466 } // namespace mozilla 467 #endif /* mozilla_AtomicBitfields_h */