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
|
||||
|
||||
#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();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue