#define EC_GROW_SIZE_AMOUNT 256
#include <array>
+#include <atomic>
#include <cstddef>
#include <vector>
#include <deque>
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;
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
if(ThreadCount >= 2) {
threadPool = std::make_unique<ThreadPool<ThreadCount> >();
}
+
+ deferringDeletions.store(0);
}
private:
}
}
+ 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.
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.
currentCapacity = 0;
deletedSet.clear();
resize(EC_INIT_ENTITIES_SIZE);
+
+ std::lock_guard<std::mutex> lock(deferredDeletionsMutex);
+ deferringDeletions.store(0);
+ deferredDeletions.clear();
}
private:
void* userData = nullptr,
const bool useThreadPool = false)
{
+ deferringDeletions.fetch_add(1);
using SignatureComponents =
typename EC::Meta::Matching<Signature, ComponentsList>::type;
using Helper =
}
threadPool->easyWakeAndWait();
}
+
+ handleDeferredDeletions();
}
/*!
void* userData = nullptr,
const bool useThreadPool = false)
{
+ deferringDeletions.fetch_add(1);
using SignatureComponents =
typename EC::Meta::Matching<Signature, ComponentsList>::type;
using Helper =
}
threadPool->easyWakeAndWait();
}
+
+ handleDeferredDeletions();
}
Function&& function,
void* userData = nullptr)
{
+ deferringDeletions.fetch_add(1);
while(forMatchingFunctions.find(functionIndex)
!= forMatchingFunctions.end())
{
}
})));
+ handleDeferredDeletions();
return functionIndex++;
}
*/
void callForMatchingFunctions(const bool useThreadPool = false)
{
+ deferringDeletions.fetch_add(1);
std::vector<BitsetType*> bitsets;
for(auto iter = forMatchingFunctions.begin();
iter != forMatchingFunctions.end();
std::get<2>(iter->second)(
useThreadPool, matching[i++], std::get<1>(iter->second));
}
+
+ handleDeferredDeletions();
}
/*!
{
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;
}
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];
}
}
);
+
+ handleDeferredDeletions();
}
/*!
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];
}
}
);
+
+ handleDeferredDeletions();
}
typedef void ForMatchingFn(std::size_t,
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) {
}
threadPool->easyWakeAndWait();
}
+
+ handleDeferredDeletions();
}
/*!
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) {
}
threadPool->easyWakeAndWait();
}
+
+ handleDeferredDeletions();
}
};
}
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)));
+ }
+ }
+}