Impl deferring deletions allowing for delete in fn
deleteEntity() is now allowed during a "forMatching" call, since it is deferred until the "forMatching" call(s) end.
This commit is contained in:
parent
e61d2724e6
commit
841a591aa4
2 changed files with 103 additions and 6 deletions
|
@ -11,6 +11,7 @@
|
||||||
#define EC_GROW_SIZE_AMOUNT 256
|
#define EC_GROW_SIZE_AMOUNT 256
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
|
#include <atomic>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <deque>
|
#include <deque>
|
||||||
|
@ -55,6 +56,12 @@ namespace EC
|
||||||
created and it will never be used, even if the "true" parameter is given
|
created and it will never be used, even if the "true" parameter is given
|
||||||
for functions that enable its usage.
|
for functions that enable its usage.
|
||||||
|
|
||||||
|
Note that when calling one of the "forMatching" functions that make use
|
||||||
|
of the internal ThreadPool, it is allowed to call addEntity() or
|
||||||
|
deleteEntity() as the functions cache which entities are alive before
|
||||||
|
running (allowing for addEntity()), and the functions defer deletions
|
||||||
|
during concurrent execution (allowing for deleteEntity()).
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
\code{.cpp}
|
\code{.cpp}
|
||||||
EC::Manager<TypeList<C0, C1, C2>, TypeList<T0, T1>> manager;
|
EC::Manager<TypeList<C0, C1, C2>, TypeList<T0, T1>> manager;
|
||||||
|
@ -96,6 +103,10 @@ namespace EC
|
||||||
|
|
||||||
std::unique_ptr<ThreadPool<ThreadCount> > threadPool;
|
std::unique_ptr<ThreadPool<ThreadCount> > threadPool;
|
||||||
|
|
||||||
|
std::atomic_uint deferringDeletions;
|
||||||
|
std::vector<std::size_t> deferredDeletions;
|
||||||
|
std::mutex deferredDeletionsMutex;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// section for "temporary" structures {{{
|
// section for "temporary" structures {{{
|
||||||
/// Temporary struct used internally by ThreadPool
|
/// Temporary struct used internally by ThreadPool
|
||||||
|
@ -191,6 +202,8 @@ namespace EC
|
||||||
if(ThreadCount >= 2) {
|
if(ThreadCount >= 2) {
|
||||||
threadPool = std::make_unique<ThreadPool<ThreadCount> >();
|
threadPool = std::make_unique<ThreadPool<ThreadCount> >();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
deferringDeletions.store(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -247,6 +260,16 @@ namespace EC
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void deleteEntityImpl(std::size_t id) {
|
||||||
|
if(hasEntity(id)) {
|
||||||
|
std::get<bool>(entities.at(id)) = false;
|
||||||
|
std::get<BitsetType>(entities.at(id)).reset();
|
||||||
|
deletedSet.insert(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
/*!
|
/*!
|
||||||
\brief Marks an entity for deletion.
|
\brief Marks an entity for deletion.
|
||||||
|
|
||||||
|
@ -254,16 +277,28 @@ namespace EC
|
||||||
addEntity is called. Thus calling addEntity may return an id of
|
addEntity is called. Thus calling addEntity may return an id of
|
||||||
a previously deleted Entity.
|
a previously deleted Entity.
|
||||||
*/
|
*/
|
||||||
void deleteEntity(const std::size_t& index)
|
void deleteEntity(std::size_t index)
|
||||||
{
|
{
|
||||||
if(hasEntity(index))
|
if(deferringDeletions.load() != 0) {
|
||||||
{
|
std::lock_guard<std::mutex> lock(deferredDeletionsMutex);
|
||||||
std::get<bool>(entities.at(index)) = false;
|
deferredDeletions.push_back(index);
|
||||||
std::get<BitsetType>(entities.at(index)).reset();
|
} else {
|
||||||
deletedSet.insert(index);
|
deleteEntityImpl(index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void handleDeferredDeletions() {
|
||||||
|
if(deferringDeletions.fetch_sub(1) == 1) {
|
||||||
|
std::lock_guard<std::mutex> lock(deferredDeletionsMutex);
|
||||||
|
for(std::size_t id : deferredDeletions) {
|
||||||
|
deleteEntityImpl(id);
|
||||||
|
}
|
||||||
|
deferredDeletions.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\brief Checks if the Entity with the given ID is in the system.
|
\brief Checks if the Entity with the given ID is in the system.
|
||||||
|
@ -603,6 +638,10 @@ namespace EC
|
||||||
currentCapacity = 0;
|
currentCapacity = 0;
|
||||||
deletedSet.clear();
|
deletedSet.clear();
|
||||||
resize(EC_INIT_ENTITIES_SIZE);
|
resize(EC_INIT_ENTITIES_SIZE);
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(deferredDeletionsMutex);
|
||||||
|
deferringDeletions.store(0);
|
||||||
|
deferredDeletions.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -711,6 +750,7 @@ namespace EC
|
||||||
void* userData = nullptr,
|
void* userData = nullptr,
|
||||||
const bool useThreadPool = false)
|
const bool useThreadPool = false)
|
||||||
{
|
{
|
||||||
|
deferringDeletions.fetch_add(1);
|
||||||
using SignatureComponents =
|
using SignatureComponents =
|
||||||
typename EC::Meta::Matching<Signature, ComponentsList>::type;
|
typename EC::Meta::Matching<Signature, ComponentsList>::type;
|
||||||
using Helper =
|
using Helper =
|
||||||
|
@ -786,6 +826,8 @@ namespace EC
|
||||||
}
|
}
|
||||||
threadPool->easyWakeAndWait();
|
threadPool->easyWakeAndWait();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleDeferredDeletions();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
@ -834,6 +876,7 @@ namespace EC
|
||||||
void* userData = nullptr,
|
void* userData = nullptr,
|
||||||
const bool useThreadPool = false)
|
const bool useThreadPool = false)
|
||||||
{
|
{
|
||||||
|
deferringDeletions.fetch_add(1);
|
||||||
using SignatureComponents =
|
using SignatureComponents =
|
||||||
typename EC::Meta::Matching<Signature, ComponentsList>::type;
|
typename EC::Meta::Matching<Signature, ComponentsList>::type;
|
||||||
using Helper =
|
using Helper =
|
||||||
|
@ -908,6 +951,8 @@ namespace EC
|
||||||
}
|
}
|
||||||
threadPool->easyWakeAndWait();
|
threadPool->easyWakeAndWait();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleDeferredDeletions();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -972,6 +1017,7 @@ namespace EC
|
||||||
Function&& function,
|
Function&& function,
|
||||||
void* userData = nullptr)
|
void* userData = nullptr)
|
||||||
{
|
{
|
||||||
|
deferringDeletions.fetch_add(1);
|
||||||
while(forMatchingFunctions.find(functionIndex)
|
while(forMatchingFunctions.find(functionIndex)
|
||||||
!= forMatchingFunctions.end())
|
!= forMatchingFunctions.end())
|
||||||
{
|
{
|
||||||
|
@ -1055,6 +1101,7 @@ namespace EC
|
||||||
}
|
}
|
||||||
})));
|
})));
|
||||||
|
|
||||||
|
handleDeferredDeletions();
|
||||||
return functionIndex++;
|
return functionIndex++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1172,6 +1219,7 @@ namespace EC
|
||||||
*/
|
*/
|
||||||
void callForMatchingFunctions(const bool useThreadPool = false)
|
void callForMatchingFunctions(const bool useThreadPool = false)
|
||||||
{
|
{
|
||||||
|
deferringDeletions.fetch_add(1);
|
||||||
std::vector<BitsetType*> bitsets;
|
std::vector<BitsetType*> bitsets;
|
||||||
for(auto iter = forMatchingFunctions.begin();
|
for(auto iter = forMatchingFunctions.begin();
|
||||||
iter != forMatchingFunctions.end();
|
iter != forMatchingFunctions.end();
|
||||||
|
@ -1191,6 +1239,8 @@ namespace EC
|
||||||
std::get<2>(iter->second)(
|
std::get<2>(iter->second)(
|
||||||
useThreadPool, matching[i++], std::get<1>(iter->second));
|
useThreadPool, matching[i++], std::get<1>(iter->second));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleDeferredDeletions();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
@ -1230,11 +1280,14 @@ namespace EC
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
deferringDeletions.fetch_add(1);
|
||||||
std::vector<std::vector<std::size_t> > matching =
|
std::vector<std::vector<std::size_t> > matching =
|
||||||
getMatchingEntities(std::vector<BitsetType*>{
|
getMatchingEntities(std::vector<BitsetType*>{
|
||||||
&std::get<BitsetType>(iter->second)}, useThreadPool);
|
&std::get<BitsetType>(iter->second)}, useThreadPool);
|
||||||
std::get<2>(iter->second)(
|
std::get<2>(iter->second)(
|
||||||
useThreadPool, matching[0], std::get<1>(iter->second));
|
useThreadPool, matching[0], std::get<1>(iter->second));
|
||||||
|
|
||||||
|
handleDeferredDeletions();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1428,6 +1481,7 @@ namespace EC
|
||||||
void* userData = nullptr,
|
void* userData = nullptr,
|
||||||
const bool useThreadPool = false)
|
const bool useThreadPool = false)
|
||||||
{
|
{
|
||||||
|
deferringDeletions.fetch_add(1);
|
||||||
std::vector<std::vector<std::size_t> >
|
std::vector<std::vector<std::size_t> >
|
||||||
multiMatchingEntities(SigList::size);
|
multiMatchingEntities(SigList::size);
|
||||||
BitsetType signatureBitsets[SigList::size];
|
BitsetType signatureBitsets[SigList::size];
|
||||||
|
@ -1578,6 +1632,8 @@ namespace EC
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
handleDeferredDeletions();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
@ -1636,6 +1692,7 @@ namespace EC
|
||||||
void* userData = nullptr,
|
void* userData = nullptr,
|
||||||
const bool useThreadPool = false)
|
const bool useThreadPool = false)
|
||||||
{
|
{
|
||||||
|
deferringDeletions.fetch_add(1);
|
||||||
std::vector<std::vector<std::size_t> > multiMatchingEntities(
|
std::vector<std::vector<std::size_t> > multiMatchingEntities(
|
||||||
SigList::size);
|
SigList::size);
|
||||||
BitsetType signatureBitsets[SigList::size];
|
BitsetType signatureBitsets[SigList::size];
|
||||||
|
@ -1791,6 +1848,8 @@ namespace EC
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
handleDeferredDeletions();
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef void ForMatchingFn(std::size_t,
|
typedef void ForMatchingFn(std::size_t,
|
||||||
|
@ -1818,6 +1877,7 @@ namespace EC
|
||||||
void forMatchingSimple(ForMatchingFn fn,
|
void forMatchingSimple(ForMatchingFn fn,
|
||||||
void *userData = nullptr,
|
void *userData = nullptr,
|
||||||
const bool useThreadPool = false) {
|
const bool useThreadPool = false) {
|
||||||
|
deferringDeletions.fetch_add(1);
|
||||||
const BitsetType signatureBitset =
|
const BitsetType signatureBitset =
|
||||||
BitsetType::template generateBitset<Signature>();
|
BitsetType::template generateBitset<Signature>();
|
||||||
if(!useThreadPool || !threadPool) {
|
if(!useThreadPool || !threadPool) {
|
||||||
|
@ -1872,6 +1932,8 @@ namespace EC
|
||||||
}
|
}
|
||||||
threadPool->easyWakeAndWait();
|
threadPool->easyWakeAndWait();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleDeferredDeletions();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
@ -1897,6 +1959,7 @@ namespace EC
|
||||||
ForMatchingFn fn,
|
ForMatchingFn fn,
|
||||||
void* userData = nullptr,
|
void* userData = nullptr,
|
||||||
const bool useThreadPool = false) {
|
const bool useThreadPool = false) {
|
||||||
|
deferringDeletions.fetch_add(1);
|
||||||
if(!useThreadPool || !threadPool) {
|
if(!useThreadPool || !threadPool) {
|
||||||
bool isValid;
|
bool isValid;
|
||||||
for(std::size_t i = 0; i < currentSize; ++i) {
|
for(std::size_t i = 0; i < currentSize; ++i) {
|
||||||
|
@ -1966,6 +2029,8 @@ namespace EC
|
||||||
}
|
}
|
||||||
threadPool->easyWakeAndWait();
|
threadPool->easyWakeAndWait();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleDeferredDeletions();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1399,3 +1399,35 @@ TEST(EC, ManagerWithLowThreadCount) {
|
||||||
EXPECT_EQ(component->y, 1);
|
EXPECT_EQ(component->y, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(EC, ManagerDeferredDeletions) {
|
||||||
|
using ManagerType = EC::Manager<ListComponentsAll, ListTagsAll, 8>;
|
||||||
|
ManagerType manager;
|
||||||
|
|
||||||
|
std::array<std::size_t, 24> entities;
|
||||||
|
for(std::size_t i = 0; i < entities.size(); ++i) {
|
||||||
|
entities.at(i) = manager.addEntity();
|
||||||
|
manager.addTag<T0>(entities.at(i));
|
||||||
|
if(i < entities.size() / 2) {
|
||||||
|
manager.addTag<T1>(entities.at(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto dataTuple = std::tuple<decltype(manager)*, std::size_t>
|
||||||
|
{&manager, entities.size()};
|
||||||
|
|
||||||
|
manager.forMatchingSignature<EC::Meta::TypeList<T0, T1>>([] (std::size_t id, void *data) {
|
||||||
|
auto *tuple = (std::tuple<ManagerType*, std::size_t>*)data;
|
||||||
|
std::size_t size = std::get<1>(*tuple);
|
||||||
|
std::get<0>(*tuple)->deleteEntity(id + size / 4);
|
||||||
|
}, &dataTuple, true);
|
||||||
|
|
||||||
|
for(std::size_t i = 0; i < entities.size(); ++i) {
|
||||||
|
if (entities.at(i) >= entities.size() / 4
|
||||||
|
&& entities.at(i) < entities.size() * 3 / 4) {
|
||||||
|
EXPECT_FALSE(manager.isAlive(entities.at(i)));
|
||||||
|
} else {
|
||||||
|
EXPECT_TRUE(manager.isAlive(entities.at(i)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue