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:
Stephen Seo 2022-01-20 17:32:05 +09:00
parent e61d2724e6
commit 841a591aa4
2 changed files with 103 additions and 6 deletions

View file

@ -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();
} }
}; };
} }

View file

@ -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)));
}
}
}