From 1eb409a6662047faba9d292b939905e2eee80211 Mon Sep 17 00:00:00 2001 From: Johan Wikman Date: Mon, 24 Apr 2017 15:38:08 +0300 Subject: [PATCH] Add possibility to wait several times on a semaphore --- include/maxscale/semaphore.hh | 143 +++++++++++++++++++++++++++++- server/core/semaphore.cc | 11 ++- server/core/test/testsemaphore.cc | 13 ++- 3 files changed, 155 insertions(+), 12 deletions(-) diff --git a/include/maxscale/semaphore.hh b/include/maxscale/semaphore.hh index 8c6b55ff4..c24a7125a 100644 --- a/include/maxscale/semaphore.hh +++ b/include/maxscale/semaphore.hh @@ -95,7 +95,7 @@ public: /** * @brief Waits on the semaphore. * - * If the semaphore count is greater that zero, decrements the count and + * If the semaphore count is greater than zero, decrements the count and * returns immediately. Otherwise blocks the caller until someone posts * the semaphore. * @@ -120,6 +120,39 @@ public: return rc == 0; } + /** + * @brief Waits multiple times on the semaphore. + * + * If the semaphore count is greater than or equal to the specified amount, + * decrements the count and returns immediately. Otherwise blocks the caller + * until the semaphore has been posted the required number of times. + * + * @param n_wait How many times should be waited. + * @param signal_approach Whether signals should be ignored or honoured. + * + * @return How many times the semaphore has been waited on. + * + * @attention The function can return a different number than `n_wait` only + * if `signal_approach` is `HONOUR_SIGNALS`. + */ + size_t wait_n(size_t n_wait, + signal_approach_t signal_approach = IGNORE_SIGNALS) const + { + bool waited = true; + size_t n_waited = 0; + + while (waited && n_wait--) + { + waited = wait(signal_approach); + if (waited) + { + ++n_waited; + } + } + + return n_waited; + } + /** * @brief Waits on the semaphore. * @@ -190,6 +223,46 @@ public: return rc == 0; } + /** + * @brief Waits on the semaphore. + * + * Waits on the sempahore the specified number of times, at most until the + * specified time. + * + * @param n_wait How many times should be waited. + * @param ts The *absolute* time until which the waiting at + * most is performed. + * @param signal_approach Whether signals should be ignored or honoured. + * + * @return How many times the semaphore has been waited on. If the + * function times out or is interrupted, then the returned + * value will be less than `n_wait`. + * + * @attention If the function returns a value less than `n_count` and + * `signal_approch` is `HONOUR_SIGNALS` then the caller must check + * the value of `errno` to find out whether the call was timed out or + * interrupted. In the former case the value will be `ETIMEDOUT` + * and in the latter `EINTR. + */ + size_t timedwait_n(size_t n_wait, + struct timespec& ts, + signal_approach_t signal_approach = IGNORE_SIGNALS) const + { + bool waited = true; + size_t n_waited = 0; + + while (waited && n_wait--) + { + waited = timedwait(ts, signal_approach); + if (waited) + { + ++n_waited; + } + } + + return n_waited; + } + /** * @brief Waits on the semaphore. * @@ -212,7 +285,43 @@ public: */ bool timedwait(time_t seconds, long nseconds, - signal_approach_t signal_approach = IGNORE_SIGNALS) const; + signal_approach_t signal_approach = IGNORE_SIGNALS) const + { + timespec ts; + get_current_timespec(seconds, nseconds, &ts); + return timedwait(ts, signal_approach); + } + + /** + * @brief Waits on the semaphore. + * + * Waits on the sempahore the specified number of times at most until the + * specified time. + * + * @param n_wait How many times should be waited. + * @param seconds How many seconds to wait at most. + * @param nseconds How many nanonseconds to wait at most. + * @param signal_approach Whether signals should be ignored or honoured. + * + * @return How many times the semaphore has been waited on. If the + * function times out or is interrupted, then the returned + * value will be less than `n_wait`. + * + * @attention If the function returns a value less than `n_count` and + * `signal_approch` is `HONOUR_SIGNALS` then the caller must check + * the value of `errno` to find out whether the call was timed out or + * interrupted. In the former case the value will be `ETIMEDOUT` + * and in the latter `EINTR. + */ + size_t timedwait_n(size_t n_wait, + time_t seconds, + long nseconds, + signal_approach_t signal_approach = IGNORE_SIGNALS) const + { + timespec ts; + get_current_timespec(seconds, nseconds, &ts); + return timedwait_n(n_wait, ts, signal_approach); + } /** * @brief Waits on the semaphore. @@ -237,6 +346,36 @@ public: return timedwait(seconds, 0, signal_approach); } + /** + * @brief Waits on the semaphore. + * + * Waits on the sempahore the specified number of times at most until the + * specified time. + * + * @param n_wait How many times should be waited. + * @param seconds How many seconds to wait at most. + * @param signal_approach Whether signals should be ignored or honoured. + * + * @return How many times the semaphore has been waited on. If the + * function times out or is interrupted, then the returned + * value will be less than `n_wait`. + * + * @attention If the function returns a value less than `n_count` and + * `signal_approch` is `HONOUR_SIGNALS` then the caller must check + * the value of `errno` to find out whether the call was timed out or + * interrupted. In the former case the value will be `ETIMEDOUT` + * and in the latter `EINTR. + */ + bool timedwait_n(size_t n_wait, + time_t seconds, + signal_approach_t signal_approach = IGNORE_SIGNALS) const + { + return timedwait_n(n_wait, seconds, 0, signal_approach); + } + +private: + static void get_current_timespec(time_t seconds, long nseconds, timespec* pTs); + private: mutable sem_t m_sem; }; diff --git a/server/core/semaphore.cc b/server/core/semaphore.cc index 2d8471062..82c40e1e2 100644 --- a/server/core/semaphore.cc +++ b/server/core/semaphore.cc @@ -17,13 +17,14 @@ namespace maxscale { -bool Semaphore::timedwait(time_t seconds, - long nseconds, - signal_approach_t signal_approach) const +//static +void Semaphore::get_current_timespec(time_t seconds, + long nseconds, + timespec* pTs) { ss_dassert(nseconds <= 999999999); - timespec ts; + timespec& ts = *pTs; ss_debug(int rc=) clock_gettime(CLOCK_REALTIME, &ts); ss_dassert(rc == 0); @@ -39,8 +40,6 @@ bool Semaphore::timedwait(time_t seconds, } ts.tv_nsec = nseconds_sum; - - return timedwait(ts, signal_approach); } } diff --git a/server/core/test/testsemaphore.cc b/server/core/test/testsemaphore.cc index 8154e5ef5..f5e2b6c59 100644 --- a/server/core/test/testsemaphore.cc +++ b/server/core/test/testsemaphore.cc @@ -65,6 +65,14 @@ void test_simple() ss_dassert(rv); cout << "Waited" << endl; + sem2.post(); + sem2.post(); + sem2.post(); + + cout << "Waiting 3 times for semaphore with a count of 3." << endl; + rv = sem2.wait_n(3); + cout << "Waited" << endl; + Semaphore sem3; time_t started; @@ -118,10 +126,7 @@ void test_threads() cout << "Waiting for threads." << endl; - for (int i = 0; i < n_threads; ++i) - { - sem.wait(); - } + sem.wait_n(n_threads); cout << "Joining threads." << endl;