idm: Implement creation/destruction invalidation counter

* "Ensures" newely created IDs won't have the same ID as old destroyed objects for lv2_obj. (256 tries cycle)

Similar to how the kernel implements it.
This commit is contained in:
Eladash 2019-11-28 12:17:16 +02:00 committed by Ivan
parent 224a0e4b1a
commit 3265772ae4
5 changed files with 44 additions and 17 deletions

View File

@ -60,6 +60,7 @@ public:
static const u32 id_base = 0x01000000; // TODO (used to determine thread type)
static const u32 id_step = 1;
static const u32 id_count = 2048;
static constexpr std::pair<u32, u32> id_invl_range = {12, 12};
virtual std::string dump_all() const override;
virtual std::string dump_regs() const override;

View File

@ -260,6 +260,7 @@ struct lv2_spu_group
static const u32 id_base = 0x04000100;
static const u32 id_step = 0x100;
static const u32 id_count = 255;
static constexpr std::pair<u32, u32> id_invl_range = {0, 8};
const std::string name;
const u32 id;

View File

@ -66,6 +66,7 @@ struct lv2_obj
static const u32 id_step = 0x100;
static const u32 id_count = 8192;
static constexpr std::pair<u32, u32> id_invl_range = {0, 8};
private:
enum thread_cmd : s32

View File

@ -1,4 +1,4 @@
#include "stdafx.h"
#include "stdafx.h"
#include "IdManager.h"
#include "Utilities/Thread.h"
@ -7,7 +7,7 @@ shared_mutex id_manager::g_mutex;
thread_local DECLARE(idm::g_id);
DECLARE(idm::g_map);
id_manager::id_map::pointer idm::allocate_id(const id_manager::id_key& info, u32 base, u32 step, u32 count)
id_manager::id_map::pointer idm::allocate_id(const id_manager::id_key& info, u32 base, u32 step, u32 count, std::pair<u32, u32> invl_range)
{
// Base type id is stored in value
auto& vec = g_map[info.value()];
@ -32,8 +32,10 @@ id_manager::id_map::pointer idm::allocate_id(const id_manager::id_key& info, u32
// Look for free ID
if (!ptr->second)
{
g_id = next;
ptr->first = id_manager::id_key(next, info.type());
// Incremenet ID invalidation counter
const u32 id = next | ((ptr->first + (1u << invl_range.first)) & (invl_range.second ? (((1u << invl_range.second) - 1) << invl_range.first) : 0));
g_id = id;
ptr->first = id_manager::id_key(id, info.type());
return ptr;
}
}

View File

@ -18,22 +18,39 @@ namespace id_manager
{
static_assert(sizeof(T) == 0, "ID object must specify: id_base, id_step, id_count");
static const u32 base = 1; // First ID (N = 0)
static const u32 step = 1; // Any ID: N * id_step + id_base
static const u32 count = 65535; // Limit: N < id_count
static const u32 invalid = 0;
static constexpr u32 base = 1; // First ID (N = 0)
static constexpr u32 step = 1; // Any ID: N * id_step + id_base
static constexpr u32 count = 65535; // Limit: N < id_count
static constexpr u32 invalid = 0;
static constexpr std::pair<u32, u32> invl_range{0, 0};
};
template <typename T, typename = void>
struct invl_range_extract_impl
{
static constexpr std::pair<u32, u32> invl_range{0, 0};
};
template <typename T>
struct invl_range_extract_impl<T, std::void_t<decltype(&T::id_invl_range)>>
{
static constexpr std::pair<u32, u32> invl_range = T::id_invl_range;
};
template <typename T>
struct id_traits<T, std::void_t<decltype(&T::id_base), decltype(&T::id_step), decltype(&T::id_count)>>
{
static const u32 base = T::id_base;
static const u32 step = T::id_step;
static const u32 count = T::id_count;
static const u32 invalid = -+!base;
static constexpr u32 base = T::id_base;
static constexpr u32 step = T::id_step;
static constexpr u32 count = T::id_count;
static constexpr u32 invalid = -+!base;
static constexpr std::pair<u32, u32> invl_range = invl_range_extract_impl<T>::invl_range;
// Note: full 32 bits range cannot be used at current implementation
static_assert(count && step && u64{step} * (count - 1) + base < u64{UINT32_MAX} + (base != 0 ? 1 : 0), "ID traits: invalid object range");
// TODO: Add more conditions
static_assert(!invl_range.second || (u64{invl_range.second} + invl_range.first <= 32 /*....*/ ));
};
// Correct usage testing
@ -138,8 +155,10 @@ class idm
{
using traits = id_manager::id_traits<T>;
constexpr u32 mask_out = ((1u << traits::invl_range.second) - 1) << traits::invl_range.first;
// Note: if id is lower than base, diff / step will be higher than count
u32 diff = id - traits::base;
u32 diff = (id & ~mask_out) - traits::base;
if (diff % traits::step)
{
@ -230,7 +249,7 @@ class idm
};
// Prepare new ID (returns nullptr if out of resources)
static id_manager::id_map::pointer allocate_id(const id_manager::id_key& info, u32 base, u32 step, u32 count);
static id_manager::id_map::pointer allocate_id(const id_manager::id_key& info, u32 base, u32 step, u32 count, std::pair<u32, u32> invl_range);
// Find ID (additionally check type if types are not equal)
template <typename T, typename Type>
@ -258,7 +277,10 @@ class idm
{
if (std::is_same<T, Type>::value || data.first.type() == get_type<Type>())
{
return &data;
if (!id_manager::id_traits<Type>::invl_range.second || data.first.value() == id)
{
return &data;
}
}
}
@ -280,7 +302,7 @@ class idm
// Allocate new id
std::lock_guard lock(id_manager::g_mutex);
if (auto* place = allocate_id(info, traits::base, traits::step, traits::count))
if (auto* place = allocate_id(info, traits::base, traits::step, traits::count, traits::invl_range))
{
// Get object, store it
place->second = provider();