Merge branch 'master' into cxx17

This commit is contained in:
Stephen Seo 2022-01-20 17:33:30 +09:00
commit b2cd215faf
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>
@ -54,6 +55,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;
@ -95,6 +102,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
@ -190,6 +201,8 @@ namespace EC
if constexpr(ThreadCount >= 2) {
threadPool = std::make_unique<ThreadPool<ThreadCount> >();
}
deferringDeletions.store(0);
}
private:
@ -246,6 +259,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.
@ -253,16 +276,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.
@ -605,6 +640,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:
@ -713,6 +752,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 =
@ -788,6 +828,8 @@ namespace EC
}
threadPool->easyWakeAndWait();
}
handleDeferredDeletions();
}
/*!
@ -836,6 +878,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 =
@ -910,6 +953,8 @@ namespace EC
}
threadPool->easyWakeAndWait();
}
handleDeferredDeletions();
}
@ -974,6 +1019,7 @@ namespace EC
Function&& function,
void* userData = nullptr)
{
deferringDeletions.fetch_add(1);
while(forMatchingFunctions.find(functionIndex)
!= forMatchingFunctions.end())
{
@ -1057,6 +1103,7 @@ namespace EC
}
})));
handleDeferredDeletions();
return functionIndex++;
}
@ -1174,6 +1221,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();
@ -1193,6 +1241,8 @@ namespace EC
std::get<2>(iter->second)(
useThreadPool, matching[i++], std::get<1>(iter->second));
}
handleDeferredDeletions();
}
/*!
@ -1232,11 +1282,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;
}
@ -1430,6 +1483,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];
@ -1580,6 +1634,8 @@ namespace EC
}
}
);
handleDeferredDeletions();
}
/*!
@ -1638,6 +1694,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];
@ -1793,6 +1850,8 @@ namespace EC
}
}
);
handleDeferredDeletions();
}
typedef void ForMatchingFn(std::size_t,
@ -1820,6 +1879,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) {
@ -1874,6 +1934,8 @@ namespace EC
}
threadPool->easyWakeAndWait();
}
handleDeferredDeletions();
}
/*!
@ -1899,6 +1961,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) {
@ -1968,6 +2031,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)));
}
}
}