384 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			384 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| #pragma once
 | |
| /*
 | |
|  * Copyright (c) 2016 MariaDB Corporation Ab
 | |
|  *
 | |
|  * Use of this software is governed by the Business Source License included
 | |
|  * in the LICENSE.TXT file and at www.mariadb.com/bsl11.
 | |
|  *
 | |
|  * Change Date: 2020-01-01
 | |
|  *
 | |
|  * On the date above, in accordance with the Business Source License, use
 | |
|  * of this software will be governed by version 2 or later of the General
 | |
|  * Public License.
 | |
|  */
 | |
| 
 | |
| #include <maxscale/cppdefs.hh>
 | |
| #include <errno.h>
 | |
| #include <maxscale/semaphore.h>
 | |
| #include <maxscale/debug.h>
 | |
| 
 | |
| namespace maxscale
 | |
| {
 | |
| 
 | |
| class Semaphore
 | |
| {
 | |
|     Semaphore(const Semaphore&);
 | |
|     Semaphore& operator = (const Semaphore&);
 | |
| 
 | |
| public:
 | |
|     enum signal_approach_t
 | |
|     {
 | |
|         HONOUR_SIGNALS, /* Honour signals and return when interrupted. */
 | |
|         IGNORE_SIGNALS  /* Ignore signals and re-issue the comment when signals occur. */
 | |
|     };
 | |
| 
 | |
|     /**
 | |
|      * @brief Constructor
 | |
|      *
 | |
|      * @param initial_count  The initial count of the semaphore.
 | |
|      *
 | |
|      * @attention If the value `initial_count` is larger than `SEM_VALUE_MAX`,
 | |
|      *            the the value will be adjusted down to `SEM_VALUE_MAX`.
 | |
|      */
 | |
|     Semaphore(uint32_t initial_count = 0)
 | |
|     {
 | |
|         if (initial_count > SEM_VALUE_MAX)
 | |
|         {
 | |
|             initial_count = SEM_VALUE_MAX;
 | |
|         }
 | |
| 
 | |
|         ss_debug(int rc =) sem_init(&m_sem, 0, initial_count);
 | |
|         ss_dassert(rc == 0);
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * @brief Destructor
 | |
|      *
 | |
|      * When the semaphore is destructed, its count should be 0 and nobody
 | |
|      * should be waiting on it.
 | |
|      */
 | |
|     ~Semaphore()
 | |
|     {
 | |
| #ifdef SS_DEBUG
 | |
|         int count;
 | |
|         int rc = sem_getvalue(&m_sem, &count);
 | |
|         ss_dassert(rc == 0);
 | |
|         ss_dassert(count == 0);
 | |
| #endif
 | |
|         ss_debug(rc =) sem_destroy(&m_sem);
 | |
|         ss_dassert(rc == 0);
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * @brief Post the semaphore.
 | |
|      *
 | |
|      * Increments the semaphore. If others threads were blocked in `wait`
 | |
|      * one of them will subsequently return.
 | |
|      *
 | |
|      * @return `True` if the semaphore could be posed, otherwise `false`.
 | |
|      *         If `false` is returned, then the maximum count of the sempahore
 | |
|      *         has been reached.
 | |
|      */
 | |
|     bool post() const
 | |
|     {
 | |
|         int rc = sem_post(&m_sem);
 | |
|         ss_dassert((rc == 0) || (errno == EOVERFLOW));
 | |
| #ifdef SS_DEBUG
 | |
|         if ((rc != 0) && (errno == EOVERFLOW))
 | |
|         {
 | |
|             ss_info_dassert(!true, "Semaphore overflow; indicates endless loop.");
 | |
|         }
 | |
| #endif
 | |
|         return rc == 0;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * @brief Waits on the semaphore.
 | |
|      *
 | |
|      * If the semaphore count is greater than zero, decrements the count and
 | |
|      * returns immediately. Otherwise blocks the caller until someone posts
 | |
|      * the semaphore.
 | |
|      *
 | |
|      * @param signal_approach  Whether signals should be ignored or honoured.
 | |
|      *
 | |
|      * @return `True` if the semaphore was waited for, `false` otherwise.
 | |
|      *
 | |
|      * @attention The function can return `false` only if `signal_approach`
 | |
|      *            is `HONOUR_SIGNALS`.
 | |
|      */
 | |
|     bool wait(signal_approach_t signal_approach = IGNORE_SIGNALS) const
 | |
|     {
 | |
|         int rc;
 | |
|         do
 | |
|         {
 | |
|             rc = sem_wait(&m_sem);
 | |
|         }
 | |
|         while ((rc != 0) && ((errno == EINTR) && (signal_approach == IGNORE_SIGNALS)));
 | |
| 
 | |
|         ss_dassert((rc == 0) || ((errno == EINTR) && (signal_approach == HONOUR_SIGNALS)));
 | |
| 
 | |
|         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.
 | |
|      *
 | |
|      * If the semaphore count is greater that zero, decrements the count and
 | |
|      * returns immediately.
 | |
|      *
 | |
|      * @param signal_approach  Whether signals should be ignored or honoured.
 | |
|      *
 | |
|      * @return `True` if the semaphore was waited for, `false` otherwise.
 | |
|      *
 | |
|      * @attention If the function returns `false` 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 `EAGAIN`
 | |
|      *            and in the latter `EINTR`.
 | |
|      */
 | |
|     bool trywait(signal_approach_t signal_approach = IGNORE_SIGNALS) const
 | |
|     {
 | |
|         errno = 0;
 | |
| 
 | |
|         int rc;
 | |
|         do
 | |
|         {
 | |
|             rc = sem_trywait(&m_sem);
 | |
|         }
 | |
|         while ((rc != 0) && ((errno == EINTR) && (signal_approach == IGNORE_SIGNALS)));
 | |
| 
 | |
|         ss_dassert((rc == 0) ||
 | |
|                    (errno == EAGAIN) ||
 | |
|                    ((errno == EINTR) && (signal_approach == HONOUR_SIGNALS)));
 | |
| 
 | |
|         return rc == 0;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * @brief Waits on the semaphore.
 | |
|      *
 | |
|      * Waits on the sempahore at most until the specified time.
 | |
|      *
 | |
|      * @param ts               The *absolute* time until which the waiting at
 | |
|      *                         most is performed.
 | |
|      * @param signal_approach  Whether signals should be ignored or honoured.
 | |
|      *
 | |
|      * @return True if the waiting could be performed, false otherwise.
 | |
|      *
 | |
|      * @attention If the function returns `false` 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(struct timespec& ts,
 | |
|                    signal_approach_t signal_approach = IGNORE_SIGNALS) const
 | |
|     {
 | |
|         errno = 0;
 | |
| 
 | |
|         int rc;
 | |
|         do
 | |
|         {
 | |
|             rc = sem_timedwait(&m_sem, &ts);
 | |
|         }
 | |
|         while ((rc != 0) && ((errno == EINTR) && (signal_approach == IGNORE_SIGNALS)));
 | |
| 
 | |
|         ss_dassert((rc == 0) ||
 | |
|                    (errno == ETIMEDOUT) ||
 | |
|                    ((errno == EINTR) && (signal_approach == HONOUR_SIGNALS)));
 | |
| 
 | |
|         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.
 | |
|      *
 | |
|      * Waits on the sempahore at most until the specified amount of time
 | |
|      * has passed.
 | |
|      *
 | |
|      * @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 True if the waiting could be performed, false otherwise.
 | |
|      *
 | |
|      * @attention `nseconds` must be less than 1000000000.
 | |
|      *
 | |
|      * @attention If the function returns `false` 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(time_t seconds,
 | |
|                    long nseconds,
 | |
|                    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.
 | |
|      *
 | |
|      * Waits on the sempahore at most until the specified amount of time
 | |
|      * has passed.
 | |
|      *
 | |
|      * @param seconds          How many seconds to wait at most.
 | |
|      * @param signal_approach  Whether signals should be ignored or honoured.
 | |
|      *
 | |
|      * @return True if the waiting could be performed, false otherwise.
 | |
|      *
 | |
|      * @attention If the function returns `false` 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(time_t seconds,
 | |
|                    signal_approach_t signal_approach = IGNORE_SIGNALS) const
 | |
|     {
 | |
|         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;
 | |
| };
 | |
| 
 | |
| }
 | 
