From 7b512958fdc0e065d840ec04525a1ba2365e1c99 Mon Sep 17 00:00:00 2001 From: Stephen Seo Date: Wed, 8 Sep 2021 17:42:43 +0900 Subject: [PATCH 1/4] Attempt to use github actions for Doxygen docs --- .github/workflows/gh-pages.yml | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 .github/workflows/gh-pages.yml diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml new file mode 100644 index 0000000..7f08984 --- /dev/null +++ b/.github/workflows/gh-pages.yml @@ -0,0 +1,26 @@ +name: GitHub Pages Generated Doxygen Docs + +on: + push: + branches: + - master + +jobs: + build-deploy-doxygen-docs: + runs-on: ubuntu-latest + concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 1 + + - name: Generate Doxygen Documentation + uses: mattnotmitt/doxygen-action@v1 + + - name: Deploy + uses: peaceiris/actions-gh-pages@v3 + if: ${{ github.ref == 'refs/heads/master' }} + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: ./doxygen_html/html From 51bea5a40f43d60f91284e763f7208b70efb0a8a Mon Sep 17 00:00:00 2001 From: Stephen Seo Date: Wed, 8 Sep 2021 18:06:24 +0900 Subject: [PATCH 2/4] Update Doxygen and README.md --- Doxyfile | 4 ++-- README.md | 4 ++++ doxygen/mainpage.dox | 7 +++++++ 3 files changed, 13 insertions(+), 2 deletions(-) create mode 100644 doxygen/mainpage.dox diff --git a/Doxyfile b/Doxyfile index 12b1307..efbdb59 100644 --- a/Doxyfile +++ b/Doxyfile @@ -771,7 +771,7 @@ WARN_LOGFILE = # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING # Note: If this tag is empty the current directory is searched. -INPUT = ./src/EC/Meta ./src/EC +INPUT = ./src/EC/Meta ./src/EC ./doxygen/mainpage.dox ./README.md # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses @@ -796,7 +796,7 @@ INPUT_ENCODING = UTF-8 # *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f, *.for, *.tcl, # *.vhd, *.vhdl, *.ucf, *.qsf, *.as and *.js. -FILE_PATTERNS = *.hpp +FILE_PATTERNS = *.hpp *.md # The RECURSIVE tag can be used to specify whether or not subdirectories should # be searched for input files as well. diff --git a/README.md b/README.md index 08f9fbe..9d8fe13 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,10 @@ built and run using cmake (gtest is a dependency). [(Note that gtest uses the BSD 3-Clause License.)](https://github.com/google/googletest/blob/master/LICENSE) +# Generated Doxygen Documentation + +[Check this repository's gh-pages documentation on ECMS](https://stephen-seo.github.io/EntityComponentMetaSystem/) + # Compiling the UnitTests Create a build directory. diff --git a/doxygen/mainpage.dox b/doxygen/mainpage.dox new file mode 100644 index 0000000..45979f4 --- /dev/null +++ b/doxygen/mainpage.dox @@ -0,0 +1,7 @@ +/*! + \mainpage EntityComponentMetaSystem Index Page + + \ref md_README + + Classes + */ From e0a18900e47a8a8370b5e2b24073825b1e77d416 Mon Sep 17 00:00:00 2001 From: Stephen Seo Date: Thu, 9 Sep 2021 13:13:55 +0900 Subject: [PATCH 3/4] Update doxygen main page to link to EC::Manager --- doxygen/mainpage.dox | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doxygen/mainpage.dox b/doxygen/mainpage.dox index 45979f4..7c1419e 100644 --- a/doxygen/mainpage.dox +++ b/doxygen/mainpage.dox @@ -4,4 +4,6 @@ \ref md_README Classes + + \ref EC::Manager "The Manager class that manages an Entity Component System" */ From f27c22675a28eae6d6b6be2a60c71b67c8a4a50a Mon Sep 17 00:00:00 2001 From: Stephen Seo Date: Thu, 9 Sep 2021 15:53:55 +0900 Subject: [PATCH 4/4] Add easyWakeAndWait() to ThreadPool Changed usage of ThreadPool in EC::Manager to use easyWakeAndWait(). --- src/EC/Manager.hpp | 63 ++++++------------------------------- src/EC/ThreadPool.hpp | 60 ++++++++++++++++++++++++----------- src/test/ThreadPoolTest.cpp | 27 ++++++++++++++++ 3 files changed, 79 insertions(+), 71 deletions(-) diff --git a/src/EC/Manager.hpp b/src/EC/Manager.hpp index 6a5333b..89e11c3 100644 --- a/src/EC/Manager.hpp +++ b/src/EC/Manager.hpp @@ -771,11 +771,7 @@ namespace EC } }, &fnDataAr[i]); } - threadPool->wakeThreads(); - do { - std::this_thread::sleep_for(std::chrono::microseconds(200)); - } while(!threadPool->isQueueEmpty() - || !threadPool->isAllThreadsWaiting()); + threadPool->easyWakeAndWait(); } } @@ -892,11 +888,7 @@ namespace EC } }, &fnDataAr[i]); } - threadPool->wakeThreads(); - do { - std::this_thread::sleep_for(std::chrono::microseconds(200)); - } while(!threadPool->isQueueEmpty() - || !threadPool->isAllThreadsWaiting()); + threadPool->easyWakeAndWait(); } } @@ -1037,12 +1029,7 @@ namespace EC } }, &fnDataAr[i]); } - threadPool->wakeThreads(); - do { - std::this_thread::sleep_for( - std::chrono::microseconds(200)); - } while(!threadPool->isQueueEmpty() - || !threadPool->isAllThreadsWaiting()); + threadPool->easyWakeAndWait(); } }))); @@ -1117,11 +1104,7 @@ namespace EC } }, &fnDataAr[i]); } - threadPool->wakeThreads(); - do { - std::this_thread::sleep_for(std::chrono::microseconds(200)); - } while(!threadPool->isQueueEmpty() - || !threadPool->isAllThreadsWaiting()); + threadPool->easyWakeAndWait(); } return matchingV; @@ -1494,11 +1477,7 @@ namespace EC } }, &fnDataAr[i]); } - threadPool->wakeThreads(); - do { - std::this_thread::sleep_for(std::chrono::microseconds(200)); - } while(!threadPool->isQueueEmpty() - || !threadPool->isAllThreadsWaiting()); + threadPool->easyWakeAndWait(); } // call functions on matching entities @@ -1560,12 +1539,7 @@ namespace EC } }, &fnDataAr[i]); } - threadPool->wakeThreads(); - do { - std::this_thread::sleep_for( - std::chrono::microseconds(200)); - } while(!threadPool->isQueueEmpty() - || !threadPool->isAllThreadsWaiting()); + threadPool->easyWakeAndWait(); } } ); @@ -1703,11 +1677,7 @@ namespace EC } }, &fnDataAr[i]); } - threadPool->wakeThreads(); - do { - std::this_thread::sleep_for(std::chrono::microseconds(200)); - } while(!threadPool->isQueueEmpty() - || !threadPool->isAllThreadsWaiting()); + threadPool->easyWakeAndWait(); } // call functions on matching entities @@ -1774,12 +1744,7 @@ namespace EC } }, &fnDataAr[i]); } - threadPool->wakeThreads(); - do { - std::this_thread::sleep_for( - std::chrono::microseconds(200)); - } while(!threadPool->isQueueEmpty() - || !threadPool->isAllThreadsWaiting()); + threadPool->easyWakeAndWait(); } } ); @@ -1857,11 +1822,7 @@ namespace EC } }, &fnDataAr[i]); } - threadPool->wakeThreads(); - do { - std::this_thread::sleep_for(std::chrono::microseconds(200)); - } while(!threadPool->isQueueEmpty() - || !threadPool->isAllThreadsWaiting()); + threadPool->easyWakeAndWait(); } } @@ -1950,11 +1911,7 @@ namespace EC } }, &fnDataAr[i]); } - threadPool->wakeThreads(); - do { - std::this_thread::sleep_for(std::chrono::microseconds(200)); - } while(!threadPool->isQueueEmpty() - || !threadPool->isAllThreadsWaiting()); + threadPool->easyWakeAndWait(); } } }; diff --git a/src/EC/ThreadPool.hpp b/src/EC/ThreadPool.hpp index 3b68c6b..e0dd21f 100644 --- a/src/EC/ThreadPool.hpp +++ b/src/EC/ThreadPool.hpp @@ -117,24 +117,7 @@ public: cv.notify_one(); } } else { - // pull functions from queue and run them on main thread - Internal::TPTupleType fnTuple; - bool hasFn; - do { - { - std::lock_guard lock(queueMutex); - if(!fnQueue.empty()) { - hasFn = true; - fnTuple = fnQueue.front(); - fnQueue.pop(); - } else { - hasFn = false; - } - } - if(hasFn) { - std::get<0>(fnTuple)(std::get<1>(fnTuple)); - } - } while(hasFn); + sequentiallyRunTasks(); } } @@ -179,6 +162,26 @@ public: return SIZE; } + /*! + \brief Wakes all threads and blocks until all queued tasks are finished. + + If SIZE is less than 2, then this function call will block until all the + queued functions have been executed on the calling thread. + + If SIZE is 2 or greater, then this function will block until all the + queued functions have been executed by the threads in the thread pool. + */ + void easyWakeAndWait() { + if(SIZE >= 2) { + wakeThreads(); + do { + std::this_thread::sleep_for(std::chrono::microseconds(150)); + } while(!isQueueEmpty() || !isAllThreadsWaiting()); + } else { + sequentiallyRunTasks(); + } + } + private: std::vector threads; std::atomic_bool isAlive; @@ -189,6 +192,27 @@ private: int waitCount; std::mutex waitCountMutex; + void sequentiallyRunTasks() { + // pull functions from queue and run them on current thread + Internal::TPTupleType fnTuple; + bool hasFn; + do { + { + std::lock_guard lock(queueMutex); + if(!fnQueue.empty()) { + hasFn = true; + fnTuple = fnQueue.front(); + fnQueue.pop(); + } else { + hasFn = false; + } + } + if(hasFn) { + std::get<0>(fnTuple)(std::get<1>(fnTuple)); + } + } while(hasFn); + } + }; } // namespace EC diff --git a/src/test/ThreadPoolTest.cpp b/src/test/ThreadPoolTest.cpp index 5a1a51d..9ebcbe1 100644 --- a/src/test/ThreadPoolTest.cpp +++ b/src/test/ThreadPoolTest.cpp @@ -77,3 +77,30 @@ TEST(ECThreadPool, QueryCount) { ASSERT_EQ(3, threeP.getThreadCount()); } } + +TEST(ECThreadPool, easyWakeAndWait) { + std::atomic_int data; + data.store(0); + { + OneThreadPool oneP; + for(unsigned int i = 0; i < 20; ++i) { + oneP.queueFn([] (void *ud) { + auto *atomicInt = static_cast(ud); + atomicInt->fetch_add(1); + }, &data); + } + oneP.easyWakeAndWait(); + EXPECT_EQ(20, data.load()); + } + { + ThreeThreadPool threeP; + for(unsigned int i = 0; i < 20; ++i) { + threeP.queueFn([] (void *ud) { + auto *atomicInt = static_cast(ud); + atomicInt->fetch_add(1); + }, &data); + } + threeP.easyWakeAndWait(); + EXPECT_EQ(40, data.load()); + } +}