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
#include <array>
#include <atomic>
#include <cstddef>
#include <vector>
#include <deque>
@ -55,6 +56,12 @@ namespace EC
created and it will never be used, even if the "true" parameter is given
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:
\code{.cpp}
EC::Manager<TypeList<C0, C1, C2>, TypeList<T0, T1>> manager;
@ -96,6 +103,10 @@ namespace EC
std::unique_ptr<ThreadPool<ThreadCount> > threadPool;
std::atomic_uint deferringDeletions;
std::vector<std::size_t> deferredDeletions;
std::mutex deferredDeletionsMutex;
public:
// section for "temporary" structures {{{
/// Temporary struct used internally by ThreadPool
@ -191,6 +202,8 @@ namespace EC
if(ThreadCount >= 2) {
threadPool = std::make_unique<ThreadPool<ThreadCount> >();
}
deferringDeletions.store(0);
}
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.
@ -254,16 +277,28 @@ namespace EC
addEntity is called. Thus calling addEntity may return an id of
a previously deleted Entity.
*/
void deleteEntity(const std::size_t& index)
void deleteEntity(std::size_t index)
{
if(hasEntity(index))
{
std::get<bool>(entities.at(index)) = false;
std::get<BitsetType>(entities.at(index)).reset();
deletedSet.insert(index);
if(deferringDeletions.load() != 0) {
std::lock_guard<std::mutex> lock(deferredDeletionsMutex);
deferredDeletions.push_back(index);
} else {
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.
@ -603,6 +638,10 @@ namespace EC
currentCapacity = 0;
deletedSet.clear();
resize(EC_INIT_ENTITIES_SIZE);
std::lock_guard<std::mutex> lock(deferredDeletionsMutex);
deferringDeletions.store(0);
deferredDeletions.clear();
}
private:
@ -711,6 +750,7 @@ namespace EC
void* userData = nullptr,
const bool useThreadPool = false)
{
deferringDeletions.fetch_add(1);
using SignatureComponents =
typename EC::Meta::Matching<Signature, ComponentsList>::type;
using Helper =
@ -786,6 +826,8 @@ namespace EC
}
threadPool->easyWakeAndWait();
}
handleDeferredDeletions();
}
/*!
@ -834,6 +876,7 @@ namespace EC
void* userData = nullptr,
const bool useThreadPool = false)
{
deferringDeletions.fetch_add(1);
using SignatureComponents =
typename EC::Meta::Matching<Signature, ComponentsList>::type;
using Helper =
@ -908,6 +951,8 @@ namespace EC
}
threadPool->easyWakeAndWait();
}
handleDeferredDeletions();
}
@ -972,6 +1017,7 @@ namespace EC
Function&& function,
void* userData = nullptr)
{
deferringDeletions.fetch_add(1);
while(forMatchingFunctions.find(functionIndex)
!= forMatchingFunctions.end())
{
@ -1055,6 +1101,7 @@ namespace EC
}
})));
handleDeferredDeletions();
return functionIndex++;
}
@ -1172,6 +1219,7 @@ namespace EC
*/
void callForMatchingFunctions(const bool useThreadPool = false)
{
deferringDeletions.fetch_add(1);
std::vector<BitsetType*> bitsets;
for(auto iter = forMatchingFunctions.begin();
iter != forMatchingFunctions.end();
@ -1191,6 +1239,8 @@ namespace EC
std::get<2>(iter->second)(
useThreadPool, matching[i++], std::get<1>(iter->second));
}
handleDeferredDeletions();
}
/*!
@ -1230,11 +1280,14 @@ namespace EC
{
return false;
}
deferringDeletions.fetch_add(1);
std::vector<std::vector<std::size_t> > matching =
getMatchingEntities(std::vector<BitsetType*>{
&std::get<BitsetType>(iter->second)}, useThreadPool);
std::get<2>(iter->second)(
useThreadPool, matching[0], std::get<1>(iter->second));
handleDeferredDeletions();
return true;
}
@ -1428,6 +1481,7 @@ namespace EC
void* userData = nullptr,
const bool useThreadPool = false)
{
deferringDeletions.fetch_add(1);
std::vector<std::vector<std::size_t> >
multiMatchingEntities(SigList::size);
BitsetType signatureBitsets[SigList::size];
@ -1578,6 +1632,8 @@ namespace EC
}
}
);
handleDeferredDeletions();
}
/*!
@ -1636,6 +1692,7 @@ namespace EC
void* userData = nullptr,
const bool useThreadPool = false)
{
deferringDeletions.fetch_add(1);
std::vector<std::vector<std::size_t> > multiMatchingEntities(
SigList::size);
BitsetType signatureBitsets[SigList::size];
@ -1791,6 +1848,8 @@ namespace EC
}
}
);
handleDeferredDeletions();
}
typedef void ForMatchingFn(std::size_t,
@ -1818,6 +1877,7 @@ namespace EC
void forMatchingSimple(ForMatchingFn fn,
void *userData = nullptr,
const bool useThreadPool = false) {
deferringDeletions.fetch_add(1);
const BitsetType signatureBitset =
BitsetType::template generateBitset<Signature>();
if(!useThreadPool || !threadPool) {
@ -1872,6 +1932,8 @@ namespace EC
}
threadPool->easyWakeAndWait();
}
handleDeferredDeletions();
}
/*!
@ -1897,6 +1959,7 @@ namespace EC
ForMatchingFn fn,
void* userData = nullptr,
const bool useThreadPool = false) {
deferringDeletions.fetch_add(1);
if(!useThreadPool || !threadPool) {
bool isValid;
for(std::size_t i = 0; i < currentSize; ++i) {
@ -1966,6 +2029,8 @@ namespace EC
}
threadPool->easyWakeAndWait();
}
handleDeferredDeletions();
}
};
}

View file

@ -1399,3 +1399,35 @@ TEST(EC, ManagerWithLowThreadCount) {
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)));
}
}
}