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:
parent
f27c22675a
commit
e61d2724e6
1 changed files with 69 additions and 16 deletions
|
@ -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;
|
||||||
|
|
Loading…
Reference in a new issue