Tearable.h 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. /*
  2. * Copyright 2018-present 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. #pragma once
  17. #include <atomic>
  18. #include <cstring>
  19. #include <type_traits>
  20. #include <folly/Traits.h>
  21. namespace folly {
  22. /**
  23. * This class allows you to perform torn loads and stores on the bits of a
  24. * trivially-copyable type T without triggering undefined behavior. You may
  25. * encounter corrupt data, but should not encounter nasal demons.
  26. *
  27. * This class provides no atomicity or memory ordering. Loads and stores are
  28. * expected often to be data races. Synchronization is expected to be provided
  29. * externally, and this class is helpful in building higher-level optimistic
  30. * concurrency tools in combination with externally-provided synchronization.
  31. *
  32. * To see why this is useful, consider the guarantees provided by
  33. * std::atomic<T>. It ensures that every load returns a T that was stored in the
  34. * atomic. If T is too large to be read/written with a single load/store
  35. * instruction, std::atomic<T> falls back to locking to provide this guarantee.
  36. * Users pay this cost even if they have some higher-level mechanism (an
  37. * external lock, version numbers, other application-level reasoning) that makes
  38. * them resilient to torn reads. Tearable<T> allows concurrent access without
  39. * these costs.
  40. *
  41. * For types smaller than the processor word size, prefer std::atomic<T>.
  42. */
  43. template <typename T>
  44. class Tearable {
  45. public:
  46. // We memcpy the object representation, and the destructor would not know how
  47. // to deal with an object state it doesn't understand.
  48. static_assert(
  49. is_trivially_copyable<T>::value,
  50. "Tearable types must be trivially copyable.");
  51. Tearable() = default;
  52. Tearable(const T& val) : Tearable() {
  53. store(val);
  54. }
  55. // Note that while filling dst with invalid data should be fine, *doing
  56. // anything* with the result may trigger undefined behavior unless you've
  57. // verified that the data you read was consistent.
  58. void load(T& dst) const {
  59. RawWord newDst[kNumDataWords];
  60. for (std::size_t i = 0; i < kNumDataWords; ++i) {
  61. newDst[i] = data_[i].load(std::memory_order_relaxed);
  62. }
  63. std::memcpy(&dst, newDst, sizeof(T));
  64. }
  65. void store(const T& val) {
  66. RawWord newData[kNumDataWords];
  67. std::memcpy(newData, &val, sizeof(T));
  68. for (std::size_t i = 0; i < kNumDataWords; ++i) {
  69. data_[i].store(newData[i], std::memory_order_relaxed);
  70. }
  71. }
  72. private:
  73. // A union gets us memcpy-like copy semantics always.
  74. union RawWord {
  75. // "unsigned" here matters; we may read uninitialized values (in the
  76. // trailing data word in write(), for instance).
  77. unsigned char data alignas(void*)[sizeof(void*)];
  78. };
  79. // Because std::atomic_init is declared but undefined in libstdc++-v4.9.2:
  80. // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=64658.
  81. struct AtomicWord : std::atomic<RawWord> {
  82. AtomicWord() noexcept : std::atomic<RawWord>{RawWord{}} {}
  83. };
  84. const static std::size_t kNumDataWords =
  85. (sizeof(T) + sizeof(RawWord) - 1) / sizeof(RawWord);
  86. AtomicWord data_[kNumDataWords];
  87. };
  88. } // namespace folly