Program Listing for File FixedMallocPool.cpp

Return to documentation for file (umpire/util/FixedMallocPool.cpp)

//////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2016-20, Lawrence Livermore National Security, LLC and Umpire
// project contributors. See the COPYRIGHT file for details.
//
// SPDX-License-Identifier: (MIT)
//////////////////////////////////////////////////////////////////////////////

// FixedMallocPool follows the algorithm presented in
// Fast Efficient Fixed-Size Memory Pool -- Ben Kenwright

#include "umpire/util/FixedMallocPool.hpp"

#include "umpire/util/Macros.hpp"

#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <cstddef>

namespace umpire {
namespace util {

inline unsigned char*
FixedMallocPool::addr_from_index(const FixedMallocPool::Pool& p, unsigned int i) const
{
  return p.data + i * m_obj_bytes;
}

inline unsigned int
FixedMallocPool::index_from_addr(const FixedMallocPool::Pool& p, const unsigned char* ptr) const
{
  return static_cast<unsigned int>((ptr - p.data) / m_obj_bytes);
}

FixedMallocPool::Pool::Pool(const std::size_t object_bytes,
                            const std::size_t objects_per_pool) :
  data(static_cast<unsigned char*>(std::malloc(object_bytes * objects_per_pool))),
  next(data),
  num_initialized(0),
  num_free(objects_per_pool) {}

FixedMallocPool::FixedMallocPool(const std::size_t object_bytes,
                                 const std::size_t objects_per_pool) :
  m_obj_bytes(object_bytes),
  m_obj_per_pool(objects_per_pool),
  m_data_bytes(m_obj_bytes * m_obj_per_pool),
  m_pool()
{
  newPool();
}

FixedMallocPool::~FixedMallocPool()
{
  for (auto& a : m_pool) std::free(a.data);
}

void
FixedMallocPool::newPool()
{
  m_pool.emplace_back(m_obj_bytes, m_obj_per_pool);
}

void*
FixedMallocPool::allocInPool(Pool& p) noexcept
{
  if (p.num_initialized < m_obj_per_pool) {
    unsigned int* ptr = reinterpret_cast<unsigned int*>(addr_from_index(p, p.num_initialized));
    *ptr = p.num_initialized + 1;
    p.num_initialized++;
  }

  void* ret = nullptr;
  if (p.num_free > 0) {
    ret = static_cast<void*>(p.next);
    --p.num_free;
    p.next = (p.num_free != 0) ? addr_from_index(p, *reinterpret_cast<unsigned int*>(p.next)) : nullptr;
  }

  return ret;
}

void*
FixedMallocPool::allocate(std::size_t bytes)
{
  void* ptr = nullptr;

  for (auto it = m_pool.rbegin(); it != m_pool.rend(); ++it) {
    ptr = allocInPool(*it);
    if (ptr) return ptr;
  }

  if (!ptr) {
    newPool();
    ptr = allocate(bytes);
  }

  // Could be an error, but FixedMallocPool is used internally and an
  // error would be unrecoverable
  UMPIRE_ASSERT(ptr);

  return ptr;
}

void
FixedMallocPool::deallocate(void* ptr)
{
  for (auto& p : m_pool) {
    const unsigned char* t_ptr = reinterpret_cast<unsigned char*>(ptr);
    const ptrdiff_t offset = t_ptr - p.data;
    if ((offset >= 0) && (offset < static_cast<ptrdiff_t>(m_data_bytes))) {
      if (p.next != nullptr) {
        *reinterpret_cast<unsigned int*>(ptr) = index_from_addr(p, p.next);
        p.next = reinterpret_cast<unsigned char*>(ptr);
      }
      else {
        *reinterpret_cast<std::size_t*>(ptr) = m_obj_per_pool;
        p.next = reinterpret_cast<unsigned char*>(ptr);
      }
      ++p.num_free;
      return;
    }
  }

  UMPIRE_ERROR("Could not find the pointer to deallocate");
}

std::size_t
FixedMallocPool::numPools() const noexcept
{
  return m_pool.size();
}

} // end of namespace strategy
} // end of namespace umpire