Fix possible data-race with isAlive() calls

Removed isAlive() calls from functions passed to ThreadPool, and instead use an
unordered_set prior to using ThreadPool to "cache" which entities are not alive.
This is so that if a function passed to ThreadPool modifies the ECManager (e.g.
creating a new entity), then there will be no data race from also calling
isAlive(). (Note that this does not protect against "deleting" entities from the
ECManager during use of ThreadPool. It is expected for the caller to do the
"deleting" after use of stored/called functions is finished.)
This commit is contained in:
Stephen Seo 2022-01-20 14:31:23 +09:00
parent f27c22675a
commit e61d2724e6

View file

@ -105,6 +105,7 @@ namespace EC
EntitiesType *entities; EntitiesType *entities;
const BitsetType *signature; const BitsetType *signature;
void *userData; void *userData;
std::unordered_set<std::size_t> dead;
}; };
/// Temporary struct used internally by ThreadPool /// Temporary struct used internally by ThreadPool
template <typename Function> template <typename Function>
@ -115,6 +116,7 @@ namespace EC
BitsetType *signature; BitsetType *signature;
void *userData; void *userData;
Function *fn; Function *fn;
std::unordered_set<std::size_t> dead;
}; };
/// Temporary struct used internally by ThreadPool /// Temporary struct used internally by ThreadPool
struct TPFnDataStructTwo { struct TPFnDataStructTwo {
@ -123,6 +125,7 @@ namespace EC
EntitiesType *entities; EntitiesType *entities;
void *userData; void *userData;
const std::vector<std::size_t> *matching; const std::vector<std::size_t> *matching;
std::unordered_set<std::size_t> dead;
}; };
/// Temporary struct used internally by ThreadPool /// Temporary struct used internally by ThreadPool
struct TPFnDataStructThree { struct TPFnDataStructThree {
@ -132,6 +135,7 @@ namespace EC
const std::vector<BitsetType*> *bitsets; const std::vector<BitsetType*> *bitsets;
EntitiesType *entities; EntitiesType *entities;
std::mutex *mutex; std::mutex *mutex;
std::unordered_set<std::size_t> dead;
}; };
/// Temporary struct used internally by ThreadPool /// Temporary struct used internally by ThreadPool
struct TPFnDataStructFour { struct TPFnDataStructFour {
@ -141,6 +145,7 @@ namespace EC
multiMatchingEntities; multiMatchingEntities;
BitsetType *signatures; BitsetType *signatures;
std::mutex *mutex; std::mutex *mutex;
std::unordered_set<std::size_t> dead;
}; };
/// Temporary struct used internally by ThreadPool /// Temporary struct used internally by ThreadPool
struct TPFnDataStructFive { struct TPFnDataStructFive {
@ -150,6 +155,7 @@ namespace EC
void *userData; void *userData;
std::vector<std::vector<std::size_t> >* std::vector<std::vector<std::size_t> >*
multiMatchingEntities; multiMatchingEntities;
std::unordered_set<std::size_t> dead;
}; };
/// Temporary struct used internally by ThreadPool /// Temporary struct used internally by ThreadPool
struct TPFnDataStructSix { struct TPFnDataStructSix {
@ -159,6 +165,7 @@ namespace EC
multiMatchingEntities; multiMatchingEntities;
BitsetType *bitsets; BitsetType *bitsets;
std::mutex *mutex; std::mutex *mutex;
std::unordered_set<std::size_t> dead;
}; };
/// Temporary struct used internally by ThreadPool /// Temporary struct used internally by ThreadPool
template <typename Iterable> template <typename Iterable>
@ -168,6 +175,7 @@ namespace EC
EntitiesType *entities; EntitiesType *entities;
Iterable *iterable; Iterable *iterable;
void *userData; void *userData;
std::unordered_set<std::size_t> dead;
}; };
// end section for "temporary" structures }}} // end section for "temporary" structures }}}
@ -750,12 +758,17 @@ namespace EC
fnDataAr[i].entities = &entities; fnDataAr[i].entities = &entities;
fnDataAr[i].signature = &signatureBitset; fnDataAr[i].signature = &signatureBitset;
fnDataAr[i].userData = userData; fnDataAr[i].userData = userData;
for(std::size_t j = begin; j < end; ++j) {
if(!isAlive(j)) {
fnDataAr[i].dead.insert(j);
}
}
threadPool->queueFn([&function] (void *ud) { threadPool->queueFn([&function] (void *ud) {
auto *data = static_cast<TPFnDataStructZero*>(ud); auto *data = static_cast<TPFnDataStructZero*>(ud);
for(std::size_t i = data->range[0]; i < data->range[1]; for(std::size_t i = data->range[0]; i < data->range[1];
++i) { ++i) {
if(!data->manager->isAlive(i)) { if(data->dead.find(i) != data->dead.end()) {
continue; continue;
} }
@ -868,11 +881,16 @@ namespace EC
fnDataAr[i].signature = &signatureBitset; fnDataAr[i].signature = &signatureBitset;
fnDataAr[i].userData = userData; fnDataAr[i].userData = userData;
fnDataAr[i].fn = function; fnDataAr[i].fn = function;
for(std::size_t j = begin; j < end; ++j) {
if(!isAlive(j)) {
fnDataAr[i].dead.insert(j);
}
}
threadPool->queueFn([] (void *ud) { threadPool->queueFn([] (void *ud) {
auto *data = static_cast<TPFnDataStructOne<Function>*>(ud); auto *data = static_cast<TPFnDataStructOne<Function>*>(ud);
for(std::size_t i = data->range[0]; i < data->range[1]; for(std::size_t i = data->range[0]; i < data->range[1];
++i) { ++i) {
if(!data->manager->isAlive(i)) { if(data->dead.find(i) != data->dead.end()) {
continue; continue;
} }
@ -1013,13 +1031,17 @@ namespace EC
fnDataAr[i].entities = &entities; fnDataAr[i].entities = &entities;
fnDataAr[i].userData = userData; fnDataAr[i].userData = userData;
fnDataAr[i].matching = &matching; fnDataAr[i].matching = &matching;
for(std::size_t j = begin; j < end; ++j) {
if(!isAlive(matching.at(j))) {
fnDataAr[i].dead.insert(j);
}
}
threadPool->queueFn([&function, helper] (void* ud) { threadPool->queueFn([&function, helper] (void* ud) {
auto *data = static_cast<TPFnDataStructTwo*>(ud); auto *data = static_cast<TPFnDataStructTwo*>(ud);
for(std::size_t i = data->range[0]; for(std::size_t i = data->range[0];
i < data->range[1]; i < data->range[1];
++i) { ++i) {
if(data->manager->isAlive( if(data->dead.find(i) == data->dead.end()) {
data->matching->at(i))) {
helper.callInstancePtr( helper.callInstancePtr(
data->matching->at(i), data->matching->at(i),
*data->manager, *data->manager,
@ -1083,11 +1105,16 @@ namespace EC
fnDataAr[i].bitsets = &bitsets; fnDataAr[i].bitsets = &bitsets;
fnDataAr[i].entities = &entities; fnDataAr[i].entities = &entities;
fnDataAr[i].mutex = &mutex; fnDataAr[i].mutex = &mutex;
for(std::size_t j = begin; j < end; ++j) {
if(!isAlive(j)) {
fnDataAr[i].dead.insert(j);
}
}
threadPool->queueFn([] (void *ud) { threadPool->queueFn([] (void *ud) {
auto *data = static_cast<TPFnDataStructThree*>(ud); auto *data = static_cast<TPFnDataStructThree*>(ud);
for(std::size_t i = data->range[0]; i < data->range[1]; for(std::size_t i = data->range[0]; i < data->range[1];
++i) { ++i) {
if(!data->manager->isAlive(i)) { if(data->dead.find(i) != data->dead.end()) {
continue; continue;
} }
for(std::size_t j = 0; j < data->bitsets->size(); for(std::size_t j = 0; j < data->bitsets->size();
@ -1455,12 +1482,17 @@ namespace EC
fnDataAr[i].multiMatchingEntities = &multiMatchingEntities; fnDataAr[i].multiMatchingEntities = &multiMatchingEntities;
fnDataAr[i].signatures = signatureBitsets; fnDataAr[i].signatures = signatureBitsets;
fnDataAr[i].mutex = &mutex; fnDataAr[i].mutex = &mutex;
for(std::size_t j = begin; j < end; ++j) {
if(!isAlive(j)) {
fnDataAr[i].dead.insert(j);
}
}
threadPool->queueFn([] (void *ud) { threadPool->queueFn([] (void *ud) {
auto *data = static_cast<TPFnDataStructFour*>(ud); auto *data = static_cast<TPFnDataStructFour*>(ud);
for(std::size_t i = data->range[0]; i < data->range[1]; for(std::size_t i = data->range[0]; i < data->range[1];
++i) { ++i) {
if(!data->manager->isAlive(i)) { if(data->dead.find(i) != data->dead.end()) {
continue; continue;
} }
for(std::size_t j = 0; j < SigList::size; ++j) { for(std::size_t j = 0; j < SigList::size; ++j) {
@ -1522,13 +1554,16 @@ namespace EC
fnDataAr[i].userData = userData; fnDataAr[i].userData = userData;
fnDataAr[i].multiMatchingEntities = fnDataAr[i].multiMatchingEntities =
&multiMatchingEntities; &multiMatchingEntities;
for(std::size_t j = begin; j < end; ++j) {
if(!isAlive(multiMatchingEntities.at(index).at(j))) {
fnDataAr[i].dead.insert(j);
}
}
threadPool->queueFn([&func] (void *ud) { threadPool->queueFn([&func] (void *ud) {
auto *data = static_cast<TPFnDataStructFive*>(ud); auto *data = static_cast<TPFnDataStructFive*>(ud);
for(std::size_t i = data->range[0]; for(std::size_t i = data->range[0];
i < data->range[1]; ++i) { i < data->range[1]; ++i) {
if(data->manager->isAlive( if(data->dead.find(i) == data->dead.end()) {
data->multiMatchingEntities
->at(data->index).at(i))) {
Helper::call( Helper::call(
data->multiMatchingEntities data->multiMatchingEntities
->at(data->index).at(i), ->at(data->index).at(i),
@ -1655,12 +1690,17 @@ namespace EC
fnDataAr[i].multiMatchingEntities = &multiMatchingEntities; fnDataAr[i].multiMatchingEntities = &multiMatchingEntities;
fnDataAr[i].bitsets = signatureBitsets; fnDataAr[i].bitsets = signatureBitsets;
fnDataAr[i].mutex = &mutex; fnDataAr[i].mutex = &mutex;
for(std::size_t j = begin; j < end; ++j) {
if(!isAlive(j)) {
fnDataAr[i].dead.insert(j);
}
}
threadPool->queueFn([] (void *ud) { threadPool->queueFn([] (void *ud) {
auto *data = static_cast<TPFnDataStructSix*>(ud); auto *data = static_cast<TPFnDataStructSix*>(ud);
for(std::size_t i = data->range[0]; i < data->range[1]; for(std::size_t i = data->range[0]; i < data->range[1];
++i) { ++i) {
if(!data->manager->isAlive(i)) { if(data->dead.find(i) != data->dead.end()) {
continue; continue;
} }
for(std::size_t j = 0; j < SigList::size; ++j) { for(std::size_t j = 0; j < SigList::size; ++j) {
@ -1727,13 +1767,16 @@ namespace EC
fnDataAr[i].userData = userData; fnDataAr[i].userData = userData;
fnDataAr[i].multiMatchingEntities = fnDataAr[i].multiMatchingEntities =
&multiMatchingEntities; &multiMatchingEntities;
for(std::size_t j = begin; j < end; ++j) {
if(!isAlive(multiMatchingEntities.at(index).at(j))) {
fnDataAr[i].dead.insert(j);
}
}
threadPool->queueFn([&func] (void *ud) { threadPool->queueFn([&func] (void *ud) {
auto *data = static_cast<TPFnDataStructFive*>(ud); auto *data = static_cast<TPFnDataStructFive*>(ud);
for(std::size_t i = data->range[0]; for(std::size_t i = data->range[0];
i < data->range[1]; ++i) { i < data->range[1]; ++i) {
if(data->manager->isAlive( if(data->dead.find(i) == data->dead.end()) {
data->multiMatchingEntities
->at(data->index).at(i))) {
Helper::callPtr( Helper::callPtr(
data->multiMatchingEntities data->multiMatchingEntities
->at(data->index).at(i), ->at(data->index).at(i),
@ -1807,13 +1850,18 @@ namespace EC
fnDataAr[i].entities = &entities; fnDataAr[i].entities = &entities;
fnDataAr[i].signature = &signatureBitset; fnDataAr[i].signature = &signatureBitset;
fnDataAr[i].userData = userData; fnDataAr[i].userData = userData;
for(std::size_t j = begin; j < end; ++j) {
if(!isAlive(j)) {
fnDataAr[i].dead.insert(j);
}
}
threadPool->queueFn([&fn] (void *ud) { threadPool->queueFn([&fn] (void *ud) {
auto *data = static_cast<TPFnDataStructZero*>(ud); auto *data = static_cast<TPFnDataStructZero*>(ud);
for(std::size_t i = data->range[0]; i < data->range[1]; for(std::size_t i = data->range[0]; i < data->range[1];
++i) { ++i) {
if(!data->manager->isAlive(i)) { if(data->dead.find(i) != data->dead.end()) {
continue; continue;
} else if((*data->signature } else if((*data->signature
& std::get<BitsetType>( & std::get<BitsetType>(
data->entities->at(i))) data->entities->at(i)))
== *data->signature) { == *data->signature) {
@ -1889,12 +1937,17 @@ namespace EC
fnDataAr[i].entities = &entities; fnDataAr[i].entities = &entities;
fnDataAr[i].iterable = &iterable; fnDataAr[i].iterable = &iterable;
fnDataAr[i].userData = userData; fnDataAr[i].userData = userData;
for(std::size_t j = begin; j < end; ++j) {
if(!isAlive(j)) {
fnDataAr[i].dead.insert(j);
}
}
threadPool->queueFn([&fn] (void *ud) { threadPool->queueFn([&fn] (void *ud) {
auto *data = static_cast<TPFnDataStructSeven<Iterable>*>(ud); auto *data = static_cast<TPFnDataStructSeven<Iterable>*>(ud);
bool isValid; bool isValid;
for(std::size_t i = data->range[0]; i < data->range[1]; for(std::size_t i = data->range[0]; i < data->range[1];
++i) { ++i) {
if(!data->manager->isAlive(i)) { if(data->dead.find(i) != data->dead.end()) {
continue; continue;
} }
isValid = true; isValid = true;