Update documentation

This commit is contained in:
Stephen Seo 2021-09-07 12:04:42 +09:00
parent 6a8902ad51
commit 183eab10b3
2 changed files with 96 additions and 27 deletions

View file

@ -49,12 +49,10 @@ namespace EC
Note that all components must have a default constructor. Note that all components must have a default constructor.
An optional third template parameter may be given, which is the size of An optional third template parameter may be given, which is the size of
the number of threads in the internal ThreadPool, and it must be at the number of threads in the internal ThreadPool, and should be at
least 2. If ThreadCount is 1 or less, the program will fail to compile. least 2. If ThreadCount is 1 or less, then the ThreadPool will not be
Note that using the internal ThreadPool is optional; several member created and it will never be used, even if the "true" parameter is given
functions of Manager accept a boolean indicating if the internal for functions that enable its usage.
ThreadPool is to be used. Always passing false for that value will
result in never using the ThreadPool.
Example: Example:
\code{.cpp} \code{.cpp}
@ -481,16 +479,16 @@ namespace EC
).template getTagBit<Tag>() = true; ).template getTagBit<Tag>() = true;
} }
/*! /*!
\brief Removes the given Tag from the given Entity. \brief Removes the given Tag from the given Entity.
If the Entity does not have the Tag given, nothing will change. If the Entity does not have the Tag given, nothing will change.
Example: Example:
\code{.cpp} \code{.cpp}
manager.removeTag<T0>(entityID); manager.removeTag<T0>(entityID);
\endcode \endcode
*/ */
template <typename Tag> template <typename Tag>
void removeTag(const std::size_t& entityID) void removeTag(const std::size_t& entityID)
{ {
@ -1319,6 +1317,14 @@ namespace EC
The second parameter (default nullptr) will be provided to every The second parameter (default nullptr) will be provided to every
function call as a void* (context). function call as a void* (context).
The third parameter is default false (not multi-threaded).
Otherwise, if true, then the thread pool will be used to call the
given function in parallel across all entities. Note that
multi-threading is based on splitting the task of calling the
function across sections of entities. Thus if there are only a small
amount of entities in the manager, then using multiple threads may
not have as great of a speed-up.
This function was created for the use case where there are many This function was created for the use case where there are many
entities in the system which can cause multiple calls to entities in the system which can cause multiple calls to
forMatchingSignature to be slow due to the overhead of iterating forMatchingSignature to be slow due to the overhead of iterating
@ -1513,6 +1519,14 @@ namespace EC
The second parameter (default nullptr) will be provided to every The second parameter (default nullptr) will be provided to every
function call as a void* (context). function call as a void* (context).
The third parameter is default false (not multi-threaded).
Otherwise, if true, then the thread pool will be used to call the
given function in parallel across all entities. Note that
multi-threading is based on splitting the task of calling the
function across sections of entities. Thus if there are only a small
amount of entities in the manager, then using multiple threads may
not have as great of a speed-up.
This function was created for the use case where there are many This function was created for the use case where there are many
entities in the system which can cause multiple calls to entities in the system which can cause multiple calls to
forMatchingSignature to be slow due to the overhead of iterating forMatchingSignature to be slow due to the overhead of iterating
@ -1687,12 +1701,21 @@ namespace EC
typedef void ForMatchingFn(std::size_t, Manager<ComponentsList, TagsList>*, void*); typedef void ForMatchingFn(std::size_t, Manager<ComponentsList, TagsList>*, void*);
/*! /*!
* \brief A simple version of forMatchingSignature() \brief A simple version of forMatchingSignature()
*
* This function behaves like forMatchingSignature(), but instead of This function behaves like forMatchingSignature(), but instead of
* providing a function with each requested component as a parameter, providing a function with each requested component as a parameter,
* the function receives a pointer to the manager itself, with which to the function receives a pointer to the manager itself, with which to
* query component/tag data. query component/tag data.
The third parameter can be optionally used to enable the use of the
internal ThreadPool to call the function in parallel. Using the
value false (which is the default) will not use the ThreadPool and
run the function sequentially on all entities on the main thread.
Note that multi-threading is based on splitting the task of calling
the functions across sections of entities. Thus if there are only a
small amount of entities in the manager, then using multiple threads
may not have as great of a speed-up.
*/ */
template <typename Signature> template <typename Signature>
void forMatchingSimple(ForMatchingFn fn, void *userData = nullptr, const bool useThreadPool = false) { void forMatchingSimple(ForMatchingFn fn, void *userData = nullptr, const bool useThreadPool = false) {
@ -1747,13 +1770,22 @@ namespace EC
} }
/*! /*!
* \brief Similar to forMatchingSimple(), but with a collection of Component/Tag indices \brief Similar to forMatchingSimple(), but with a collection of Component/Tag indices
*
* This function works like forMatchingSimple(), but instead of This function works like forMatchingSimple(), but instead of
* providing template types that filter out non-matching entities, an providing template types that filter out non-matching entities, an
* iterable of indices must be provided which correlate to matching iterable of indices must be provided which correlate to matching
* Component/Tag indices. The function given must match the previously Component/Tag indices. The function given must match the previously
* defined typedef of type ForMatchingFn. defined typedef of type ForMatchingFn.
The fourth parameter can be optionally used to enable the use of the
internal ThreadPool to call the function in parallel. Using the
value false (which is the default) will not use the ThreadPool and
run the function sequentially on all entities on the main thread.
Note that multi-threading is based on splitting the task of calling
the functions across sections of entities. Thus if there are only a
small amount of entities in the manager, then using multiple threads
may not have as great of a speed-up.
*/ */
template <typename Iterable> template <typename Iterable>
void forMatchingIterable(Iterable iterable, ForMatchingFn fn, void* userData = nullptr, const bool useThreadPool = false) { void forMatchingIterable(Iterable iterable, ForMatchingFn fn, void* userData = nullptr, const bool useThreadPool = false) {

View file

@ -20,6 +20,12 @@ namespace Internal {
using TPQueueType = std::queue<TPTupleType>; using TPQueueType = std::queue<TPTupleType>;
} // namespace Internal } // namespace Internal
/*!
\brief Implementation of a Thread Pool.
Note that if SIZE is less than 2, then ThreadPool will not create threads and
run queued functions on the calling thread.
*/
template <unsigned int SIZE> template <unsigned int SIZE>
class ThreadPool { class ThreadPool {
public: public:
@ -83,11 +89,27 @@ public:
} }
} }
/*!
\brief Queues a function to be called (doesn't start calling yet).
To run the queued functions, wakeThreads() must be called to wake the
waiting threads which will start pulling functions from the queue to be
called.
*/
void queueFn(std::function<void(void*)>&& fn, void *ud = nullptr) { void queueFn(std::function<void(void*)>&& fn, void *ud = nullptr) {
std::lock_guard<std::mutex> lock(queueMutex); std::lock_guard<std::mutex> lock(queueMutex);
fnQueue.emplace(std::make_tuple(fn, ud)); fnQueue.emplace(std::make_tuple(fn, ud));
} }
/*!
\brief Wakes waiting threads to start running queued functions.
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 return immediately after
waking one or all threads, depending on the given boolean parameter.
*/
void wakeThreads(bool wakeAll = true) { void wakeThreads(bool wakeAll = true) {
if(SIZE >= 2) { if(SIZE >= 2) {
// wake threads to pull functions from queue and run them // wake threads to pull functions from queue and run them
@ -118,11 +140,23 @@ public:
} }
} }
/*!
\brief Gets the number of waiting threads.
If all threads are waiting, this should equal ThreadCount.
If SIZE is less than 2, then this will always return 0.
*/
int getWaitCount() { int getWaitCount() {
std::lock_guard<std::mutex> lock(waitCountMutex); std::lock_guard<std::mutex> lock(waitCountMutex);
return waitCount; return waitCount;
} }
/*!
\brief Returns true if all threads are waiting.
If SIZE is less than 2, then this will always return true.
*/
bool isAllThreadsWaiting() { bool isAllThreadsWaiting() {
if(SIZE >= 2) { if(SIZE >= 2) {
std::lock_guard<std::mutex> lock(waitCountMutex); std::lock_guard<std::mutex> lock(waitCountMutex);
@ -132,6 +166,9 @@ public:
} }
} }
/*!
\brief Returns true if the function queue is empty.
*/
bool isQueueEmpty() { bool isQueueEmpty() {
std::lock_guard<std::mutex> lock(queueMutex); std::lock_guard<std::mutex> lock(queueMutex);
return fnQueue.empty(); return fnQueue.empty();