Merge branch 'master' into cxx17
This commit is contained in:
commit
b2cd215faf
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>
|
||||||
|
@ -54,6 +55,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;
|
||||||
|
@ -95,6 +102,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
|
||||||
|
@ -190,6 +201,8 @@ namespace EC
|
||||||
if constexpr(ThreadCount >= 2) {
|
if constexpr(ThreadCount >= 2) {
|
||||||
threadPool = std::make_unique<ThreadPool<ThreadCount> >();
|
threadPool = std::make_unique<ThreadPool<ThreadCount> >();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
deferringDeletions.store(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
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.
|
\brief Marks an entity for deletion.
|
||||||
|
|
||||||
|
@ -253,16 +276,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.
|
||||||
|
@ -605,6 +640,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:
|
||||||
|
@ -713,6 +752,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 =
|
||||||
|
@ -788,6 +828,8 @@ namespace EC
|
||||||
}
|
}
|
||||||
threadPool->easyWakeAndWait();
|
threadPool->easyWakeAndWait();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleDeferredDeletions();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
@ -836,6 +878,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 =
|
||||||
|
@ -910,6 +953,8 @@ namespace EC
|
||||||
}
|
}
|
||||||
threadPool->easyWakeAndWait();
|
threadPool->easyWakeAndWait();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleDeferredDeletions();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -974,6 +1019,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())
|
||||||
{
|
{
|
||||||
|
@ -1057,6 +1103,7 @@ namespace EC
|
||||||
}
|
}
|
||||||
})));
|
})));
|
||||||
|
|
||||||
|
handleDeferredDeletions();
|
||||||
return functionIndex++;
|
return functionIndex++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1174,6 +1221,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();
|
||||||
|
@ -1193,6 +1241,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();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
@ -1232,11 +1282,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1430,6 +1483,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];
|
||||||
|
@ -1580,6 +1634,8 @@ namespace EC
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
handleDeferredDeletions();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
@ -1638,6 +1694,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];
|
||||||
|
@ -1793,6 +1850,8 @@ namespace EC
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
handleDeferredDeletions();
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef void ForMatchingFn(std::size_t,
|
typedef void ForMatchingFn(std::size_t,
|
||||||
|
@ -1820,6 +1879,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) {
|
||||||
|
@ -1874,6 +1934,8 @@ namespace EC
|
||||||
}
|
}
|
||||||
threadPool->easyWakeAndWait();
|
threadPool->easyWakeAndWait();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleDeferredDeletions();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
@ -1899,6 +1961,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) {
|
||||||
|
@ -1968,6 +2031,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