Introduce folly::max_align_t and folly::max_align_v
authorPhil Willoughby <philwill@fb.com>
Thu, 20 Jul 2017 12:28:49 +0000 (05:28 -0700)
committerFacebook Github Bot <facebook-github-bot@users.noreply.github.com>
Thu, 20 Jul 2017 12:37:58 +0000 (05:37 -0700)
Summary:
`folly::max_align_t` is a portable replacement for `std::max_align_t`.

`folly::max_align_v` is a `constexpr size_t` with the same value as `alignof(folly::max_align_t)`

32-bit iOS environments have `alignof(std::max_align_t) == 4`, which is not correct. This is more
correct in that I have not yet found a more-aligned basic type.

Reviewed By: yfeldblum

Differential Revision: D5442333

fbshipit-source-id: 0a93e48a730d65ef76e04f132b5976c93587b14c

folly/CachelinePadded.h
folly/MicroSpinLock.h
folly/Portability.h
folly/concurrency/CacheLocality.cpp
folly/concurrency/CacheLocality.h
folly/experimental/hazptr/memory_resource.h
folly/io/IOBuf.cpp
folly/test/CachelinePaddedTest.cpp
folly/test/ThreadCachedArenaTest.cpp

index fe362a8375a65bf738015b4b3ba307aa5fc34102..faaa1eebadc1274f289a165b8bc1ec67418d1731 100644 (file)
@@ -18,6 +18,7 @@
 
 #include <cstddef>
 
+#include <folly/Portability.h>
 #include <folly/concurrency/CacheLocality.h>
 
 namespace folly {
@@ -32,8 +33,8 @@ namespace folly {
 template <typename T>
 class CachelinePadded {
   static_assert(
-      alignof(T) < CacheLocality::kFalseSharingRange,
-      "CachelinePadded does not support types aligned >= a cache-line.");
+      alignof(T) <= folly::max_align_v,
+      "CachelinePadded does not support over-aligned types.");
 
  public:
   template <typename... Args>
index 737ac70efaf63010b1ff74975941778649a07a4b..ee2c4420f62a33953d68d94885708ece6cddfe0b 100644 (file)
@@ -142,10 +142,11 @@ struct FOLLY_ALIGNED_MAX SpinLockArray {
                 "Invalid size of PaddedSpinLock");
 
   // Check if T can theoretically cross a cache line.
-  static_assert(alignof(std::max_align_t) > 0 &&
-                FOLLY_CACHE_LINE_SIZE % alignof(std::max_align_t) == 0 &&
-                sizeof(T) <= alignof(std::max_align_t),
-                "T can cross cache line boundaries");
+  static_assert(
+      folly::max_align_v > 0 &&
+          FOLLY_CACHE_LINE_SIZE % folly::max_align_v == 0 &&
+          sizeof(T) <= folly::max_align_v,
+      "T can cross cache line boundaries");
 
   char padding_[FOLLY_CACHE_LINE_SIZE];
   std::array<PaddedSpinLock, N> data_;
index 053b5b1632b57004db715d80be899671c24830a4..d0c14dd7e388122d732b9f29fb1ae11c0aebde1c 100644 (file)
@@ -19,6 +19,7 @@
 #include <string.h>
 
 #include <cstddef>
+#include <type_traits>
 
 #include <folly/portability/Config.h>
 
@@ -31,7 +32,75 @@ constexpr bool kHasUnalignedAccess = true;
 #else
 constexpr bool kHasUnalignedAccess = false;
 #endif
-}
+
+namespace detail {
+
+template <typename I, I A, I... Bs>
+struct integral_max
+    : std::integral_constant<
+          I,
+          (A > integral_max<I, Bs...>::value) ? A
+                                              : integral_max<I, Bs...>::value> {
+};
+
+template <typename I, size_t A>
+struct integral_max<I, A> : std::integral_constant<I, A> {};
+
+template <typename... Ts>
+using max_alignment = integral_max<size_t, alignof(Ts)...>;
+
+using max_basic_alignment = max_alignment<
+    std::max_align_t,
+    long double,
+    double,
+    float,
+    long long int,
+    long int,
+    int,
+    short int,
+    bool,
+    char,
+    char16_t,
+    char32_t,
+    wchar_t,
+    std::nullptr_t>;
+} // namespace detail
+
+constexpr size_t max_align_v = detail::max_basic_alignment::value;
+
+// max_align_t is a type which is aligned at least as strictly as the
+// most-aligned basic type (see the specification of std::max_align_t). This
+// implementation exists because 32-bit iOS platforms have a broken
+// std::max_align_t (see below).
+//
+// You should refer to this as `::folly::max_align_t` in portable code, even if
+// you have `using namespace folly;` because C11 defines a global namespace
+// `max_align_t` type.
+//
+// To be certain, we consider every non-void fundamental type specified by the
+// standard. On most platforms `long double` would be enough, but iOS 32-bit
+// has an 8-byte aligned `double` and `long long int` and a 4-byte aligned
+// `long double`.
+//
+// So far we've covered locals and other non-allocated storage, but we also need
+// confidence that allocated storage from `malloc`, `new`, etc will also be
+// suitable for objects with this alignment reuirement.
+//
+// Apple document that their implementation of malloc will issue 16-byte
+// granularity chunks for small allocations (large allocations are page-size
+// granularity and page-aligned). We think that allocated storage will be
+// suitable for these objects based on the following assumptions:
+//
+// 1. 16-byte granularity also means 16-byte aligned.
+// 2. `new` and other allocators follow the `malloc` rules.
+//
+// We also have some anecdotal evidence: we don't see lots of misaligned-storage
+// crashes on 32-bit iOS apps that use `double`.
+//
+// Apple's allocation reference: http://bit.ly/malloc-small
+struct alignas(max_align_v) max_align_t {};
+
+} // namespace folly
 
 // compiler specific attribute translation
 // msvc should come first, so if clang is in msvc mode it gets the right defines
