Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
145 changes: 145 additions & 0 deletions src/umpire/ResourceManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,145 @@ void ResourceManager::removeAlias(const std::string& name, Allocator allocator)
m_allocators_by_name.erase(a);
}

bool ResourceManager::isBuiltinAllocator(strategy::AllocationStrategy* strategy)
{
for (const auto& entry : m_memory_resources) {
if (entry.second == strategy) {
return true;
}
}

std::string name = strategy->getName();
if (name == "__umpire_internal_null" || name == "__umpire_internal_0_byte_pool") {
return true;
}

return false;
}

void ResourceManager::destroyAllocator(const std::string& name, bool free_allocations)
{
std::lock_guard<std::mutex> lock(m_mutex);

UMPIRE_LOG(Debug, "(name=\"" << name << "\", free_allocations=" << free_allocations << ")");

auto it = m_allocators_by_name.find(name);
if (it == m_allocators_by_name.end()) {
UMPIRE_ERROR(runtime_error, fmt::format("Allocator \"{}\" not found", name));
}

strategy::AllocationStrategy* strategy = it->second;
int id = strategy->getId();

if (isBuiltinAllocator(strategy)) {
UMPIRE_ERROR(runtime_error,
fmt::format("Cannot destroy builtin allocator \"{}\"", name));
}

if (isStrictDestructionMode()) {
auto records = umpire::get_allocator_records(Allocator(strategy));
if (!records.empty() && !free_allocations) {
UMPIRE_ERROR(runtime_error,
fmt::format("Allocator \"{}\" has {} active allocations. "
"Use free_allocations=true or deallocate them first.",
name, records.size()));
}
} else if (!free_allocations) {
UMPIRE_LOG(Warning, "Allocator \"" << name << "\" may have active allocations. "
<< "Destroying anyway (non-strict mode).");
}


if (isStrictDestructionMode()) {
std::vector<std::string> child_names;
for (const auto& alloc : m_allocators) {
if (alloc.get() != strategy && alloc->getParent() == strategy) {
child_names.push_back(alloc->getName());
}
}

if (!child_names.empty()) {
std::string children_str;
for (size_t i = 0; i < child_names.size(); ++i) {
if (i > 0) children_str += ", ";
children_str += child_names[i];
}

UMPIRE_ERROR(runtime_error,
fmt::format("Allocator \"{}\" is a parent of other allocators: {}. "
"Destroy children first.",
name, children_str));
}
} else {
UMPIRE_LOG(Warning, "Allocator \"" << name << "\" may be a parent of other allocators. "
<< "Destroying anyway (non-strict mode).");
}

if (free_allocations) {
auto records = umpire::get_allocator_records(Allocator(strategy));
UMPIRE_LOG(Debug, "Freeing " << records.size() << " allocations");
for (const auto& record : records) {
strategy->deallocate_internal(record.ptr, record.size);
}
}

std::vector<std::string> names_to_remove;
for (const auto& entry : m_allocators_by_name) {
if (entry.second == strategy) {
names_to_remove.push_back(entry.first);
}
}

for (const auto& n : names_to_remove) {
m_allocators_by_name.erase(n);
}

m_allocators_by_id.erase(id);

for (auto it_mem = m_memory_resources.begin(); it_mem != m_memory_resources.end();) {
if (it_mem->second == strategy) {
it_mem = m_memory_resources.erase(it_mem);
} else {
++it_mem;
}
}

auto shared_it = std::find(m_shared_allocator_names.begin(), m_shared_allocator_names.end(), name);
if (shared_it != m_shared_allocator_names.end()) {
m_shared_allocator_names.erase(shared_it);
}

for (auto it_alloc = m_allocators.begin(); it_alloc != m_allocators.end(); ++it_alloc) {
if (it_alloc->get() == strategy) {
m_allocators.erase(it_alloc);
break;
}
}

umpire::event::record([&](auto& event) {
event.name("destroy_allocator")
.category(event::category::operation)
.arg("allocator_name", name)
.arg("allocator_id", id)
.arg("freed_allocations", free_allocations)
.tag("replay", "true");
});

UMPIRE_LOG(Debug, "Allocator \"" << name << "\" destroyed successfully");
}

void ResourceManager::destroyAllocator(int id, bool free_allocations)
{
UMPIRE_LOG(Debug, "(id=" << id << ", free_allocations=" << free_allocations << ")");

auto it = m_allocators_by_id.find(id);
if (it == m_allocators_by_id.end()) {
UMPIRE_ERROR(runtime_error, fmt::format("Allocator with id {} not found", id));
}

destroyAllocator(it->second->getName(), free_allocations);
}

