EXPERIMENTAL: this is a fundamental change to the function of ChannelLookup, instead of maintaining a list in which we add or remove elements as they are available or in use, we will maintain a mapped channel ID to index bit array, where each bit index within the array of bits represents the availability state of the channel, this should make it far more stable to mark a channel as free or allocated instead of manipulating an array everytime;

pull/121/merge
Bryan Biedenkapp 3 weeks ago
parent 436bbf634e
commit 8bf2bf16d3

@ -4,7 +4,7 @@
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright (C) 2022,2024 Bryan Biedenkapp, N2PLL
* Copyright (C) 2022,2024,2026 Bryan Biedenkapp, N2PLL
*
*/
#include "lookups/AffiliationLookup.h"
@ -341,12 +341,8 @@ bool AffiliationLookup::grantCh(uint32_t dstId, uint32_t srcId, uint32_t grantTi
return false;
}
if (!m_chLookup->isRFChAvailable()) {
return false;
}
uint32_t chNo = m_chLookup->getFirstRFChannel();
if (!m_chLookup->removeRFCh(chNo)) {
uint32_t chNo = 0U;
if (!m_chLookup->takeFirstRFChannel(chNo)) {
return false;
}
@ -451,7 +447,7 @@ bool AffiliationLookup::releaseGrant(uint32_t dstId, bool releaseAll)
m_netGrantedTable.erase(dstId);
if (m_chLookup != nullptr) {
m_chLookup->addRFCh(chNo, true);
m_chLookup->freeRFCh(chNo);
}
if (m_rfGrantChCnt > 0U) {

@ -4,12 +4,14 @@
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright (C) 2024-2025 Bryan Biedenkapp, N2PLL
* Copyright (C) 2024-2026 Bryan Biedenkapp, N2PLL
*
*/
#include "lookups/ChannelLookup.h"
#include "Log.h"
#include <algorithm>
using namespace lookups;
// ---------------------------------------------------------------------------
@ -19,10 +21,15 @@ using namespace lookups;
/* Initializes a new instance of the ChannelLookup class. */
ChannelLookup::ChannelLookup() :
m_rfChTable(),
m_rfChMutex(),
m_idxToCh(),
m_chToIdx(),
m_freeBits(),
m_rfChDataTable()
{
m_rfChTable.clear();
m_idxToCh.clear();
m_chToIdx.clear();
m_freeBits.clear();
}
/* Finalizes a instance of the ChannelLookup class. */
@ -47,41 +54,230 @@ VoiceChData ChannelLookup::getRFChData(uint32_t chNo) const
return data;
}
/* Helper to add a RF channel. */
/* Helper to get first available channel number. */
uint32_t ChannelLookup::getFirstRFChannel() const
{
std::lock_guard<std::mutex> lock(m_rfChMutex);
uint32_t idx = 0U;
if (!findFirstClearBit(idx) || idx >= m_idxToCh.size()) {
return 0U;
}
return m_idxToCh[idx];
}
/* Helper to atomically take first available RF channel. */
bool ChannelLookup::takeFirstRFChannel(uint32_t& chNo)
{
chNo = 0U;
std::lock_guard<std::mutex> lock(m_rfChMutex);
uint32_t idx = 0U;
if (!findFirstClearBit(idx) || idx >= m_idxToCh.size()) {
return false;
}
setBit(idx);
chNo = m_idxToCh[idx];
return true;
}
/* Helper to get the count of RF channels. */
uint8_t ChannelLookup::rfChSize() const
{
std::lock_guard<std::mutex> lock(m_rfChMutex);
uint32_t count = 0U;
for (uint32_t idx = 0U; idx < m_idxToCh.size(); ++idx) {
count++;
}
if (count > 255U) {
return 255U;
}
return (uint8_t)count;
}
/* Helper to get RF channels table. */
bool ChannelLookup::addRFCh(uint32_t chNo, bool force)
std::vector<uint32_t> ChannelLookup::rfChTable() const
{
std::lock_guard<std::mutex> lock(m_rfChMutex);
std::vector<uint32_t> channels;
channels.reserve(m_idxToCh.size());
for (uint32_t idx = 0U; idx < m_idxToCh.size(); ++idx) {
channels.push_back(m_idxToCh[idx]);
}
return channels;
}
/* Helper to initialize a RF channel. */
bool ChannelLookup::initializeRFCh(uint32_t chNo)
{
if (chNo == 0U) {
return false;
}
if (force) {
m_rfChTable.push_back(chNo);
std::lock_guard<std::mutex> lock(m_rfChMutex);
// check if channel already exists, if not add it and mark as free (clear bit)
auto it = m_chToIdx.find(chNo);
if (it == m_chToIdx.end()) {
uint32_t idx = (uint32_t)m_idxToCh.size();
m_chToIdx[chNo] = idx;
m_idxToCh.push_back(chNo);
ensureBitCapacity(idx);
clearBit(idx);
return true;
}
return false;
}
/* Helper to mark a RF channel allocated for use. */
bool ChannelLookup::allocRFCh(uint32_t chNo)
{
if (chNo == 0U) {
return false;
}
std::lock_guard<std::mutex> lock(m_rfChMutex);
// check if channel already exists, if not add it and mark allocated (set bit)
auto it = m_chToIdx.find(chNo);
if (it == m_chToIdx.end()) {
uint32_t idx = (uint32_t)m_idxToCh.size();
m_chToIdx[chNo] = idx;
m_idxToCh.push_back(chNo);
ensureBitCapacity(idx);
setBit(idx);
return true;
}
auto it = std::find(m_rfChTable.begin(), m_rfChTable.end(), chNo);
if (it == m_rfChTable.end()) {
m_rfChTable.push_back(chNo);
// channel exists, set bit if currently free
uint32_t idx = it->second;
if (!isBitSet(idx)) {
setBit(idx);
return true;
}
return false;
}
/* Helper to remove a RF channel. */
/* Helper to mark a RF channel as free for use. */
bool ChannelLookup::removeRFCh(uint32_t chNo)
bool ChannelLookup::freeRFCh(uint32_t chNo)
{
if (chNo == 0U) {
return false;
}
auto it = std::find(m_rfChTable.begin(), m_rfChTable.end(), chNo);
if (it != m_rfChTable.end()) {
m_rfChTable.erase(it);
std::lock_guard<std::mutex> lock(m_rfChMutex);
// check if channel exists and bit is set (allocated), then clear to free
auto it = m_chToIdx.find(chNo);
if (it == m_chToIdx.end()) {
return false;
}
// channel exists, clear bit if allocated
uint32_t idx = it->second;
if (!isBitSet(idx)) {
return false;
}
clearBit(idx);
return true;
}
/* Helper to determine if there are any RF channels available. */
bool ChannelLookup::isRFChAvailable() const
{
std::lock_guard<std::mutex> lock(m_rfChMutex);
for (uint32_t idx = 0U; idx < m_idxToCh.size(); ++idx) {
if (!isBitSet(idx)) {
return true;
}
}
return false;
}
// ---------------------------------------------------------------------------
// Private Class Members
// ---------------------------------------------------------------------------
/* Ensures bitset has enough words for the given index. */
void ChannelLookup::ensureBitCapacity(uint32_t idx)
{
size_t wordIdx = (size_t)(idx / BITS_PER_WORD);
if (wordIdx >= m_freeBits.size()) {
m_freeBits.resize(wordIdx + 1U, 0ULL);
}
}
/* Determines if the bit for index is currently set. */
bool ChannelLookup::isBitSet(uint32_t idx) const
{
size_t wordIdx = (size_t)(idx / BITS_PER_WORD);
if (wordIdx >= m_freeBits.size()) {
return false;
}
uint32_t bit = idx % BITS_PER_WORD;
return ((m_freeBits[wordIdx] >> bit) & 1ULL) != 0ULL;
}
/* Sets the bit for index. */
void ChannelLookup::setBit(uint32_t idx)
{
ensureBitCapacity(idx);
size_t wordIdx = (size_t)(idx / BITS_PER_WORD);
uint32_t bit = idx % BITS_PER_WORD;
m_freeBits[wordIdx] |= (1ULL << bit);
}
/* Clears the bit for index. */
void ChannelLookup::clearBit(uint32_t idx)
{
size_t wordIdx = (size_t)(idx / BITS_PER_WORD);
if (wordIdx >= m_freeBits.size()) {
return;
}
uint32_t bit = idx % BITS_PER_WORD;
m_freeBits[wordIdx] &= ~(1ULL << bit);
}
/* Finds the first available (clear) channel bit index. */
bool ChannelLookup::findFirstClearBit(uint32_t& idx) const
{
for (uint32_t candidateIdx = 0U; candidateIdx < m_idxToCh.size(); ++candidateIdx) {
if (!isBitSet(candidateIdx)) {
idx = candidateIdx;
return true;
}
}
return false;
}

@ -4,7 +4,7 @@
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright (C) 2024-2025 Bryan Biedenkapp, N2PLL
* Copyright (C) 2024-2026 Bryan Biedenkapp, N2PLL
*
*/
/**
@ -21,11 +21,14 @@
#define __CHANNEL_LOOKUP_H__
#include "common/Defines.h"
#include "common/concurrent/vector.h"
#include "common/concurrent/unordered_map.h"
#include "common/Timer.h"
#include <cstdio>
#include <cstdint>
#include <mutex>
#include <unordered_map>
#include <vector>
namespace lookups
{
@ -232,40 +235,84 @@ namespace lookups
* @brief Helper to get first available channel number.
* @returns uint32_t First available channel number.
*/
uint32_t getFirstRFChannel() const { return m_rfChTable.at(0); }
uint32_t getFirstRFChannel() const;
/**
* @brief Helper to atomically acquire the first available channel number.
* @param chNo Acquired channel number.
* @returns bool True, if a channel was acquired, otherwise false.
*/
bool takeFirstRFChannel(uint32_t& chNo);
/**
* @brief Gets the count of RF channels.
* @returns uint8_t Total count of RF channels.
*/
uint8_t rfChSize() const { return m_rfChTable.size(); }
uint8_t rfChSize() const;
/**
* @brief Gets the RF channels table.
* @returns std::vector<uint32_t> RF channel table.
*/
std::vector<uint32_t> rfChTable() const { return m_rfChTable.get(); }
std::vector<uint32_t> rfChTable() const;
/**
* @brief Helper to initialize a RF channel.
* @param chNo Channel Number.
* @returns bool True, if channel initialized, otherwise false.
*/
bool initializeRFCh(uint32_t chNo);
/**
* @brief Helper to add a RF channel.
* @brief Helper to mark a RF channel allocated for use.
* @param chNo Channel Number.
* @param force Flag indicating the channel should be forcibly added.
* @returns bool True, if channel added, otherwise false.
* @returns bool True, if channel allocated, otherwise false.
*/
bool addRFCh(uint32_t chNo, bool force = false);
bool allocRFCh(uint32_t chNo);
/**
* @brief Helper to remove a RF channel.
* @brief Helper to mark a RF channel as free for use.
* @param chNo Channel Number.
* @returns bool True, if channel remove, otherwise false.
* @returns bool True, if channel freed, otherwise false.
*/
bool removeRFCh(uint32_t chNo);
bool freeRFCh(uint32_t chNo);
/**
* @brief Helper to determine if there are any RF channels available..
* @returns bool True, if any RF channels are available for use, otherwise false.
*/
bool isRFChAvailable() const { return !m_rfChTable.empty(); }
bool isRFChAvailable() const;
/** @} */
private:
concurrent::vector<uint32_t> m_rfChTable;
static constexpr uint32_t BITS_PER_WORD = 64U;
/**
* @brief Ensures bitset has enough words for the given index.
* @param idx Index of the bit.
*/
void ensureBitCapacity(uint32_t idx);
/**
* @brief Determines if the bit for index is currently set (allocated).
* @param idx Bit index.
* @returns bool True, if the bit is set, otherwise false.
*/
bool isBitSet(uint32_t idx) const;
/**
* @brief Sets the bit for index.
* @param idx Bit index.
*/
void setBit(uint32_t idx);
/**
* @brief Clears the bit for index.
* @param idx Bit index.
*/
void clearBit(uint32_t idx);
/**
* @brief Finds the first available (clear) channel bit index.
* @param idx Index of the first clear bit.
* @returns bool True, if an available channel bit was found, otherwise false.
*/
bool findFirstClearBit(uint32_t& idx) const;
mutable std::mutex m_rfChMutex;
std::vector<uint32_t> m_idxToCh;
std::unordered_map<uint32_t, uint32_t> m_chToIdx;
std::vector<uint64_t> m_freeBits;
concurrent::unordered_map<uint32_t, VoiceChData> m_rfChDataTable;
};
} // namespace lookups

@ -304,13 +304,13 @@ bool Host::readParams()
VoiceChData data = VoiceChData(chId, chNo, rxChId, rxChNo, rpcApiAddress, rpcApiPort, rpcApiPassword);
m_channelLookup->setRFChData(chNo, data);
m_channelLookup->addRFCh(chNo);
m_channelLookup->initializeRFCh(chNo);
} else {
::LogInfoEx(LOG_HOST, "Voice Channel Id %u Channel No $%04X (%u-%u) RPC Address %s:%u", chId, chNo, chId, chNo, rpcApiAddress.c_str(), rpcApiPort);
VoiceChData data = VoiceChData(chId, chNo, rpcApiAddress, rpcApiPort, rpcApiPassword);
m_channelLookup->setRFChData(chNo, data);
m_channelLookup->addRFCh(chNo);
m_channelLookup->initializeRFCh(chNo);
}
}

@ -4,7 +4,7 @@
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright (C) 2023-2024 Bryan Biedenkapp, N2PLL
* Copyright (C) 2023-2026 Bryan Biedenkapp, N2PLL
*
*/
#include "common/Log.h"
@ -62,7 +62,7 @@ bool DMRAffiliationLookup::grantChSlot(uint32_t dstId, uint32_t srcId, uint8_t s
}
if (getAvailableSlotForChannel(chNo) == 0U || chNo == m_tsccChNo) {
m_chLookup->removeRFCh(chNo);
m_chLookup->allocRFCh(chNo);
}
__lock();
@ -136,7 +136,7 @@ bool DMRAffiliationLookup::releaseGrant(uint32_t dstId, bool releaseAll)
m_grantChSlotTable.erase(dstId);
m_netGrantedTable.erase(dstId);
m_chLookup->addRFCh(chNo);
m_chLookup->freeRFCh(chNo);
if (m_rfGrantChCnt > 0U) {
m_rfGrantChCnt--;

Loading…
Cancel
Save

Powered by TurnKey Linux.