Program Listing for File FixedPool.cpp¶
↰ Return to documentation for file (umpire/strategy/FixedPool.cpp
)
//////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2016-20, Lawrence Livermore National Security, LLC and Umpire
// project contributors. See the COPYRIGHT file for details.
//
// SPDX-License-Identifier: (MIT)
//////////////////////////////////////////////////////////////////////////////
#include "umpire/strategy/FixedPool.hpp"
#include "umpire/util/Macros.hpp"
#include <sstream>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#if !defined(_MSC_VER)
#define _XOPEN_SOURCE_EXTENDED 1
#include <strings.h>
#endif
namespace umpire {
namespace strategy {
inline int find_first_set(int i)
{
#if defined(_MSC_VER)
unsigned long bit;
unsigned long i_l = static_cast<unsigned long>(i);
_BitScanForward(&bit, i_l);
return static_cast<int>(bit);
#else
return ffs(i);
#endif
}
static constexpr std::size_t bits_per_int = sizeof(int) * 8;
FixedPool::Pool::Pool(AllocationStrategy* allocation_strategy,
const std::size_t object_bytes, const std::size_t objects_per_pool,
const std::size_t avail_bytes) :
strategy(allocation_strategy),
data(reinterpret_cast<char*>(strategy->allocate(object_bytes * objects_per_pool))),
avail(reinterpret_cast<int*>(std::malloc(avail_bytes))),
num_avail(objects_per_pool)
{
// Set all bits to 1
const unsigned char not_zero = static_cast<unsigned char>(~0);
std::memset(avail, not_zero, avail_bytes);
}
FixedPool::FixedPool(const std::string& name, int id,
Allocator allocator, const std::size_t object_bytes,
const std::size_t objects_per_pool) noexcept :
AllocationStrategy{name, id},
m_strategy{allocator.getAllocationStrategy()},
m_obj_bytes{object_bytes},
m_obj_per_pool{objects_per_pool},
m_data_bytes{m_obj_bytes * m_obj_per_pool},
m_avail_bytes{objects_per_pool/bits_per_int + 1},
m_current_bytes{0},
m_actual_bytes{0},
m_highwatermark{0},
m_pool{}
{
newPool();
}
FixedPool::~FixedPool()
{
std::vector<void*> leaked_addrs{};
for (auto& p : m_pool) {
if (m_obj_per_pool != p.num_avail) {
for (unsigned int int_index = 0; int_index < m_avail_bytes; ++int_index)
for (unsigned int bit_index = 0; bit_index < bits_per_int; ++bit_index) {
if (!(p.avail[int_index] & 1 << bit_index)) {
const std::size_t index{int_index * bits_per_int + bit_index};
leaked_addrs.push_back(
static_cast<void*>(p.data + m_obj_bytes * index));
}
}
}
}
if (leaked_addrs.size() > 0) {
const std::size_t max_addr{25};
std::stringstream ss;
ss << "There are " << leaked_addrs.size() << " addresses";
ss << " not deallocated at destruction. This will cause leak(s). ";
if (leaked_addrs.size() <= max_addr)
ss << "Addresses:";
else
ss << "First " << max_addr << " addresses:";
for (std::size_t i = 0; i < std::min(max_addr, leaked_addrs.size()); ++i) {
if (i % 5 == 0) ss << "\n\t";
ss << " " << leaked_addrs[i];
}
UMPIRE_LOG(Warning, ss.str());
} else {
for (auto& p : m_pool) {
p.strategy->deallocate(p.data);
std::free(p.avail);
}
}
}
void
FixedPool::newPool()
{
m_pool.emplace_back(m_strategy, m_obj_bytes, m_obj_per_pool, m_avail_bytes * sizeof(int));
m_actual_bytes += m_avail_bytes + m_data_bytes;
}
void*
FixedPool::allocInPool(Pool& p)
{
if (!p.num_avail) return nullptr;
for (unsigned int int_index = 0; int_index < m_avail_bytes; ++int_index) {
// Return the index of the first 1 bit
const int bit_index = find_first_set(p.avail[int_index]) - 1;
if (bit_index >= 0) {
const std::size_t index = int_index * bits_per_int + bit_index;
if (index < m_obj_per_pool) {
// Flip bit 1 -> 0
p.avail[int_index] ^= 1 << bit_index;
p.num_avail--;
return static_cast<void*>(p.data + m_obj_bytes * index);
}
}
}
UMPIRE_ASSERT("FixedPool::allocInPool(): num_avail > 0, but no available slots" && 0);
return nullptr;
}
void*
FixedPool::allocate(std::size_t bytes)
{
// Check that bytes passed matches m_obj_bytes or bytes was not passed (default = 0)
UMPIRE_ASSERT(!bytes || bytes == m_obj_bytes);
void* ptr = nullptr;
for (auto it = m_pool.rbegin(); it != m_pool.rend(); ++it) {
ptr = allocInPool(*it);
if (ptr) {
m_current_bytes += m_obj_bytes;
m_highwatermark = std::max(m_highwatermark, m_current_bytes);
break;
}
}
if (!ptr) {
newPool();
ptr = allocate(bytes);
}
if (!ptr) {
UMPIRE_ERROR("FixedPool::allocate(size=" << m_obj_bytes << "): Could not allocate");
}
return ptr;
}
void
FixedPool::deallocate(void* ptr)
{
for (auto& p : m_pool) {
const char* t_ptr = reinterpret_cast<char*>(ptr);
const ptrdiff_t offset = t_ptr - p.data;
if ((offset >= 0) && (offset < static_cast<ptrdiff_t>(m_data_bytes))) {
const std::size_t alloc_index = offset / m_obj_bytes;
const std::size_t int_index = alloc_index / bits_per_int;
const short bit_index = alloc_index % bits_per_int;
UMPIRE_ASSERT(! (p.avail[int_index] & (1 << bit_index)));
// Flip bit 0 -> 1
p.avail[int_index] ^= 1 << bit_index;
p.num_avail++;
m_current_bytes -= m_obj_bytes;
return;
}
}
UMPIRE_ERROR("Could not find the pointer to deallocate");
}
std::size_t
FixedPool::getCurrentSize() const noexcept
{
return m_current_bytes;
}
std::size_t
FixedPool::getActualSize() const noexcept
{
return m_actual_bytes;
}
std::size_t
FixedPool::getHighWatermark() const noexcept
{
return m_highwatermark;
}
Platform
FixedPool::getPlatform() noexcept
{
return m_strategy->getPlatform();
}
std::size_t
FixedPool::numPools() const noexcept
{
return m_pool.size();
}
bool
FixedPool::pointerIsFromPool(void* ptr) const noexcept
{
for (auto& p : m_pool) {
const char* t_ptr = reinterpret_cast<char*>(ptr);
const ptrdiff_t offset = t_ptr - p.data;
if ((offset >= 0) && (offset < static_cast<ptrdiff_t>(m_data_bytes))) {
return true;
}
}
return false;
}
} // end of namespace strategy
} // end of namespace umpire