Allocator ResourceManager::getAllocator(void* ptr)
{
UMPIRE_LOG(Debug, "(ptr=" << ptr << ")");
Expand Down Expand Up @@ -958,6 +1097,12 @@ std::shared_ptr<op::MemoryOperation> ResourceManager::getOperation(const std::st
return op_registry.find(operation_name, src_allocator.getAllocationStrategy(), dst_allocator.getAllocationStrategy());
}

bool ResourceManager::isStrictDestructionMode() const noexcept
{
static const char* env_value = std::getenv("UMPIRE_STRICT_DESTRUCTION");
return (env_value != nullptr);
}

int ResourceManager::getNumDevices() const
{
int device_count{0};
Expand Down
55 changes: 55 additions & 0 deletions src/umpire/ResourceManager.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,48 @@ class ResourceManager {
*/
void removeAlias(const std::string& name, Allocator allocator);

/*!
* \brief Destroy an allocator by name.
*
* Removes the allocator from the ResourceManager and frees associated
* resources. Core resource allocators (HOST, DEVICE, etc.) cannot be
* destroyed.
*
* Behavior is controlled by the UMPIRE_STRICT_DESTRUCTION environment variable:
*
* If UMPIRE_STRICT_DESTRUCTION is set (to any value):
* - Throws error if allocator has active allocations and free_allocations=false
* - Throws error if allocator is a parent of other allocators
*
* If UMPIRE_STRICT_DESTRUCTION is not set (default):
* - Logs warning but proceeds if allocator has active allocations
* - Logs warning but proceeds if allocator is a parent
*
* Example:
* export UMPIRE_STRICT_DESTRUCTION=1 // Enable strict mode
* unset UMPIRE_STRICT_DESTRUCTION // Disable strict mode (default)
*
* \param name Name of the allocator to destroy
* \param free_allocations If true, deallocates all active allocations
* before destroying. Defaults to false.
*
* \throw runtime_error if allocator is a core resource or not found
*/
void destroyAllocator(const std::string& name, bool free_allocations = false);

/*!
* \brief Destroy an allocator by ID.
*
* See destroyAllocator(const std::string&, bool) for detailed behavior.
*
* \param id ID of the allocator to destroy
* \param free_allocations If true, deallocates all active allocations
* before destroying. Defaults to false.
*
* \throw runtime_error if allocator is a core resource or not found
*/
void destroyAllocator(int id, bool free_allocations = false);

/*!
* \brief Get the Allocator used to allocate ptr.
*
Expand Down Expand Up @@ -328,6 +370,19 @@ class ResourceManager {
strategy::AllocationStrategy* findAllocatorForId(int id);
strategy::AllocationStrategy* getAllocationStrategy(const std::string& name);

bool isBuiltinAllocator(strategy::AllocationStrategy* strategy);

/*!
* \brief Check if strict destruction mode is enabled via environment variable.
*
* Checks the UMPIRE_STRICT_DESTRUCTION environment variable. If set to any
* value, strict mode is enabled (errors thrown). If unset, non-strict mode
* is used (warnings logged). The check is cached on first call.
*
* \return true if UMPIRE_STRICT_DESTRUCTION is set, false otherwise
*/
bool isStrictDestructionMode() const noexcept;

int getNextId() noexcept;

std::string getAllocatorInformation() const noexcept;
Expand Down
1 change: 1 addition & 0 deletions src/umpire/ResourceManager.inl
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

#include "camp/list.hpp"
#include "umpire/ResourceManager.hpp"
#include "umpire/event/event.hpp"
#include "umpire/util/Macros.hpp"
#include "umpire/util/error.hpp"
#include "umpire/util/make_unique.hpp"
Expand Down
90 changes: 90 additions & 0 deletions src/umpire/interface/c_fortran/wrapResourceManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -729,6 +729,96 @@ void umpire_resourcemanager_remove_alias_bufferify(
// splicer end class.ResourceManager.method.remove_alias_bufferify
}

/**
* \brief Destroy an allocator by name
*
*/
void umpire_resourcemanager_destroy_allocator_by_name(
umpire_resourcemanager * self, const char * name)
{
umpire::ResourceManager *SH_this =
static_cast<umpire::ResourceManager *>(self->addr);
// splicer begin class.ResourceManager.method.destroy_allocator_by_name
const std::string SHCXX_name(name);
SH_this->destroyAllocator(SHCXX_name);
// splicer end class.ResourceManager.method.destroy_allocator_by_name
}

/**
* \brief Destroy an allocator by name
*
*/
void umpire_resourcemanager_destroy_allocator_by_name_bufferify(
umpire_resourcemanager * self, const char * name, int Lname)
{
umpire::ResourceManager *SH_this =
static_cast<umpire::ResourceManager *>(self->addr);
// splicer begin class.ResourceManager.method.destroy_allocator_by_name_bufferify
const std::string SHCXX_name(name, Lname);
SH_this->destroyAllocator(SHCXX_name);
// splicer end class.ResourceManager.method.destroy_allocator_by_name_bufferify
}

/**
* \brief Destroy an allocator by name
*
*/
void umpire_resourcemanager_destroy_allocator_by_name_with_free(
umpire_resourcemanager * self, const char * name,
bool free_allocations)
{
umpire::ResourceManager *SH_this =
static_cast<umpire::ResourceManager *>(self->addr);
// splicer begin class.ResourceManager.method.destroy_allocator_by_name_with_free
const std::string SHCXX_name(name);
SH_this->destroyAllocator(SHCXX_name, free_allocations);
// splicer end class.ResourceManager.method.destroy_allocator_by_name_with_free
}

/**
* \brief Destroy an allocator by name
*
*/
void umpire_resourcemanager_destroy_allocator_by_name_with_free_bufferify(
umpire_resourcemanager * self, const char * name, int Lname,
bool free_allocations)
{
umpire::ResourceManager *SH_this =
static_cast<umpire::ResourceManager *>(self->addr);
// splicer begin class.ResourceManager.method.destroy_allocator_by_name_with_free_bufferify
const std::string SHCXX_name(name, Lname);
SH_this->destroyAllocator(SHCXX_name, free_allocations);
// splicer end class.ResourceManager.method.destroy_allocator_by_name_with_free_bufferify
}

/**
* \brief Destroy an allocator by ID
*
*/
void umpire_resourcemanager_destroy_allocator_by_id(
umpire_resourcemanager * self, int id)
{
umpire::ResourceManager *SH_this =
static_cast<umpire::ResourceManager *>(self->addr);
// splicer begin class.ResourceManager.method.destroy_allocator_by_id
SH_this->destroyAllocator(id);
// splicer end class.ResourceManager.method.destroy_allocator_by_id
}

/**
* \brief Destroy an allocator by ID
*
*/
void umpire_resourcemanager_destroy_allocator_by_id_with_free(
umpire_resourcemanager * self, int id, bool free_allocations)
{
umpire::ResourceManager *SH_this =
static_cast<umpire::ResourceManager *>(self->addr);
// splicer begin class.ResourceManager.method.destroy_allocator_by_id_with_free
SH_this->destroyAllocator(id, free_allocations);
// splicer end class.ResourceManager.method.destroy_allocator_by_id_with_free
}

umpire_allocator * umpire_resourcemanager_get_allocator_for_ptr(
umpire_resourcemanager * self, void * ptr,
umpire_allocator * SHC_rv)
Expand Down
20 changes: 20 additions & 0 deletions src/umpire/interface/c_fortran/wrapResourceManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,26 @@ void umpire_resourcemanager_remove_alias_bufferify(
umpire_resourcemanager * self, const char * name, int Lname,
umpire_allocator allocator);

void umpire_resourcemanager_destroy_allocator_by_name(
umpire_resourcemanager * self, const char * name);

void umpire_resourcemanager_destroy_allocator_by_name_bufferify(
umpire_resourcemanager * self, const char * name, int Lname);

void umpire_resourcemanager_destroy_allocator_by_name_with_free(
umpire_resourcemanager * self, const char * name,
bool free_allocations);

void umpire_resourcemanager_destroy_allocator_by_name_with_free_bufferify(
umpire_resourcemanager * self, const char * name, int Lname,
bool free_allocations);

void umpire_resourcemanager_destroy_allocator_by_id(
umpire_resourcemanager * self, int id);

void umpire_resourcemanager_destroy_allocator_by_id_with_free(
umpire_resourcemanager * self, int id, bool free_allocations);

umpire_allocator * umpire_resourcemanager_get_allocator_for_ptr(
umpire_resourcemanager * self, void * ptr,
umpire_allocator * SHC_rv);
Expand Down
Loading