]> git.seodisparate.com - EntityComponentMetaSystem/commitdiff
Fix possible data-race with isAlive() calls
authorStephen Seo <seo.disparate@gmail.com>
Thu, 20 Jan 2022 05:31:23 +0000 (14:31 +0900)
committerStephen Seo <seo.disparate@gmail.com>
Thu, 20 Jan 2022 05:31:23 +0000 (14:31 +0900)
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.)

src/EC/Manager.hpp

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