foonathan / memory

STL compatible C++ memory allocator library using a new RawAllocator concept that is similar to an Allocator but easier to use and write.

Home Page:https://memory.foonathan.net

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

ARM64 crash when using static block allocator with small types

kevin-- opened this issue · comments

Crash when deallocating shared_ptr building for arm64 -- Xcode Apple clang version 14.0.3 (clang-1403.0.22.14.1)

Has been happening for a couple major Xcode versions so far.

Example program

using StaticMemoryPoolAllocator =
    foonathan::memory::memory_pool<foonathan::memory::node_pool, foonathan::memory::static_block_allocator>

size_t Capacity = 20;
using TestType = int;
using NodeSize = shared_ptr_stateful_node_size<TestType>;

constexpr size_t storage_size_bytes = StaticMemoryPoolAllocator::min_block_size( NodeSize::value, Capacity );

// setup storage & pool
foonathan::memory::static_allocator_storage<storage_size_bytes> storage;
StaticMemoryPoolAllocator pool( NodeSize::value, storage_size_bytes, storage );

// test case

std::list<std::shared_ptr<TestType>> retain;
for ( size_t i = 0; i < capacity; ++i ) {
    retain.push_back( foonathan::memory::allocate_shared<TestType>( pool ) );
}
retain.clear(); // < crash here

In fact, resetting the shared_ptr of the 2nd created item can cause a crash.

In my experimenting, the only thing that alleviated the crash was to use TestType with a sizeof 8 or higher.

I don't have access to XCode, can you please give me the following information:

  1. shared_ptr_stateful_node_size<TestType>::value
  2. How many bytes are allocated by memory::allocate_shared<TestType>(pool) (you can find it out by setting a breakpoint in std_allocator::allocate, the value is n * sizeof(T) of whatever allocator instantiation was being used.
  3. If there is a mismatch between 1 and 2, the output of nodesize_dbg --verbose

Thank you.

Sorry for the long delay, here is the info

using TestType = int;

  1. sizeof(int) = 4, alignof(int) = 4
  2. shared_ptr_stateful_node_size<int>::value = 44
  3. memory::allocate_shared<TestType>(pool) -> std_allocator::allocate = n = 1
    • std::allocator::allocate_impl:
          void* allocate_impl(std::false_type, size_type n)
          {
              if (n == 1)
                  return this->allocate_node(sizeof(T), alignof(T));
              else
                  return this->allocate_array(n, sizeof(T), alignof(T));
          }
      
      taking the n == 1 branch here, sizeof(T) = 40, alignof(T) = 8.
    • so I think therefore it is allocating 48, but the determined size is 44 from (1) above
  4. see attached nodesize_dbg_verbose.txt

It seems the discrepancy in (1) is that detail::shared_ptr_stateful_node_size<alignof(T)>::value + sizeof(T) here we take the alignof(int)
BUT in (2) the allocator impl is taking the alignof(shared_ptr_stateful_node_size<int>)

The type of the std_allocator is

foonathan::memory::std_allocator<
    std::__shared_ptr_emplace<
        int,
        foonathan::memory::std_allocator<
            int,
            rt::StaticMemoryPool<
                20,
                shared_ptr_stateful_node_size<int>,
                rt::container_overhead::None
            >
        >
    >,
    rt::StaticMemoryPool<
        20,
        shared_ptr_stateful_node_size<int>,
        rt::container_overhead::None
    >
> 

The rt::StaticMemoryPool is my implementation of an allocator-- the 1st argument is the capacity, the 2nd template argument is used to determine the size of the nodes