From 6b64e2016106bc104dda390ffb852e2d7b48e263 Mon Sep 17 00:00:00 2001 From: Michele Caini Date: Sat, 9 Mar 2019 23:44:02 +0100 Subject: [PATCH] doc: thread.hpp --- src/uvw/thread.hpp | 186 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 183 insertions(+), 3 deletions(-) diff --git a/src/uvw/thread.hpp b/src/uvw/thread.hpp index d41be8cc..59d9f1d0 100644 --- a/src/uvw/thread.hpp +++ b/src/uvw/thread.hpp @@ -36,6 +36,15 @@ class Condition; class Barrier; +/** + * @brief The Thread wrapper. + * + * To create a `Thread` through a `Loop`, arguments follow: + * + * * A callback invoked to initialize thread execution. The type must be such + * that it can be assigned to an `std::function)>`. + * * An optional payload the type of which is `std::shared_ptr`. + */ class Thread final: public UnderlyingType { using InternalTask = std::function)>; @@ -49,14 +58,24 @@ public: using Task = InternalTask; using Type = uv_thread_t; - explicit Thread(ConstructorAccess ca, std::shared_ptr ref, InternalTask t, std::shared_ptr d = nullptr) noexcept + explicit Thread(ConstructorAccess ca, std::shared_ptr ref, Task t, std::shared_ptr d = nullptr) noexcept : UnderlyingType{ca, std::move(ref)}, data{std::move(d)}, task{std::move(t)} {} + /** + * @brief Obtains the identifier of the calling thread. + * @return The identifier of the calling thread. + */ static Type self() noexcept { return uv_thread_self(); } + /** + * @brief Compares thread by means of their identifiers. + * @param tl A valid instance of a thread. + * @param tr A valid instance of a thread. + * @return True if the two threads are the same thread, false otherwise. + */ static bool equal(const Thread &tl, const Thread &tr) noexcept { return !(0 == uv_thread_equal(tl.get(), tr.get())); } @@ -65,15 +84,36 @@ public: join(); } + /** + * @brief Creates a new thread. + * @return True in case of success, false otherwise. + */ bool run() noexcept { return (0 == uv_thread_create(get(), &createCallback, this)); } + /** + * @brief Creates a new thread. + * + * Available flags are: + * + * * `Thread::Options::THREAD_NO_FLAGS`: no flags set. + * * `Thread::Options::THREAD_HAS_STACK_SIZE`: if set, `stack` specifies a + * stack size for the new thread. 0 indicates that the default value should + * be used (it behaves as if the flag was not set). Other values will be + * rounded up to the nearest page boundary. + * + * @return True in case of success, false otherwise. + */ bool run(Flags opts, std::size_t stack = {}) noexcept { uv_thread_options_t params{opts, stack}; return (0 == uv_thread_create_ex(get(), ¶ms, &createCallback, this)); } + /** + * @brief Joins with a terminated thread. + * @return True in case of success, false otherwise. + */ bool join() noexcept { return (0 == uv_thread_join(get())); } @@ -84,6 +124,13 @@ private: }; +/** + * @brief The ThreadLocalStorage wrapper. + * + * A storage area that can only be accessed by one thread. The variable can be + * seen as a global variable that is only visible to a particular thread and not + * the whole program. + */ class ThreadLocalStorage final: public UnderlyingType { public: explicit ThreadLocalStorage(ConstructorAccess ca, std::shared_ptr ref) noexcept @@ -96,11 +143,21 @@ public: uv_key_delete(UnderlyingType::get()); } + /** + * @brief Gets the value of a given variable. + * @tparam T Type to which to cast the opaque storage area. + * @return A pointer to the given variable. + */ template T* get() noexcept { return static_cast(uv_key_get(UnderlyingType::get())); } + /** + * @brief Sets the value of a given variable. + * @tparam T Type of the variable to store aside. + * @param value A valid pointer to the variable to store + */ template void set(T *value) noexcept { return uv_key_set(UnderlyingType::get(), value); @@ -108,7 +165,12 @@ public: }; -// `Once` is an odd one as it doesn't use a `libuv` structure per object. +/** + * @brief The Once wrapper. + * + * Runs a function once and only once. Concurrent calls to `once` will block all + * callers except one (it’s unspecified which one). + */ class Once final: public UnderlyingType { static uv_once_t* guard() noexcept { static uv_once_t once = UV_ONCE_INIT; @@ -118,9 +180,18 @@ class Once final: public UnderlyingType { public: using UnderlyingType::UnderlyingType; + /** + * @brief Runs a function once and only once. + * + * The callback must be such that it's convertible to `void(*)(void)`. Free + * functions and non-capturing lambdas are both viable solutions. + * + * @tparam F Type of the callback. + * @param f A valid callback function. + */ template static void once(F &&f) noexcept { - using CallbackType = void (*)(void); + using CallbackType = void(*)(void); static_assert(std::is_convertible::value, "!"); CallbackType cb = f; uv_once(guard(), cb); @@ -128,6 +199,14 @@ public: }; +/** + * @brief The Mutex wrapper. + * + * To create a `Mutex` through a `Loop`, arguments follow: + * + * * An option boolean that specifies if the mutex is a recursive one. The + * default value is false, the mutex isn't recursive. + */ class Mutex final: public UnderlyingType { friend class Condition; @@ -146,20 +225,33 @@ public: uv_mutex_destroy(get()); } + /** + * @brief Locks the mutex. + */ void lock() noexcept { uv_mutex_lock(get()); } + /** + * @brief Tries to lock the mutex. + * @return True in case of success, false otherwise. + */ bool tryLock() noexcept { return (0 == uv_mutex_trylock(get())); } + /** + * @brief Unlocks the mutex. + */ void unlock() noexcept { uv_mutex_unlock(get()); } }; +/** + * @brief The RWLock wrapper. + */ class RWLock final: public UnderlyingType { public: explicit RWLock(ConstructorAccess ca, std::shared_ptr ref) noexcept @@ -172,32 +264,59 @@ public: uv_rwlock_destroy(get()); } + /** + * @brief Locks a read-write lock object for reading. + */ void rdLock() noexcept { uv_rwlock_rdlock(get()); } + /** + * @brief Tries to lock a read-write lock object for reading. + * @return True in case of success, false otherwise. + */ bool tryRdLock() noexcept { return (0 == uv_rwlock_tryrdlock(get())); } + /** + * @brief Unlocks a read-write lock object previously locked for reading. + */ void rdUnlock() noexcept { uv_rwlock_rdunlock(get()); } + /** + * @brief Locks a read-write lock object for writing. + */ void wrLock() noexcept { uv_rwlock_wrlock(get()); } + /** + * @brief Tries to lock a read-write lock object for writing. + * @return True in case of success, false otherwise. + */ bool tryWrLock() noexcept { return (0 == uv_rwlock_trywrlock(get())); } + /** + * @brief Unlocks a read-write lock object previously locked for writing. + */ void wrUnlock() noexcept { uv_rwlock_wrunlock(get()); } }; +/** + * @brief The Semaphore wrapper. + * + * To create a `Semaphore` through a `Loop`, arguments follow: + * + * * An unsigned integer that specifies the initial value for the semaphore. + */ class Semaphore final: public UnderlyingType { public: explicit Semaphore(ConstructorAccess ca, std::shared_ptr ref, unsigned int value) noexcept @@ -210,20 +329,33 @@ public: uv_sem_destroy(get()); } + /** + * @brief Unlocks a semaphore. + */ void post() noexcept { uv_sem_post(get()); } + /** + * @brief Locks a semaphore. + */ void wait() noexcept { uv_sem_wait(get()); } + /** + * @brief Tries to lock a semaphore. + * @return True in case of success, false otherwise. + */ bool tryWait() noexcept { return (0 == uv_sem_trywait(get())); } }; +/** + * @brief The Condition wrapper. + */ class Condition final: public UnderlyingType { public: explicit Condition(ConstructorAccess ca, std::shared_ptr ref) noexcept @@ -236,24 +368,68 @@ public: uv_cond_destroy(get()); } + /** + * @brief Signals a condition. + * + * This function shall unblock at least one of the threads that are blocked + * on the specified condition variable (if any threads are blocked on it). + */ void signal() noexcept { uv_cond_signal(get()); } + /** + * @brief Broadcasts a condition. + * + * This function shall unblock threads blocked on a condition variable. + */ void broadcast() noexcept { uv_cond_broadcast(get()); } + /** + * @brief Waits on a condition. + * + * These function atomically releases the mutex and causes the calling + * thread to block on the condition variable. + * + * @param mutex A mutex locked by the calling thread, otherwise expect + * undefined behavior. + */ void wait(Mutex &mutex) noexcept { uv_cond_wait(get(), mutex.get()); } + /** + * @brief Waits on a condition. + * + * These function atomically releases the mutex and causes the calling + * thread to block on the condition variable.
+ * The functions returns with an error if the absolute time specified passes + * (that is, system time equals or exceeds it) before the condition is + * signaled or broadcasted, or if the absolute time specified has already + * been passed at the time of the call. + * + * @param mutex A mutex locked by the calling thread, otherwise expect + * undefined behavior. + * @param timeout The maximum time to wait before to return. + * @return True in case of success, false otherwise. + */ bool timedWait(Mutex &mutex, uint64_t timeout) noexcept { return (0 == uv_cond_timedwait(get(), mutex.get(), timeout)); } }; +/** + * @brief The Barrier wrapper. + * + * To create a `Barrier` through a `Loop`, arguments follow: + * + * * An unsigned integer that specifies the number of threads that must call + * `wait` before any of them successfully return from the call. The value + * specified must be greater than zero. + */ class Barrier final: public UnderlyingType { public: explicit Barrier(ConstructorAccess ca, std::shared_ptr ref, unsigned int count) noexcept @@ -266,6 +442,10 @@ public: uv_barrier_destroy(get()); } + /** + * @brief Synchronizes at a barrier. + * @return True in case of success, false otherwise. + */ bool wait() noexcept { return (0 == uv_barrier_wait(get())); }