@@ -43,7 +112,7 @@ constexpr bool kHasUnalignedAccess = false;
 #else
 # error Cannot define FOLLY_ALIGNED on this platform
 #endif
-#define FOLLY_ALIGNED_MAX FOLLY_ALIGNED(alignof(std::max_align_t))
+#define FOLLY_ALIGNED_MAX FOLLY_ALIGNED(::folly::max_align_v)
 
 // NOTE: this will only do checking in msvc with versions that support /analyze
 #if _MSC_VER
index bb465d46f52d23845693a2fa59288c91339f61fe..7f1253d18bce95749af68d758c985632e8779d18 100644 (file)
@@ -257,9 +257,8 @@ void* SimpleAllocator::allocateHard() {
   // Install a pointer to ourselves as the allocator.
   *reinterpret_cast<SimpleAllocator**>(mem_) = this;
   static_assert(
-      alignof(std::max_align_t) >= sizeof(SimpleAllocator*),
-      "alignment too small");
-  mem_ += std::min(sz_, alignof(std::max_align_t));
+      folly::max_align_v >= sizeof(SimpleAllocator*), "alignment too small");
+  mem_ += std::min(sz_, folly::max_align_v);
 
   // New allocation.
   auto mem = mem_;
index f2a391799e8b3e1953884d42b6c7a6863b0defbd..38f5557ceced1ff9de3369a82f942bd76c848481 100644 (file)
@@ -390,7 +390,7 @@ class SimpleAllocator {
     if (intptr_t(mem_) % 128 == 0) {
       // Avoid allocating pointers that may look like malloc
       // pointers.
-      mem_ += std::min(sz_, alignof(std::max_align_t));
+      mem_ += std::min(sz_, folly::max_align_v);
     }
     if (mem_ && (mem_ + sz_ <= end_)) {
       auto mem = mem_;
index 8a855685baa991fa006033ef463bae4ab3cad000..bb049f874eb67a9e387d1e3ddec7e05e66a493d9 100644 (file)
@@ -22,7 +22,7 @@
 /// std::pmr::memory_resource (C++17) as needed for developing a
 /// hazptr prototype.
 ////////////////////////////////////////////////////////////////////////////////
-#include <cstddef>
+#include <folly/Portability.h>
 #include <memory>
 
 namespace folly {
@@ -33,11 +33,11 @@ class memory_resource {
   virtual ~memory_resource() = default;
   virtual void* allocate(
       const size_t bytes,
-      const size_t alignment = alignof(std::max_align_t)) = 0;
+      const size_t alignment = folly::max_align_v) = 0;
   virtual void deallocate(
       void* p,
       const size_t bytes,
-      const size_t alignment = alignof(std::max_align_t)) = 0;
+      const size_t alignment = folly::max_align_v) = 0;
 };
 
 memory_resource* get_default_resource();
@@ -70,7 +70,7 @@ inline memory_resource* new_delete_resource() {
    public:
     void* allocate(
         const size_t bytes,
-        const size_t alignment = alignof(std::max_align_t)) override {
+        const size_t alignment = folly::max_align_v) override {
       (void)alignment;
       void* p = static_cast<void*>(new char[bytes]);
       DEBUG_PRINT(this << " " << p << " " << bytes);
@@ -79,7 +79,7 @@ inline memory_resource* new_delete_resource() {
     void deallocate(
         void* p,
         const size_t bytes,
-        const size_t alignment = alignof(std::max_align_t)) override {
+        const size_t alignment = folly::max_align_v) override {
       (void)alignment;
       (void)bytes;
       DEBUG_PRINT(p << " " << bytes);
index a71ff32e2e3f8d94210ef865925cebf76e234815..3ca39aa269da41d9d196d3e014dfa29af7f95558 100644 (file)
@@ -114,7 +114,7 @@ struct IOBuf::HeapFullStorage {
 
   HeapStorage hs;
   SharedInfo shared;
-  std::max_align_t align;
+  folly::max_align_t align;
 };
 
 IOBuf::SharedInfo::SharedInfo()
index 11cefe332ff37053ea87b6a4a163ea510e1aade9..9c1253019bcb1123c45b0bf317d9f3cfa173e057 100644 (file)
@@ -67,7 +67,7 @@ using CachelinePaddedTypes = ::testing::Types<
     SizedData<kCachelineSize / 2>,
     SizedData<kCachelineSize + kCachelineSize / 2>,
     // Mimic single basic types:
-    SizedDataMimic<std::max_align_t>,
+    SizedDataMimic<folly::max_align_t>,
     SizedDataMimic<void*>,
     SizedDataMimic<long double>,
     SizedDataMimic<double>,
@@ -78,7 +78,7 @@ using CachelinePaddedTypes = ::testing::Types<
     SizedDataMimic<short>,
     SizedDataMimic<char>,
     // Mimic small arrays of basic types:
-    SizedDataMimic<std::max_align_t, 3>,
+    SizedDataMimic<folly::max_align_t, 3>,
     SizedDataMimic<void*, 3>,
     SizedDataMimic<long double, 3>,
     SizedDataMimic<double, 3>,
@@ -89,7 +89,7 @@ using CachelinePaddedTypes = ::testing::Types<
     SizedDataMimic<short, 3>,
     SizedDataMimic<char, 3>,
     // Mimic large arrays of basic types:
-    SizedDataMimic<std::max_align_t, kCachelineSize + 3>,
+    SizedDataMimic<folly::max_align_t, kCachelineSize + 3>,
     SizedDataMimic<void*, kCachelineSize + 3>,
     SizedDataMimic<long double, kCachelineSize + 3>,
     SizedDataMimic<double, kCachelineSize + 3>,
index 458239db9aff799fb0ff5579182534a1edf8b8e1..8471e939841fcf1a5e7330ff0266c230eb5fc6fd 100644 (file)
@@ -92,7 +92,7 @@ void ArenaTester::merge(ArenaTester&& other) {
 }  // namespace
 
 TEST(ThreadCachedArena, BlockSize) {
-  static const size_t alignment = alignof(std::max_align_t);
+  static const size_t alignment = folly::max_align_v;
   static const size_t requestedBlockSize = 64;
 
   ThreadCachedArena arena(requestedBlockSize);