MaxScale/include/maxscale/semaphore.hh
2017-06-01 10:24:20 +03:00

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;
};
}