blob: 725d8a4d6fab7c42c501a6af67b5ce810bdd557f [file] [log] [blame]
/*----------------------------------------------------------------------------*/
/* Copyright (c) FIRST 2008-2017. All Rights Reserved. */
/* Open Source Software - may be modified and shared by FRC teams. The code */
/* must be accompanied by the FIRST BSD license file in the root directory of */
/* the project. */
/*----------------------------------------------------------------------------*/
#include "Resource.h"
#include "ErrorBase.h"
#include "WPIErrors.h"
using namespace frc;
priority_recursive_mutex Resource::m_createLock;
/**
* Allocate storage for a new instance of Resource.
*
* Allocate a bool array of values that will get initialized to indicate that no
* resources have been allocated yet. The indicies of the resources are [0 ..
* elements - 1].
*/
Resource::Resource(uint32_t elements) {
std::lock_guard<priority_recursive_mutex> sync(m_createLock);
m_isAllocated = std::vector<bool>(elements, false);
}
/**
* Factory method to create a Resource allocation-tracker *if* needed.
*
* @param r address of the caller's Resource pointer. If *r == nullptr,
* this will construct a Resource and make *r point to it. If
* *r != nullptr, i.e. the caller already has a Resource
* instance, this won't do anything.
* @param elements the number of elements for this Resource allocator to
* track, that is, it will allocate resource numbers in the
* range [0 .. elements - 1].
*/
/*static*/ void Resource::CreateResourceObject(std::unique_ptr<Resource>& r,
uint32_t elements) {
std::lock_guard<priority_recursive_mutex> sync(m_createLock);
if (!r) {
r = std::make_unique<Resource>(elements);
}
}
/**
* Allocate a resource.
*
* When a resource is requested, mark it allocated. In this case, a free
* resource value within the range is located and returned after it is marked
* allocated.
*/
uint32_t Resource::Allocate(const std::string& resourceDesc) {
std::lock_guard<priority_recursive_mutex> sync(m_allocateLock);
for (uint32_t i = 0; i < m_isAllocated.size(); i++) {
if (!m_isAllocated[i]) {
m_isAllocated[i] = true;
return i;
}
}
wpi_setWPIErrorWithContext(NoAvailableResources, resourceDesc);
return std::numeric_limits<uint32_t>::max();
}
/**
* Allocate a specific resource value.
*
* The user requests a specific resource value, i.e. channel number and it is
* verified unallocated, then returned.
*/
uint32_t Resource::Allocate(uint32_t index, const std::string& resourceDesc) {
std::lock_guard<priority_recursive_mutex> sync(m_allocateLock);
if (index >= m_isAllocated.size()) {
wpi_setWPIErrorWithContext(ChannelIndexOutOfRange, resourceDesc);
return std::numeric_limits<uint32_t>::max();
}
if (m_isAllocated[index]) {
wpi_setWPIErrorWithContext(ResourceAlreadyAllocated, resourceDesc);
return std::numeric_limits<uint32_t>::max();
}
m_isAllocated[index] = true;
return index;
}
/**
* Free an allocated resource.
*
* After a resource is no longer needed, for example a destructor is called for
* a channel assignment class, Free will release the resource value so it can
* be reused somewhere else in the program.
*/
void Resource::Free(uint32_t index) {
std::unique_lock<priority_recursive_mutex> sync(m_allocateLock);
if (index == std::numeric_limits<uint32_t>::max()) return;
if (index >= m_isAllocated.size()) {
wpi_setWPIError(NotAllocated);
return;
}
if (!m_isAllocated[index]) {
wpi_setWPIError(NotAllocated);
return;
}
m_isAllocated[index] = false;
}