Add Semaphore class

A simple Semaphore class that makes it simpler and less
erroprone to use a semaphore.
This commit is contained in:
Johan Wikman
2017-04-23 11:37:38 +03:00
parent c5fd2bdb81
commit b0922576be
5 changed files with 491 additions and 0 deletions

View File

@ -32,6 +32,7 @@ add_library(maxscale-common SHARED
resultset.cc
router.cc
secrets.cc
semaphore.cc
server.cc
service.cc
session.cc

46
server/core/semaphore.cc Normal file
View File

@ -0,0 +1,46 @@
/*
* 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: 2019-07-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/semaphore.hh>
#include <time.h>
namespace maxscale
{
bool Semaphore::timedwait(time_t seconds,
long nseconds,
signal_approach_t signal_approach) const
{
ss_dassert(nseconds <= 999999999);
timespec ts;
ss_debug(int rc=) clock_gettime(CLOCK_REALTIME, &ts);
ss_dassert(rc == 0);
ts.tv_sec += seconds;
uint64_t nseconds_sum = ts.tv_nsec + nseconds;
if (nseconds_sum > 1000000000)
{
ts.tv_sec += 1;
nseconds_sum -= 1000000000;
}
ts.tv_nsec = nseconds_sum;
return timedwait(ts, signal_approach);
}
}

View File

@ -10,6 +10,7 @@ add_executable(test_logthrottling testlogthrottling.cc)
add_executable(test_modutil testmodutil.c)
add_executable(test_poll testpoll.c)
add_executable(test_queuemanager testqueuemanager.c)
add_executable(test_semaphore testsemaphore.cc)
add_executable(test_server testserver.c)
add_executable(test_service testservice.c)
add_executable(test_spinlock testspinlock.c)
@ -33,6 +34,7 @@ target_link_libraries(test_logthrottling maxscale-common)
target_link_libraries(test_modutil maxscale-common)
target_link_libraries(test_poll maxscale-common)
target_link_libraries(test_queuemanager maxscale-common)
target_link_libraries(test_semaphore maxscale-common)
target_link_libraries(test_server maxscale-common)
target_link_libraries(test_service maxscale-common)
target_link_libraries(test_spinlock maxscale-common)
@ -58,6 +60,7 @@ add_test(TestModutil test_modutil)
add_test(NAME TestMaxPasswd COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/testmaxpasswd.sh)
add_test(TestPoll test_poll)
add_test(TestQueueManager test_queuemanager)
add_test(TestSemaphore test_semaphore)
add_test(TestServer test_server)
add_test(TestService test_service)
add_test(TestSpinlock test_spinlock)

View File

@ -0,0 +1,197 @@
/*
* 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: 2019-07-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.
*/
#if !defined(SS_DEBUG)
#define SS_DEBUG
#endif
#if defined(NDEBUG)
#undef NDEBUG
#endif
#include <maxscale/cppdefs.hh>
#include <time.h>
#include <iostream>
#include <pthread.h>
#include <signal.h>
#include <maxscale/semaphore.hh>
using namespace maxscale;
using namespace std;
namespace
{
void test_simple()
{
bool rv;
Semaphore sem1(1);
cout << "Waiting for semaphore with a count of 1." << endl;
rv = sem1.wait();
ss_dassert(rv);
cout << "Waited" << endl;
Semaphore sem2(3);
cout << "Waiting 3 times for semaphore with a count of 3." << endl;
rv = sem2.wait();
ss_dassert(rv);
rv = sem2.wait();
ss_dassert(rv);
rv = sem2.wait();
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();
ss_dassert(rv);
rv = sem2.wait();
ss_dassert(rv);
rv = sem2.wait();
ss_dassert(rv);
cout << "Waited" << endl;
Semaphore sem3;
time_t started;
time_t finished;
time_t diff;
cout << "Waiting 3 seconds for semaphore with a count of 0..." << endl;
started = time(NULL);
rv = sem3.timedwait(4);
finished = time(NULL);
diff = finished - started;
ss_dassert(!rv);
ss_dassert((diff >= 2) && (diff <= 4));
cout << "Waited." << endl;
cout << "Waiting 1 second for semaphore with a count of 0..." << endl;
started = time(NULL);
rv = sem3.timedwait(0, 999999999);
finished = time(NULL);
diff = finished - started;
ss_dassert(!rv);
ss_dassert((diff >= 0) && (diff <= 2));
cout << "Waited." << endl;
}
void* thread_main(void* pArg)
{
Semaphore* pSem = static_cast<Semaphore*>(pArg);
cout << "Hello from thread" << endl;
sleep(1);
pSem->post();
return 0;
}
void test_threads()
{
const int n_threads = 10;
pthread_t threads[n_threads];
Semaphore sem;
cout << "Starting threads." << endl;
for (int i = 0; i < n_threads; ++i)
{
int rc = pthread_create(&threads[i], NULL, thread_main, &sem);
ss_dassert(rc == 0);
}
cout << "Waiting for threads." << endl;
for (int i = 0; i < n_threads; ++i)
{
sem.wait();
}
cout << "Joining threads." << endl;
for (int i = 0; i < n_threads; ++i)
{
pthread_join(threads[i], NULL);
}
cout << "Joined." << endl;
}
void* send_signal(void*)
{
cout << "Sleeping 2 seconds." << endl;
sleep(2);
cout << "Sending signal" << endl;
kill(getpid(), SIGTERM);
cout << "Sent signal" << endl;
return NULL;
}
void sighandler(int s)
{
}
void test_signal()
{
Semaphore sem;
signal(SIGTERM, sighandler);
pthread_t thread;
int rc;
rc = pthread_create(&thread, NULL, send_signal, NULL);
ss_dassert(rc == 0);
bool waited;
cout << "Waiting" << endl;
waited = sem.timedwait(4, Semaphore::HONOUR_SIGNALS);
cout << "Waited" << endl;
// Should return false and errno should be EINTR.
ss_dassert(!waited && (errno == EINTR));
pthread_join(thread, NULL);
rc = pthread_create(&thread, NULL, send_signal, NULL);
ss_dassert(rc == 0);
cout << "Waiting" << endl;
waited = sem.timedwait(4, Semaphore::IGNORE_SIGNALS);
cout << "Waited" << endl;
// Should return false and errno should be ETIMEDOUT.
ss_dassert(!waited && (errno == ETIMEDOUT));
pthread_join(thread, NULL);
}
}
int main(int argc, char* argv[])
{
test_simple();
test_threads();
test_signal();
return EXIT_SUCCESS;
}