src/lib/util/python/gen_wiredata.py
src/lib/util/tests/Makefile
src/lib/util/tests/process_spawn_app.sh
- src/lib/util/threads/Makefile
- src/lib/util/threads/tests/Makefile
src/lib/util/unittests/Makefile
src/lib/yang/Makefile
src/lib/yang/pretests/Makefile
ca_unittests_LDADD += $(top_builddir)/src/lib/cryptolink/libkea-cryptolink.la
ca_unittests_LDADD += $(top_builddir)/src/lib/hooks/libkea-hooks.la
ca_unittests_LDADD += $(top_builddir)/src/lib/log/libkea-log.la
-ca_unittests_LDADD += $(top_builddir)/src/lib/util/threads/libkea-threads.la
ca_unittests_LDADD += $(top_builddir)/src/lib/testutils/libkea-testutils.la
ca_unittests_LDADD += $(top_builddir)/src/lib/util/libkea-util.la
ca_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la
#include <cc/command_interpreter.h>
#include <cc/data.h>
#include <process/testutils/d_test_stubs.h>
-#include <util/threads/thread.h>
#include <boost/bind.hpp>
#include <boost/pointer_cast.hpp>
#include <gtest/gtest.h>
#include <testutils/sandbox.h>
#include <cstdlib>
#include <vector>
+#include <thread>
using namespace isc::agent;
using namespace isc::asiolink;
// to this we need to run the server side socket at the same time as the
// client. Running IO service in a thread guarantees that the server
//responds as soon as it receives the control command.
- isc::util::thread::Thread th(boost::bind(&IOService::run,
- getIOService().get()));
+ std::thread th(boost::bind(&IOService::run, getIOService().get()));
// Wait for the IO service in thread to actually run.
getIOService()->stop();
// Wait for the thread to finish.
- th.wait();
+ th.join();
// Cancel all asynchronous operations on the server.
server_socket_->stopServer();
// to this we need to run the server side socket at the same time.
// Running IO service in a thread guarantees that the server responds
// as soon as it receives the control command.
- isc::util::thread::Thread th(boost::bind(&IOService::run, getIOService().get()));
+ std::thread th(boost::bind(&IOService::run, getIOService().get()));
// Wait for the IO service in thread to actually run.
server_socket_->waitForRunning();
getIOService()->stop();
// Wait for the thread to finish.
- th.wait();
+ th.join();
// Cancel all asynchronous operations on the server.
server_socket_->stopServer();
kea_netconf_LDADD += $(top_builddir)/src/lib/cc/libkea-cc.la
kea_netconf_LDADD += $(top_builddir)/src/lib/asiolink/libkea-asiolink.la
kea_netconf_LDADD += $(top_builddir)/src/lib/log/libkea-log.la
-kea_netconf_LDADD += $(top_builddir)/src/lib/util/threads/libkea-threads.la
kea_netconf_LDADD += $(top_builddir)/src/lib/util/libkea-util.la
kea_netconf_LDADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la
kea_netconf_LDADD += $(LOG4CPLUS_LIBS) $(CRYPTO_LIBS) $(BOOST_LIBS) $(SYSREPO_LIBS)
-// Copyright (C) 2018 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2018-2019 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
#include <asiolink/io_error.h>
#include <cc/command_interpreter.h>
#include <config/timeouts.h>
-#include <util/threads/thread.h>
#include <boost/pointer_cast.hpp>
+#include <thread>
using namespace isc::asiolink;
using namespace isc::config;
using namespace isc::data;
using namespace isc::http;
using namespace isc::process;
-using namespace isc::util::thread;
namespace isc {
namespace netconf {
try {
// Initialize netconf agent in a thread.
- Thread th([this]() {
+ std::thread th([this]() {
if (shouldShutdown()) {
return;
}
// Call init.
agent_.init(cfg_mgr);
- });
+ });
+
+ th.detach();
// Let's process incoming data or expiring timers in a loop until
// shutdown condition is detected.
runIO();
}
stopIOService();
+
} catch (const std::exception& ex) {
LOG_FATAL(netconf_logger, NETCONF_FAILED).arg(ex.what());
try {
netconf_unittests_LDADD += $(top_builddir)/src/lib/cryptolink/libkea-cryptolink.la
netconf_unittests_LDADD += $(top_builddir)/src/lib/log/libkea-log.la
netconf_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
-netconf_unittests_LDADD += $(top_builddir)/src/lib/util/threads/libkea-threads.la
netconf_unittests_LDADD += $(top_builddir)/src/lib/util/libkea-util.la
netconf_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la
netconf_unittests_LDADD += $(LOG4CPLUS_LIBS) $(CRYPTO_LIBS)
#include <http/tests/response_test.h>
#include <testutils/threaded_test.h>
#include <testutils/sandbox.h>
-#include <util/threads/thread.h>
-#include <util/threads/sync.h>
#include <gtest/gtest.h>
#include <sstream>
+#include <thread>
using namespace std;
using namespace isc;
using namespace isc::http;
using namespace isc::http::test;
using namespace isc::test;
-using namespace isc::util::thread;
namespace {
/// @brief Type definition for the pointer to Thread objects.
-typedef boost::shared_ptr<Thread> ThreadPtr;
+typedef boost::shared_ptr<thread> ThreadPtr;
//////////////////////////////// STDOUT ////////////////////////////////
/// @brief Destructor.
virtual ~UnixControlSocketTest() {
if (thread_) {
- thread_->wait();
+ thread_->join();
thread_.reset();
}
// io_service must be stopped after the thread returns,
ASSERT_TRUE(ucs);
// Run a reflecting server in a thread.
- thread_.reset(new Thread([this]() { reflectServer(); }));
+ thread_.reset(new thread([this]() { reflectServer(); }));
waitReady();
ASSERT_TRUE(ucs);
// Run a reflecting server in a thread.
- thread_.reset(new Thread([this]() { reflectServer(); }));
+ thread_.reset(new thread([this]() { reflectServer(); }));
waitReady();
ASSERT_TRUE(ucs);
// Run a reflecting server in a thread.
- thread_.reset(new Thread([this]() { reflectServer(); }));
+ thread_.reset(new thread([this]() { reflectServer(); }));
waitReady();
ASSERT_TRUE(ucs);
// Run a timeout server in a thread.
- thread_.reset(new Thread([this]() { waitReady(); }));
+ thread_.reset(new thread([this]() { waitReady(); }));
// Try configGet: it should get a communication error,
EXPECT_THROW(ucs->configGet("foo"), ControlSocketError);
/// @brief Destructor.
virtual ~HttpControlSocketTest() {
if (thread_) {
- thread_->wait();
+ thread_->join();
thread_.reset();
}
// io_service must be stopped after the thread returns,
///
/// Run IO in a thread.
void start() {
- thread_.reset(new Thread([this]() {
+ thread_.reset(new thread([this]() {
// The thread is ready to go. Signal it to the main
// thread so it can start the actual test.
signalReady();
#include <asiolink/interval_timer.h>
#include <asiolink/io_service.h>
#include <cc/command_interpreter.h>
-#include <util/threads/thread.h>
#include <yang/yang_models.h>
#include <yang/yang_revisions.h>
#include <yang/translator_config.h>
#include <testutils/sandbox.h>
#include <gtest/gtest.h>
#include <sstream>
+#include <thread>
using namespace std;
using namespace isc;
using namespace isc::test;
using namespace isc::yang;
using namespace isc::yang::test;
-using namespace isc::util::thread;
#ifndef HAVE_PRE_0_7_6_SYSREPO
using namespace sysrepo;
#endif
const string TEST_SOCKET = "test-socket";
/// @brief Type definition for the pointer to Thread objects.
-typedef boost::shared_ptr<Thread> ThreadPtr;
+typedef boost::shared_ptr<thread> ThreadPtr;
/// @brief Test version of the NetconfAgent class.
class NakedNetconfAgent : public NetconfAgent {
virtual ~NetconfAgentTest() {
NetconfProcess::shut_down = true;
if (thread_) {
- thread_->wait();
+ thread_->join();
thread_.reset();
}
// io_service must be stopped after the thread returns,
io_service_->stop();
io_service_.reset();
if (thread_) {
- thread_->wait();
+ thread_->join();
thread_.reset();
}
if (agent_) {
EXPECT_NO_THROW(subs->module_change_subscribe(KEA_DHCP4_SERVER.c_str(),
cb, 0, 0,
SR_SUBSCR_APPLY_ONLY));
- thread_.reset(new Thread([this]() { io_service_->run(); }));
+ thread_.reset(new thread([this]() { io_service_->run(); }));
// Change configuration (subnet #1 moved from 10.0.0.0/24 to 10.0.1/0/24).
const YRTree tree1 = {
EXPECT_NO_THROW(subs->module_change_subscribe(KEA_DHCP4_SERVER.c_str(),
cb, 0, 0,
SR_SUBSCR_APPLY_ONLY));
- thread_.reset(new Thread([this]() { io_service_->run(); }));
+ thread_.reset(new thread([this]() { io_service_->run(); }));
// Change configuration (subnet #1 moved to #10).
string xpath = "/kea-dhcp4-server:config/subnet4[id='1']";
CfgServersMapPair service_pair = *servers_map->begin();
// Launch server.
- thread_.reset(new Thread([this]() { fakeServer(); signalStopped(); }));
+ thread_.reset(new thread([this]() { fakeServer(); signalStopped(); }));
// Wait until the server is listening.
waitReady();
CfgServersMapPair service_pair = *servers_map->begin();
// Launch server.
- thread_.reset(new Thread([this]() { fakeServer(); signalStopped();}));
+ thread_.reset(new thread([this]() { fakeServer(); signalStopped();}));
// Wait until the server is listening.
waitReady();
EXPECT_EQ(2, agent_->subscriptions_.size());
// Launch server.
- thread_.reset(new Thread([this]() { fakeServer(); signalStopped(); }));
+ thread_.reset(new thread([this]() { fakeServer(); signalStopped(); }));
// Wait until the server is listening.
waitReady();
EXPECT_EQ(2, agent_->subscriptions_.size());
// Launch server twice.
- thread_.reset(new Thread([this]()
+ thread_.reset(new thread([this]()
{
fakeServer();
fakeServer();
perfdhcp_LDADD += $(top_builddir)/src/lib/cryptolink/libkea-cryptolink.la
perfdhcp_LDADD += $(top_builddir)/src/lib/hooks/libkea-hooks.la
perfdhcp_LDADD += $(top_builddir)/src/lib/log/libkea-log.la
-perfdhcp_LDADD += $(top_builddir)/src/lib/util/threads/libkea-threads.la
perfdhcp_LDADD += $(top_builddir)/src/lib/util/libkea-util.la
perfdhcp_LDADD += $(top_builddir)/src/lib/cc/libkea-cc.la
perfdhcp_LDADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la
run_flag_.clear();
isc_throw(isc::Unexpected, "run_flag_ should be false.");
}
- recv_thread_.reset(new util::thread::Thread(boost::bind(&Receiver::run, this)));
+ recv_thread_.reset(new std::thread(boost::bind(&Receiver::run, this)));
}
void
if (run_flag_.test_and_set()) {
// Clear flags to order the thread to stop its main loop.
run_flag_.clear();
- recv_thread_->wait();
+ recv_thread_->join();
}
}
return readPktFromSocket();
} else {
// In multi thread mode read packet from the queue which is feed by Receiver thread.
- util::thread::Mutex::Locker lock(pkt_queue_mutex_);
+ std::lock_guard<std::mutex> lock(pkt_queue_mutex_);
if (pkt_queue_.empty()) {
if (ip_version_ == 4) {
return Pkt4Ptr();
if (pkt->getType() == DHCPOFFER || pkt->getType() == DHCPACK ||
pkt->getType() == DHCPV6_ADVERTISE || pkt->getType() == DHCPV6_REPLY) {
// Otherwise push the packet to the queue, to main thread.
- util::thread::Mutex::Locker lock(pkt_queue_mutex_);
+ std::lock_guard<std::mutex> lock(pkt_queue_mutex_);
pkt_queue_.push(pkt);
}
}
#include <perfdhcp/command_options.h>
#include <dhcp/pkt.h>
-#include <util/threads/thread.h>
-#include <util/threads/sync.h>
#include <queue>
#include <thread>
+#include <mutex>
#include <boost/atomic.hpp>
namespace isc {
boost::atomic_flag run_flag_;
/// \brief Thread for receiving packets.
- std::unique_ptr<util::thread::Thread> recv_thread_;
+ std::unique_ptr<std::thread> recv_thread_;
/// \brief Queue for passing packets from receiver thread to main thread.
std::queue<dhcp::PktPtr> pkt_queue_;
/// \brief Mutex for controlling access to the queue.
- util::thread::Mutex pkt_queue_mutex_;
+ std::mutex pkt_queue_mutex_;
BasePerfSocket &socket_;
run_unittests_LDADD += $(top_builddir)/src/lib/cryptolink/libkea-cryptolink.la
run_unittests_LDADD += $(top_builddir)/src/lib/hooks/libkea-hooks.la
run_unittests_LDADD += $(top_builddir)/src/lib/log/libkea-log.la
-run_unittests_LDADD += $(top_builddir)/src/lib/util/threads/libkea-threads.la
run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
run_unittests_LDADD += $(top_builddir)/src/lib/util/libkea-util.la
run_unittests_LDADD += $(top_builddir)/src/lib/cc/libkea-cc.la
ha_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libkea-asiolink.la
ha_unittests_LDADD += $(top_builddir)/src/lib/cryptolink/libkea-cryptolink.la
ha_unittests_LDADD += $(top_builddir)/src/lib/log/libkea-log.la
-ha_unittests_LDADD += $(top_builddir)/src/lib/util/threads/libkea-threads.la
ha_unittests_LDADD += $(top_builddir)/src/lib/util/libkea-util.la
ha_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la
ha_unittests_LDADD += $(LOG4CPLUS_LIBS)
// Stop the IO service. This should cause the thread to terminate.
io_service_->stop();
- thread->wait();
+ thread->join();
io_service_->get_io_service().reset();
io_service_->poll();
}
// Stop the IO service. This should cause the thread to terminate.
io_service_->stop();
- thread->wait();
+ thread->join();
io_service_->get_io_service().reset();
io_service_->poll();
}
-// Copyright (C) 2017-2018 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2017-2019 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
timer.cancel();
}
-boost::shared_ptr<util::thread::Thread>
+boost::shared_ptr<std::thread>
HATest::runIOServiceInThread() {
io_service_->get_io_service().reset();
bool running = false;
- util::thread::Mutex mutex;
- util::thread::CondVar condvar;
+ std::mutex mutex;
+ std::condition_variable condvar;
io_service_->post(boost::bind(&HATest::signalServiceRunning, this, boost::ref(running),
boost::ref(mutex), boost::ref(condvar)));
- boost::shared_ptr<util::thread::Thread>
- th(new util::thread::Thread(boost::bind(&IOService::run, io_service_.get())));
+ boost::shared_ptr<std::thread>
+ th(new std::thread(boost::bind(&IOService::run, io_service_.get())));
- util::thread::Mutex::Locker lock(mutex);
+ std::unique_lock<std::mutex> lock(mutex);
while (!running) {
- condvar.wait(mutex);
+ condvar.wait(lock);
}
return (th);
// Stop the IO service. This should cause the thread to terminate.
io_service_->stop();
- thread->wait();
+ thread->join();
}
void
-HATest::signalServiceRunning(bool& running, util::thread::Mutex& mutex,
- util::thread::CondVar& condvar) {
+HATest::signalServiceRunning(bool& running, std::mutex& mutex,
+ std::condition_variable& condvar) {
{
- util::thread::Mutex::Locker lock(mutex);
+ std::lock_guard<std::mutex> lock(mutex);
running = true;
}
- condvar.signal();
+ condvar.notify_one();
}
void
#include <dhcp/pkt6.h>
#include <dhcpsrv/network_state.h>
#include <hooks/libinfo.h>
-#include <util/threads/sync.h>
-#include <util/threads/thread.h>
#include <boost/shared_ptr.hpp>
#include <gtest/gtest.h>
#include <cstdint>
#include <functional>
#include <string>
#include <vector>
+#include <mutex>
+#include <thread>
namespace isc {
namespace ha {
/// @brief Runs IO service in a thread.
///
/// @return Shared pointer to the thread.
- boost::shared_ptr<util::thread::Thread>
- runIOServiceInThread();
+ boost::shared_ptr<std::thread> runIOServiceInThread();
/// @brief Executes commands while running IO service in a thread.
///
/// IO service starts running and executes this function.
/// @param mutex reference to the mutex used for synchronization.
/// @param condvar reference to condition variable used for synchronization.
- void signalServiceRunning(bool& running, util::thread::Mutex& mutex,
- util::thread::CondVar& condvar);
+ void signalServiceRunning(bool& running, std::mutex& mutex,
+ std::condition_variable& condvar);
public:
-// Copyright (C) 2018 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2018-2019 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
#include <testutils/threaded_test.h>
-using namespace isc::util::thread;
-
namespace isc {
namespace test {
void
ThreadedTest::doSignal(bool& flag) {
{
- Mutex::Locker lock(mutex_);
+ std::lock_guard<std::mutex> lock(mutex_);
flag = true;
}
- condvar_.signal();
+ condvar_.notify_one();
}
void
void
ThreadedTest::doWait(bool& flag) {
- Mutex::Locker lock(mutex_);
+ std::unique_lock<std::mutex> lock(mutex_);
while (!flag) {
- condvar_.wait(mutex_);
+ condvar_.wait(lock);
}
}
bool
ThreadedTest::isStopping() {
- Mutex::Locker lock(mutex_);
+ std::lock_guard<std::mutex> lock(mutex_);
return (stopping_);
}
-// Copyright (C) 2018 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2018-2019 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
#ifndef THREADED_TEST_H
#define THREADED_TEST_H
-#include <util/threads/thread.h>
-#include <util/threads/sync.h>
#include <boost/shared_ptr.hpp>
#include <gtest/gtest.h>
+#include <thread>
+#include <mutex>
namespace isc {
namespace test {
bool isStopping();
/// @brief Pointer to server thread.
- boost::shared_ptr<util::thread::Thread> thread_;
+ boost::shared_ptr<std::thread> thread_;
/// @brief Mutex used to synchronize threads.
- util::thread::Mutex mutex_;
+ std::mutex mutex_;
/// Condtional variable for thread waits.
- util::thread::CondVar condvar_;
+ std::condition_variable condvar_;
/// Flag indicating that the thread is ready.
bool ready_;
AUTOMAKE_OPTIONS = subdir-objects
-SUBDIRS = . io unittests tests python threads
+SUBDIRS = . io unittests tests python
AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
AM_CPPFLAGS += $(BOOST_INCLUDES)
libkea_util_random_include_HEADERS = \
random/qid_gen.h \
random/random_number_generator.h
-
-libkea_util_threads_includedir = $(pkgincludedir)/util/threads
-libkea_util_threads_include_HEADERS = \
- threads/sync.h \
- threads/thread.h
+++ /dev/null
-SUBDIRS = . tests
-AM_CXXFLAGS = $(KEA_CXXFLAGS)
-
-AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
-AM_CPPFLAGS += $(BOOST_INCLUDES)
-
-lib_LTLIBRARIES = libkea-threads.la
-libkea_threads_la_SOURCES = sync.h sync.cc
-libkea_threads_la_SOURCES += thread.h thread.cc
-libkea_threads_la_LIBADD = $(top_builddir)/src/lib/util/libkea-util.la
-libkea_threads_la_LIBADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la
-
-libkea_threads_la_LDFLAGS = -no-undefined -version-info 3:0:0
-
-
-CLEANFILES = *.gcno *.gcda
+++ /dev/null
-// Copyright (C) 2012-2016 Internet Systems Consortium, Inc. ("ISC")
-//
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-#include <config.h>
-
-#include <util/threads/sync.h>
-
-#include <exceptions/exceptions.h>
-
-#include <cstring>
-#include <memory>
-#include <cerrno>
-#include <cassert>
-
-#include <pthread.h>
-
-using std::unique_ptr;
-
-namespace isc {
-namespace util {
-namespace thread {
-
-class Mutex::Impl {
-public:
- Impl()
-#ifdef ENABLE_DEBUG
- : locked_count(0)
-#endif // ENABLE_DEBUG
- {}
-
- pthread_mutex_t mutex;
-#ifdef ENABLE_DEBUG
- size_t locked_count;
-#endif // ENABLE_DEBUG
-};
-
-namespace {
-
-struct Deinitializer {
- Deinitializer(pthread_mutexattr_t& attributes):
- attributes_(attributes)
- {}
- ~Deinitializer() {
- const int result = pthread_mutexattr_destroy(&attributes_);
- // This should never happen. According to the man page,
- // if there's error, it's our fault.
- assert(result == 0);
- }
- pthread_mutexattr_t& attributes_;
-};
-
-}
-
-Mutex::Mutex() :
- impl_(NULL)
-{
- pthread_mutexattr_t attributes;
- int result = pthread_mutexattr_init(&attributes);
- switch (result) {
- case 0: // All 0K
- break;
- case ENOMEM:
- throw std::bad_alloc();
- default:
- isc_throw(isc::InvalidOperation, std::strerror(result));
- }
- Deinitializer deinitializer(attributes);
-
- // If debug mode is enabled in compilation, use the slower
- // error-checking mutexes that detect deadlocks. Otherwise, use fast
- // mutexes which don't. See the pthread_mutexattr_settype() POSIX
- // documentation which describes these type attributes.
-#ifdef ENABLE_DEBUG
- result = pthread_mutexattr_settype(&attributes, PTHREAD_MUTEX_ERRORCHECK);
-#else
- result = pthread_mutexattr_settype(&attributes, PTHREAD_MUTEX_NORMAL);
-#endif // ENABLE_DEBUG
- if (result != 0) {
- isc_throw(isc::InvalidOperation, std::strerror(result));
- }
-
- unique_ptr<Impl> impl(new Impl);
- result = pthread_mutex_init(&impl->mutex, &attributes);
- switch (result) {
- case 0: // All 0K
- impl_ = impl.release();
- break;
- case ENOMEM:
- case EAGAIN:
- throw std::bad_alloc();
- default:
- isc_throw(isc::InvalidOperation, std::strerror(result));
- }
-}
-
-Mutex::~Mutex() {
- if (impl_ != NULL) {
- const int result = pthread_mutex_destroy(&impl_->mutex);
-
-#ifdef ENABLE_DEBUG
- const bool locked = impl_->locked_count != 0;
-#endif // ENABLE_DEBUG
-
- delete impl_;
- // We don't want to throw from the destructor. Also, if this ever
- // fails, something is really screwed up a lot.
- assert(result == 0);
-
-#ifdef ENABLE_DEBUG
- // We should not try to destroy a locked mutex, bad threaded monsters
- // could get loose if we ever do and it is also forbidden by pthreads.
-
- // This should not be possible to happen, since the
- // pthread_mutex_destroy should check for it already. But it seems
- // there are systems that don't check it.
- assert(!locked);
-#endif // ENABLE_DEBUG
- }
-}
-
-#ifdef ENABLE_DEBUG
-
-void
-Mutex::postLockAction() {
- assert(impl_->locked_count == 0);
- ++impl_->locked_count;
-}
-
-void
-Mutex::preUnlockAction(bool throw_ok) {
- if (impl_->locked_count == 0) {
- if (throw_ok) {
- isc_throw(isc::InvalidOperation,
- "Unlock attempt for unlocked mutex");
- } else {
- assert(false);
- }
- }
- --impl_->locked_count;
-}
-
-bool
-Mutex::locked() const {
- return (impl_->locked_count != 0);
-}
-
-#endif // ENABLE_DEBUG
-
-void
-Mutex::lock() {
- assert(impl_ != NULL);
- const int result = pthread_mutex_lock(&impl_->mutex);
- if (result != 0) {
- isc_throw(isc::InvalidOperation, std::strerror(result));
- }
-#ifdef ENABLE_DEBUG
- postLockAction(); // Only in debug mode
-#endif // ENABLE_DEBUG
-}
-
-bool
-Mutex::tryLock() {
- assert(impl_ != NULL);
- const int result = pthread_mutex_trylock(&impl_->mutex);
- // In the case of pthread_mutex_trylock(), if it is called on a
- // locked mutex from the same thread, some platforms (such as fedora
- // and debian) return EBUSY whereas others (such as centos 5) return
- // EDEADLK. We return false and don't pass the lock attempt in both
- // cases.
- if (result == EBUSY || result == EDEADLK) {
- return (false);
- } else if (result != 0) {
- isc_throw(isc::InvalidOperation, std::strerror(result));
- }
-#ifdef ENABLE_DEBUG
- postLockAction(); // Only in debug mode
-#endif // ENABLE_DEBUG
- return (true);
-}
-
-void
-Mutex::unlock() {
- assert(impl_ != NULL);
-#ifdef ENABLE_DEBUG
- preUnlockAction(false); // Only in debug mode. Ensure no throw.
-#endif // ENABLE_DEBUG
- const int result = pthread_mutex_unlock(&impl_->mutex);
- assert(result == 0); // This should never be possible
-}
-
-class CondVar::Impl {
-public:
- Impl() {
- const int result = pthread_cond_init(&cond_, NULL);
- if (result != 0) {
- isc_throw(isc::Unexpected, "pthread_cond_init failed: "
- << std::strerror(result));
- }
- }
- ~Impl() {
- const int result = pthread_cond_destroy(&cond_);
-
- // This can happen if we try to destroy cond_ while some other thread
- // is waiting on it. assert() may be too strong for such a case,
- // but we cannot safely destroy cond_ anyway. In order to avoid
- // throwing from a destructor we simply let the process die.
- assert(result == 0);
- }
-
- // For convenience allow the main class to access this directly.
- pthread_cond_t cond_;
-};
-
-CondVar::CondVar() : impl_(new Impl)
-{}
-
-CondVar::~CondVar() {
- delete impl_;
-}
-
-void
-CondVar::wait(Mutex& mutex) {
-#ifdef ENABLE_DEBUG
- mutex.preUnlockAction(true); // Only in debug mode
- const int result = pthread_cond_wait(&impl_->cond_, &mutex.impl_->mutex);
- mutex.postLockAction(); // Only in debug mode
-#else
- const int result = pthread_cond_wait(&impl_->cond_, &mutex.impl_->mutex);
-#endif
- // pthread_cond_wait should normally succeed unless mutex is completely
- // broken.
- if (result != 0) {
- isc_throw(isc::BadValue, "pthread_cond_wait failed unexpectedly: " <<
- std::strerror(result));
- }
-}
-
-void
-CondVar::signal() {
- const int result = pthread_cond_signal(&impl_->cond_);
-
- // pthread_cond_signal() can only fail when if cond_ is invalid. It
- //should be impossible as long as this is a valid CondVar object.
- assert(result == 0);
-}
-
-}
-}
-}
+++ /dev/null
-// Copyright (C) 2012-2016 Internet Systems Consortium, Inc. ("ISC")
-//
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-#ifndef KEA_THREAD_SYNC_H
-#define KEA_THREAD_SYNC_H
-
-#include <exceptions/exceptions.h>
-
-#include <boost/noncopyable.hpp>
-
-#include <cstdlib> // for NULL.
-
-namespace isc {
-namespace util {
-namespace thread {
-class CondVar;
-
-/// \brief Mutex with very simple interface
-///
-/// Since mutexes are very system dependent, we create our own wrapper around
-/// whatever is available on the system and hide it.
-///
-/// To use this mutex, create it and then lock and unlock it by creating the
-/// Mutex::Locker object.
-///
-/// Also, as mutex is a low-level system object, an error might happen at any
-/// operation with it. We convert many errors to the isc::InvalidOperation,
-/// since the errors usually happen only when used in a wrong way. Any methods
-/// or constructors in this class can throw. Allocation errors are converted
-/// to std::bad_alloc (for example when OS-dependent limit of mutexes is
-/// exceeded). Some errors which usually mean a programmer error abort the
-/// program, since there could be no safe way to recover from them.
-///
-/// The current interface is somewhat minimalist. If we ever need more, we
-/// can add it later.
-class Mutex : boost::noncopyable {
-public:
- /// \brief Constructor.
- ///
- /// Creates a mutex. It is a non-recursive mutex (can be locked just once,
- /// if the same threads tries to lock it again, Bad Things Happen).
- ///
- /// Depending on compilation parameters and OS, the mutex may or may not
- /// do some error and sanity checking. However, such checking is meant
- /// only to aid development, not rely on it as a feature.
- ///
- /// \throw std::bad_alloc In case allocation of something (memory, the
- /// OS mutex) fails.
- /// \throw isc::InvalidOperation Other unspecified errors around the mutex.
- /// This should be rare.
- Mutex();
-
- /// \brief Destructor.
- ///
- /// Destroys the mutex. It is not allowed to destroy a mutex which is
- /// currently locked. This means a Locker created with this Mutex must
- /// never live longer than the Mutex itself.
- ~Mutex();
-
- /// \brief This holds a lock on a Mutex.
- ///
- /// To lock a mutex, create a locker. It'll get unlocked when the locker
- /// is destroyed.
- ///
- /// If you create the locker on the stack or using some other "garbage
- /// collecting" mechanism (unique_ptr, for example), it ensures exception
- /// safety with regards to the mutex - it'll get released on the exit
- /// of function no matter by what means.
- class Locker : boost::noncopyable {
- public:
- /// \brief Exception thrown when the mutex is already locked and
- /// a non-blocking locker is attempted around it.
- struct AlreadyLocked : public isc::InvalidParameter {
- AlreadyLocked(const char* file, size_t line, const char* what) :
- isc::InvalidParameter(file, line, what)
- {}
- };
-
- /// \brief Constructor.
- ///
- /// Locks the mutex. May block for extended period of time if
- /// \c block is true.
- ///
- /// \throw isc::InvalidOperation when OS reports error. This usually
- /// means an attempt to use the mutex in a wrong way (locking
- /// a mutex second time from the same thread, for example).
- /// \throw AlreadyLocked if \c block is false and the mutex is
- /// already locked.
- Locker(Mutex& mutex, bool block = true) :
- mutex_(mutex)
- {
- if (block) {
- mutex.lock();
- } else {
- if (!mutex.tryLock()) {
- isc_throw(AlreadyLocked, "The mutex is already locked");
- }
- }
- }
-
- /// \brief Destructor.
- ///
- /// Unlocks the mutex.
- ~Locker() {
- mutex_.unlock();
- }
- private:
- Mutex& mutex_;
- };
- /// \brief If the mutex is currently locked
- ///
- /// This is debug aiding method only. And it might be unavailable in
- /// non-debug build (because keeping the state might be needlessly
- /// slow).
- ///
- /// \todo Disable in non-debug build
- bool locked() const;
-
-private:
- /// \brief Lock the mutex
- ///
- /// This method blocks until the mutex can be locked.
- void lock();
-
- /// \brief Try to lock the mutex
- ///
- /// This method doesn't block and returns immediately with a status
- /// on whether the lock operation was successful.
- ///
- /// \return true if the lock was successful, false otherwise.
- bool tryLock();
-
- /// \brief Unlock the mutex
- void unlock();
-
-private:
- friend class CondVar;
-
- // Commonly called after acquiring the lock, checking and updating
- // internal state for debug.
- //
- // Note that this method is only available when the build is
- // configured with debugging support.
- void postLockAction();
-
- // Commonly called before releasing the lock, checking and updating
- // internal state for debug.
- //
- // If throw_ok is true, it throws \c isc::InvalidOperation when the check
- // fails; otherwise it aborts the process. This parameter must be set
- // to false if the call to this shouldn't result in an exception (e.g.
- // when called from a destructor).
- //
- // Note that this method is only available when the build is
- // configured with debugging support.
- void preUnlockAction(bool throw_ok);
-
- class Impl;
- Impl* impl_;
-};
-
-/// \brief Encapsulation for a condition variable.
-///
-/// This class provides a simple encapsulation of condition variable for
-/// inter-thread synchronization. It has similar but simplified interface as
-/// that for \c pthread_cond_ variants.
-///
-/// It uses the \c Mutex class object for the mutex used with the condition
-/// variable. Since for normal applications the internal \c Mutex::Locker
-/// class is the only available interface to acquire a lock, sample code
-/// for waiting on a condition variable would look like this:
-/// \code
-/// CondVar cond;
-/// Mutex mutex;
-/// {
-/// Mutex::Locker locker(mutex);
-/// while (some_condition) {
-/// cond.wait(mutex);
-/// }
-/// // do something under the protection of locker
-/// } // lock is released here
-/// \endcode
-/// Note that \c mutex passed to the \c wait() method must be the same one
-/// used to construct the \c locker.
-///
-/// Right now there is no equivalent to pthread_cond_broadcast() or
-/// pthread_cond_timedwait() in this class, because this class was meant
-/// for internal development of BIND 10 and we don't need these at the
-/// moment. If and when we need these interfaces they can be added at that
-/// point. Also, Kea likely to not use threading model, so the usefulness
-/// of this class is uncertain.
-///
-/// \note This class is defined as a friend class of \c Mutex and directly
-/// refers to and modifies private internals of the \c Mutex class. It breaks
-/// the assumption that the lock is only acquired or released via the
-/// \c Locker class and breaks other integrity assumption on \c Mutex,
-/// thereby making it more fragile, but we couldn't find other way to
-/// implement a safe and still simple realization of condition variables.
-/// So, this is a kind of compromise. If this class is needed to be
-/// extended, first consider a way to use public interfaces of \c Mutex;
-/// do not easily rely on the fact that this class is a friend of it.
-class CondVar : boost::noncopyable {
-public:
- /// \brief Constructor.
- ///
- /// \throw std::bad_alloc memory allocation failure
- /// \throw isc::Unexpected other unexpected shortage of system resource
- CondVar();
-
- /// \brief Destructor.
- ///
- /// An object of this class must not be destroyed while some thread
- /// is waiting on it. If this condition isn't met the destructor will
- /// terminate the program.
- ~CondVar();
-
- /// \brief Wait on the condition variable.
- ///
- /// This method works like \c pthread_cond_wait(). For mutex it takes
- /// an \c Mutex class object. A lock for the mutex must have been
- /// acquired. If this condition isn't met, it can throw an exception
- /// (in the debug mode build) or result in undefined behavior.
- ///
- /// The lock will be automatically released within this method, and
- /// will be re-acquired on the exit of this method.
- ///
- /// \throw isc::InvalidOperation mutex isn't locked
- /// \throw isc::BadValue mutex is not a valid \c Mutex object
- ///
- /// \param mutex A \c Mutex object to be released on wait().
- void wait(Mutex& mutex);
-
- /// \brief Unblock a thread waiting for the condition variable.
- ///
- /// This method wakes one of other threads (if any) waiting on this object
- /// via the \c wait() call.
- ///
- /// This method never throws; if some unexpected low level error happens
- /// it terminates the program.
- void signal();
-private:
- class Impl;
- Impl* impl_;
-};
-
-} // namespace thread
-} // namespace util
-} // namespace isc
-
-#endif
-
-// Local Variables:
-// mode: c++
-// End:
+++ /dev/null
-/run_unittests
+++ /dev/null
-SUBDIRS = .
-
-AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
-AM_CPPFLAGS += $(BOOST_INCLUDES)
-# XXX: we'll pollute the top builddir for creating a temporary test file
-# # used to bind a UNIX domain socket so we can minimize the risk of exceeding
-# # the limit of file name path size.
-AM_CPPFLAGS += -DTEST_DATA_TOPBUILDDIR=\"$(abs_top_builddir)\"
-AM_CXXFLAGS = $(KEA_CXXFLAGS)
-
-if USE_STATIC_LINK
-AM_LDFLAGS = -static
-endif
-
-CLEANFILES = *.gcno *.gcda
-
-TESTS_ENVIRONMENT = \
- $(LIBTOOL) --mode=execute $(VALGRIND_COMMAND)
-
-TESTS =
-if HAVE_GTEST
-TESTS += run_unittests
-run_unittests_SOURCES = run_unittests.cc
-run_unittests_SOURCES += thread_unittest.cc
-run_unittests_SOURCES += lock_unittest.cc
-run_unittests_SOURCES += condvar_unittest.cc
-
-run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
-run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
-
-run_unittests_LDADD = $(top_builddir)/src/lib/util/threads/libkea-threads.la
-run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
-run_unittests_LDADD += $(top_builddir)/src/lib/util/libkea-util.la
-run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la
-run_unittests_LDADD += $(GTEST_LDADD)
-endif
-
-noinst_PROGRAMS = $(TESTS)
+++ /dev/null
-// Copyright (C) 2012-2018 Internet Systems Consortium, Inc. ("ISC")
-//
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-#include <config.h>
-
-#include <exceptions/exceptions.h>
-#include <util/unittests/check_valgrind.h>
-
-#include <util/threads/sync.h>
-#include <util/threads/thread.h>
-
-#include <gtest/gtest.h>
-
-#include <boost/bind.hpp>
-#include <boost/scoped_ptr.hpp>
-
-#include <cstring>
-
-#include <unistd.h>
-#include <signal.h>
-
-using namespace isc::util::thread;
-
-namespace {
-// Used as a signal handler below.
-volatile bool do_exit; // use for emergency escape
-void
-alarmHandler(int) {
- do_exit = true;
-}
-
-class CondVarTest : public ::testing::Test {
-protected:
- // We use a signal in case some of the thread synchronization tests
- // unexpectedly cause a deadlock.
- void SetUp() {
- do_exit = false;
-
- std::memset(&handler_, 0, sizeof(handler_));
- handler_.sa_handler = alarmHandler;
- if (sigaction(SIGALRM, &handler_, &original_) != 0) {
- FAIL() << "Couldn't set alarm";
- }
- alarm(10); // 10sec duration: arbitrary choice
- }
- void TearDown() {
- // Cancel the alarm and return the original handler
- alarm(0);
- if (sigaction(SIGALRM, &original_, NULL)) {
- FAIL() << "Couldn't restore alarm";
- }
- }
-
- CondVar condvar_;
- Mutex mutex_;
-private:
- struct sigaction handler_, original_;
-};
-
-TEST(CondVarTest0, create) {
- // Just construct and destruct it. Nothing unusual should happen.
- EXPECT_NO_THROW(CondVar condvar);
-}
-
-// Running on a separate thread, just updating the argument and waking up
-// the other thread via the condition variable passed.
-void
-ringSignal(CondVar* condvar, Mutex* mutex, int* arg) {
- assert(*arg == 0);
- Mutex::Locker locker(*mutex);
- ++*arg;
- condvar->signal();
-}
-
-// A simple wait-signal operation on a condition variable.
-TEST_F(CondVarTest, waitAndSignal) {
- if (!isc::util::unittests::runningOnValgrind()) {
- Mutex::Locker locker(mutex_);
- int shared_var = 0; // let the other thread increment this
- Thread t(boost::bind(&ringSignal, &condvar_, &mutex_, &shared_var));
- condvar_.wait(mutex_);
- t.wait();
- EXPECT_EQ(1, shared_var);
- }
-}
-
-// Thread's main code for the next test
-void
-signalAndWait(CondVar* condvar1, CondVar* condvar2, Mutex* mutex, int* arg) {
- Mutex::Locker locker(*mutex);
- ++*arg;
- condvar2->signal(); // let the main thread know this one is ready
- condvar1->wait(*mutex);
- ++*arg;
-}
-
-// Similar to the previous test, but having two threads wait for a condvar.
-TEST_F(CondVarTest, multiWaits) {
- boost::scoped_ptr<Mutex::Locker> locker(new Mutex::Locker(mutex_));
- CondVar condvar2; // separate cond var for initial synchronization
- int shared_var = 0; // let the other thread increment this
- Thread t1(boost::bind(&signalAndWait, &condvar_, &condvar2, &mutex_,
- &shared_var));
- Thread t2(boost::bind(&signalAndWait, &condvar_, &condvar2, &mutex_,
- &shared_var));
-
- // Wait until both threads are waiting on condvar_.
- while (shared_var < 2 && !do_exit) {
- condvar2.wait(mutex_);
- }
- // Check we exited from the loop successfully.
- ASSERT_FALSE(do_exit);
- ASSERT_EQ(2, shared_var);
-
- // release the lock, wake up both threads, wait for them to die, and
- // confirm they successfully woke up.
- locker.reset();
- condvar_.signal();
- condvar_.signal();
- t1.wait();
- t2.wait();
- EXPECT_EQ(4, shared_var);
-}
-
-// Similar to the previous version of the same function, but just do
-// condvar operations. It will never wake up.
-void
-signalAndWait(CondVar* condvar, Mutex* mutex) {
- Mutex::Locker locker(*mutex);
- condvar->signal();
- condvar->wait(*mutex);
-}
-
-#ifdef HAS_UNDEFINED_PTHREAD_BEHAVIOR
-TEST_F(CondVarTest, DISABLED_destroyWhileWait) {
-#else
-// This tests had to be disabled because it hangs on most of the OS used in lab
-// TODO fix destroyWhileWait test
-TEST_F(CondVarTest, DISABLED_destroyWhileWait) {
-#endif
- // We'll destroy a CondVar object while the thread is still waiting
- // on it. This will trigger an assertion failure.
- if (!isc::util::unittests::runningOnValgrind()) {
- EXPECT_DEATH_IF_SUPPORTED({
- CondVar cond;
- Mutex::Locker locker(mutex_);
- Thread t(boost::bind(&signalAndWait, &cond, &mutex_));
- cond.wait(mutex_);
- }, "");
- }
-}
-
-#ifdef ENABLE_DEBUG
-
-TEST_F(CondVarTest, badWait) {
- // In our implementation, wait() requires acquiring the lock beforehand.
- EXPECT_THROW(condvar_.wait(mutex_), isc::InvalidOperation);
-}
-
-#endif // ENABLE_DEBUG
-
-TEST_F(CondVarTest, emptySignal) {
- // It's okay to call signal when no one waits.
- EXPECT_NO_THROW(condvar_.signal());
-}
-
-}
+++ /dev/null
-// Copyright (C) 2012-2015 Internet Systems Consortium, Inc. ("ISC")
-//
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-#include <config.h>
-
-#include <gtest/gtest.h>
-
-#include <util/threads/sync.h>
-#include <util/threads/thread.h>
-#include <util/unittests/check_valgrind.h>
-
-#include <boost/bind.hpp>
-#include <unistd.h>
-#include <signal.h>
-
-using namespace isc::util::thread;
-
-namespace {
-
-#ifdef ENABLE_DEBUG
-
-// If we try to lock the debug mutex multiple times, it should
-// throw. This test will complete properly only when pthread debugging
-// facilities are enabled by configuring the code for debug build.
-TEST(MutexTest, lockMultiple) {
- Mutex mutex;
- EXPECT_FALSE(mutex.locked()); // Debug-only build
-
- Mutex::Locker l1(mutex);
- EXPECT_TRUE(mutex.locked()); // Debug-only build
-
- EXPECT_THROW({
- Mutex::Locker l2(mutex); // Attempt to lock again.
- }, isc::InvalidOperation);
- EXPECT_TRUE(mutex.locked()); // Debug-only build
-
- // block=true explicitly.
- Mutex mutex2;
- EXPECT_FALSE(mutex2.locked()); // Debug-only build
- Mutex::Locker l12(mutex2, true);
- EXPECT_TRUE(mutex2.locked()); // Debug-only build
-}
-
-void
-testThread(Mutex* mutex)
-{
- // block=false (tryLock). This should not block indefinitely, but
- // throw AlreadyLocked. If block were true, this would block
- // indefinitely here.
- EXPECT_THROW({
- Mutex::Locker l3(*mutex, false);
- }, Mutex::Locker::AlreadyLocked);
-
- EXPECT_TRUE(mutex->locked()); // Debug-only build
-}
-
-// Test the non-blocking variant using a second thread.
-TEST(MutexTest, lockNonBlocking) {
- // block=false (tryLock).
- Mutex mutex;
- Mutex::Locker l1(mutex, false);
- EXPECT_TRUE(mutex.locked()); // Debug-only build
-
- // First, try another locker from the same thread.
- EXPECT_THROW({
- Mutex::Locker l2(mutex, false);
- }, Mutex::Locker::AlreadyLocked);
-
- EXPECT_TRUE(mutex.locked()); // Debug-only build
-
- // Now try another locker from a different thread.
- Thread thread(boost::bind(&testThread, &mutex));
- thread.wait();
-}
-
-#endif // ENABLE_DEBUG
-
-// Destroying a locked mutex is a bad idea as well
-#ifdef HAS_UNDEFINED_PTHREAD_BEHAVIOR
-TEST(MutexTest, DISABLED_destroyLocked) {
-#else
-TEST(MutexTest, destroyLocked) {
-#endif
- if (!isc::util::unittests::runningOnValgrind()) {
- EXPECT_DEATH_IF_SUPPORTED({
- Mutex* mutex = new Mutex;
- new Mutex::Locker(*mutex);
- delete mutex;
- // This'll leak the locker, but inside the slave process, it should
- // not be an issue.
- }, "");
- }
-}
-
-// In this test, we try to check if a mutex really locks. We could try that
-// with a deadlock, but that's not practical (the test would not end).
-//
-// Instead, we try do to some operation on the same data from multiple threads
-// that's likely to break if not locked. Also, the test must run for a while
-// to have an opportunity to manifest.
-//
-// Currently we try incrementing a double variable. That one is large enough
-// and complex enough so it should not be possible for the CPU to do it as an
-// atomic operation, at least on common architectures.
-const size_t iterations = 100000;
-
-void
-performIncrement(volatile double* canary, volatile bool* ready_me,
- volatile bool* ready_other, Mutex* mutex)
-{
- // Loosely (busy) wait for the other thread so both will start
- // approximately at the same time.
- *ready_me = true;
- while (!*ready_other) {}
-
- for (size_t i = 0; i < iterations; ++i) {
- Mutex::Locker lock(*mutex);
- *canary += 1;
- }
-}
-
-void
-noHandler(int) {}
-
-TEST(MutexTest, swarm) {
- if (!isc::util::unittests::runningOnValgrind()) {
- // Create a timeout in case something got stuck here
- struct sigaction ignored, original;
- memset(&ignored, 0, sizeof(ignored));
- ignored.sa_handler = noHandler;
- if (sigaction(SIGALRM, &ignored, &original)) {
- FAIL() << "Couldn't set alarm";
- }
- alarm(10);
- // This type has a low chance of being atomic itself, further raising
- // the chance of problems appearing.
- double canary = 0;
- Mutex mutex;
- // Run two parallel threads
- bool ready1 = false;
- bool ready2 = false;
- Thread t1(boost::bind(&performIncrement, &canary, &ready1, &ready2,
- &mutex));
- Thread t2(boost::bind(&performIncrement, &canary, &ready2, &ready1,
- &mutex));
- t1.wait();
- t2.wait();
- // Check it the sum is the expected value.
- EXPECT_EQ(iterations * 2, canary) << "Threads are badly synchronized";
- // Cancel the alarm and return the original handler
- alarm(0);
- if (sigaction(SIGALRM, &original, NULL)) {
- FAIL() << "Couldn't restore alarm";
- }
- }
-}
-
-}
+++ /dev/null
-// Copyright (C) 2012-2017 Internet Systems Consortium, Inc. ("ISC")
-//
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-#include <config.h>
-
-#include <gtest/gtest.h>
-#include <util/unittests/run_all.h>
-#include <stdlib.h>
-
-// This file uses TEST_DATA_TOPBUILDDIR macro, which must point to a writable
-// directory. It will be used for creating a logger lockfile.
-
-int
-main(int argc, char* argv[]) {
- ::testing::InitGoogleTest(&argc, argv);
-
- setenv("KEA_LOCKFILE_DIR", TEST_DATA_TOPBUILDDIR, 1);
- return (isc::util::unittests::run_all());
-}
+++ /dev/null
-// Copyright (C) 2012-2015 Internet Systems Consortium, Inc. ("ISC")
-//
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-#include <config.h>
-
-#include <util/process_spawn.h>
-#include <util/threads/sync.h>
-#include <util/threads/thread.h>
-#include <util/unittests/check_valgrind.h>
-
-#include <boost/bind.hpp>
-#include <boost/scoped_ptr.hpp>
-
-#include <gtest/gtest.h>
-
-#include <signal.h>
-#include <unistd.h>
-
-// This file tests the Thread class. It's hard to test an actual thread is
-// started, but we at least check the function is run and exceptions are
-// propagated as they should.
-//
-// We run some tests multiple times to see if there happen to be a race
-// condition (then it would have better chance showing up).
-//
-// The detached tests are not run as many times to prevent many threads being
-// started in parallel (the other tests wait for the previous one to terminate
-// before starting new one).
-
-using namespace isc::util;
-using namespace isc::util::thread;
-
-namespace {
-const size_t iterations = 200;
-const size_t detached_iterations = 25;
-
-/// @brief Implements a thread which can be stopped.
-///
-/// This class implements a worker thread which can be stopped by calling
-/// StoppableThread::stop. The call to this function blocks until the thread
-/// terminates. This class is useful for testing scenarios when the thread
-/// needs to be run for a specific amount of time.
-class StoppableThread {
-public:
-
- /// @brief Constructor.
- ///
- /// It doesn't run the thread yet. It merely initializes required
- /// class members.
- StoppableThread()
- : stopping_(false), mutex_(), thread_() {
- }
-
- /// @brief Destructor.
- ///
- /// Detaches the thread.
- ~StoppableThread() {
- }
-
- /// @brief Starts the execution of the thread.
- ///
- /// This method will not start the thread if the thread has been stopped.
- /// In order to start the thread again, @c StoppableThread::reset must be
- /// called first.
- void run() {
- if (!amStopping()) {
- thread_.reset(new Thread(boost::bind(&StoppableThread::loop, this)));
- }
- }
-
- /// @brief Stops the thread as soon as possible.
- void stop() {
- if (!amStopping()) {
- setStopping(true);
- if (thread_) {
- thread_->wait();
- thread_.reset();
- }
- }
- }
-
- /// @brief Resets the thread state so as it can be ran again.
- void reset() {
- setStopping(false);
- }
-
-private:
-
- /// @brief Checks if the thread is being stopped.
- ///
- /// @return true if the thread is being stopped.
- bool amStopping() {
- Mutex::Locker lock(mutex_);
- return (stopping_);
- }
-
- /// @brief Sets the stopping state.
- ///
- /// @param stopping New value for @c stopping_ state.
- void setStopping(const bool stopping) {
- Mutex::Locker lock(mutex_);
- stopping_ = stopping;
- }
-
- /// @brief Worker thread loop.
- ///
- /// It runs until the @c StoppableThread::stop is called.
- void loop() {
- for (;;) {
- if (amStopping()) {
- break;
- }
- usleep(100);
- }
- }
-
- /// @brief Flag indicating if the thread is being stopped.
- bool stopping_;
- /// @brief Mutex used for protecting @c stopping_ flag.
- Mutex mutex_;
- /// @brief Pointer to the thread instance.
- boost::scoped_ptr<Thread> thread_;
-};
-
-/// @brief Static instance of the stoppable thread.
-boost::scoped_ptr<StoppableThread> thread;
-
-/// @brief Test fixture class for testing @c Thread.
-class ThreadTest : public ::testing::Test {
-public:
-
- /// @brief Destructor.
- ///
- /// Stops the thread and resets the static pointer to
- /// @c StoppableThread.
- virtual ~ThreadTest() {
- if (thread) {
- thread->stop();
- }
- thread.reset();
- }
-
- /// @brief No-op method.
- static void doSomething(int*) { }
-
- /// @brief Marks specified boolean value as true to indicate that the
- /// function has been run.
- static void markRun(bool* mark) {
- EXPECT_FALSE(*mark);
- *mark = true;
- }
-
- /// @brief Throws 42.
- static void throwSomething() {
- throw 42; // Throw something really unusual, to see everything is caught.
- }
-
- /// @brief Throws standard exception.
- static void throwException() {
- throw std::exception();
- }
-
- /// @brief Returns signal mask set for a thread.
- ///
- /// @parm mask Pointer to signal mask set for the calling thread.
- static void getSignalMask(sigset_t* mask) {
- pthread_sigmask(SIG_SETMASK, 0, mask);
- }
-};
-
-
-
-// We just test that we can forget about the thread and nothing
-// bad will happen on our side.
-TEST_F(ThreadTest, detached) {
- if (!isc::util::unittests::runningOnValgrind()) {
- int x;
- for (size_t i = 0; i < detached_iterations; ++i) {
- Thread thread(boost::bind(&ThreadTest::doSomething, &x));
- }
- }
-}
-
-// Wait for a thread to end first. The variable must be set at the time.
-TEST_F(ThreadTest, wait) {
- if (!isc::util::unittests::runningOnValgrind()) {
- for (size_t i = 0; i < iterations; ++i) {
- bool mark = false;
- Thread thread(boost::bind(&ThreadTest::markRun, &mark));
- thread.wait();
- ASSERT_TRUE(mark) << "Not finished yet in " << i << "th iteration";
- // Can't wait second time
- ASSERT_THROW(thread.wait(), isc::InvalidOperation);
- }
- }
-}
-
-// Exception in the thread we forget about should not do anything to us
-TEST_F(ThreadTest, detachedException) {
- if (!isc::util::unittests::runningOnValgrind()) {
- for (size_t i = 0; i < detached_iterations; ++i) {
- Thread thread(&ThreadTest::throwSomething);
- }
- for (size_t i = 0; i < detached_iterations; ++i) {
- Thread thread(&ThreadTest::throwException);
- }
- }
-}
-
-// An uncaught exception in the thread should propagate through wait
-TEST_F(ThreadTest, exception) {
- if (!isc::util::unittests::runningOnValgrind()) {
- for (size_t i = 0; i < iterations; ++i) {
- Thread thread(throwSomething);
- Thread thread2(throwException);
- ASSERT_THROW(thread.wait(), Thread::UncaughtException);
- ASSERT_THROW(thread2.wait(), Thread::UncaughtException);
- }
- }
-}
-
-// Verify that all signals are blocked.
-TEST_F(ThreadTest, sigmask) {
- sigset_t mask;
- sigemptyset(&mask);
- Thread thread(boost::bind(&ThreadTest::getSignalMask, &mask));
- ASSERT_NO_THROW(thread.wait());
- EXPECT_EQ(1, sigismember(&mask, SIGHUP));
- EXPECT_EQ(1, sigismember(&mask, SIGINT));
- EXPECT_EQ(1, sigismember(&mask, SIGTERM));
-}
-
-
-/// This test verifies using threads and spawning child processes
-/// work together.
-TEST_F(ThreadTest, spawnProcessWithThread) {
- // Initialize and run the stoppable thread. Note that the 'thread'
- // is a static variable, which will be 'cloned' into the child
- // process. Its destructor must not be called when the child process
- // terminates with EXIT_FAILURE status.
- thread.reset(new StoppableThread());
- thread->run();
-
- // Spawn the new process, using some non-existing executable. The
- // current process will fork but the execvp should fail.
- ProcessSpawn process_spawn("kea-dhcp4-a86570943h");
- pid_t pid = process_spawn.spawn();
- // Wait for the process to terminate.
- while (process_spawn.isRunning(pid)) {
- usleep(100);
- }
- // When the child process terminates it will call _exit() so
- // nothing bad should happen from the child.
- EXPECT_EQ(EXIT_FAILURE, process_spawn.getExitStatus(pid));
- // The thread is still there.
- EXPECT_TRUE(thread);
-}
-
-}
+++ /dev/null
-// Copyright (C) 2012-2016 Internet Systems Consortium, Inc. ("ISC")
-//
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-#include <config.h>
-
-#include <util/threads/thread.h>
-#include <util/threads/sync.h>
-
-#include <memory>
-#include <string>
-#include <cstring>
-#include <cerrno>
-
-#include <pthread.h>
-#include <signal.h>
-
-#include <boost/noncopyable.hpp>
-#include <boost/scoped_ptr.hpp>
-
-using std::string;
-using std::exception;
-using std::unique_ptr;
-using boost::scoped_ptr;
-
-namespace isc {
-namespace util {
-namespace thread {
-
-namespace {
-
-// Signal blocker class.
-class Blocker : boost::noncopyable {
-public:
- // Constructor blocks all signals
- Blocker() {
- sigset_t new_mask;
- sigfillset(&new_mask);
- pthread_sigmask(SIG_BLOCK, &new_mask, &old_mask_);
- }
-
- // Destructor restores the previous signal mask
- ~Blocker() {
- pthread_sigmask(SIG_SETMASK, &old_mask_, 0);
- }
-
-private:
- // The previous signal mask
- sigset_t old_mask_;
-};
-
-}
-
-// The implementation of the Thread class.
-//
-// This internal state is not deleted until the thread terminates and is either
-// waited for or detached. We could do this with shared_ptr (or, shared_ptr and
-// weak_ptr), but we plan on compiling boost without thread support, so it
-// might not be safe. Therefore we use an explicit mutex. It is being locked
-// only 2-3 times in the lifetime of the thread, which should be negligible
-// overhead anyway.
-class Thread::Impl {
-public:
- Impl(const boost::function<void ()>& main) :
- // Two things to happen before destruction - thread needs to terminate
- // and the creating thread needs to release it.
- waiting_(2),
- main_(main),
- exception_(false),
- tid_(0)
- {}
- // Another of the waiting events is done. If there are no more, delete
- // impl.
- static void done(Impl* impl) {
- bool should_delete(false);
- { // We need to make sure the mutex is unlocked before it is deleted
- Mutex::Locker locker(impl->mutex_);
- if (--impl->waiting_ == 0) {
- should_delete = true;
- }
- }
- if (should_delete) {
- delete impl;
- }
- }
- // Run the thread. The type of parameter is because the pthread API.
- static void* run(void* impl_raw) {
- Impl* impl = static_cast<Impl*>(impl_raw);
- try {
- impl->main_();
- } catch (const exception& e) {
- impl->exception_ = true;
- impl->exception_text_ = e.what();
- } catch (...) {
- impl->exception_ = true;
- impl->exception_text_ = "Unknown exception";
- }
- done(impl);
- return (NULL);
- }
- // How many events are waiting? One is for the thread to finish, one
- // for the destructor of Thread or wait. Once both happen, this is
- // no longer needed.
- size_t waiting_;
- // The main function of the thread.
- boost::function<void ()> main_;
- // Was there an exception?
- bool exception_;
- string exception_text_;
- // The mutex protects the waiting_ member, which ensures there are
- // no race conditions and collisions when terminating. The other members
- // should be safe, because:
- // * tid_ is read only.
- // * exception_ and exception_text_ is accessed outside of the thread
- // only after join, by that time the thread must have terminated.
- // * main_ is used in a read-only way here. If there are any shared
- // resources used inside, it is up to the main_ itself to take care.
- Mutex mutex_;
- // Which thread are we talking about anyway?
- pthread_t tid_;
-};
-
-Thread::Thread(const boost::function<void ()>& main) :
- impl_(NULL)
-{
- unique_ptr<Impl> impl(new Impl(main));
- Blocker blocker;
- const int result = pthread_create(&impl->tid_, NULL, &Impl::run,
- impl.get());
- // Any error here?
- switch (result) {
- case 0: // All 0K
- impl_ = impl.release();
- break;
- case EAGAIN:
- throw std::bad_alloc();
- default: // Other errors. They should not happen.
- isc_throw(isc::InvalidOperation, std::strerror(result));
- }
-}
-
-Thread::~Thread() {
- if (impl_ != NULL) {
- // In case we didn't call wait yet
- const int result = pthread_detach(impl_->tid_);
- Impl::done(impl_);
- impl_ = NULL;
- // If the detach ever fails, something is screwed rather badly.
- assert(result == 0);
- }
-}
-
-void
-Thread::wait() {
- if (impl_ == NULL) {
- isc_throw(isc::InvalidOperation,
- "Wait called and no thread to wait for");
- }
-
- const int result = pthread_join(impl_->tid_, NULL);
- if (result != 0) {
- isc_throw(isc::InvalidOperation, std::strerror(result));
- }
-
- // Was there an exception in the thread?
- scoped_ptr<UncaughtException> ex;
- // Something here could in theory throw. But we already terminated the thread, so
- // we need to make sure we are in consistent state even in such situation (like
- // releasing the mutex and impl_).
- try {
- if (impl_->exception_) {
- ex.reset(new UncaughtException(__FILE__, __LINE__,
- impl_->exception_text_.c_str()));
- }
- } catch (...) {
- Impl::done(impl_);
- impl_ = NULL;
- // We have eaten the UncaughtException by now, but there's another
- // exception instead, so we have at least something.
- throw;
- }
-
- Impl::done(impl_);
- impl_ = NULL;
- if (ex.get() != NULL) {
- throw UncaughtException(*ex);
- }
-}
-
-}
-}
-}
+++ /dev/null
-// Copyright (C) 2012-2018 Internet Systems Consortium, Inc. ("ISC")
-//
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-#ifndef KEA_THREAD_H
-#define KEA_THREAD_H
-
-#include <exceptions/exceptions.h>
-
-#include <boost/noncopyable.hpp>
-#include <boost/function.hpp>
-
-namespace isc {
-namespace util {
-/// \brief Wrappers for thread related functionality
-///
-/// We provide our own wrappers, currently around pthreads. We tried using
-/// the boost thread support, but it gave us some trouble, so we implemented
-/// in-house ones.
-namespace thread {
-
-/// \brief A separate thread.
-///
-/// A thread of execution. When created, starts running in the background.
-/// You can wait for it then or just forget it ever existed and leave it
-/// live peacefully.
-///
-/// The interface is minimalist for now. We may need to extend it later.
-///
-/// \note While the objects of this class represent another thread, they
-/// are not thread-safe. You're not supposed to call wait() on the same
-/// object from multiple threads or so. They are reentrant (you can
-/// wait for different threads from different threads).
-class Thread : public boost::noncopyable {
-public:
- /// \brief There's an uncaught exception in a thread.
- ///
- /// When a thread terminates because it the main function of the thread
- /// throws, this one is re-thrown out of wait() and contains the what
- /// of the original exception.
- class UncaughtException : public isc::Exception {
- public:
- UncaughtException(const char* file, size_t line, const char* what) :
- Exception(file, line, what)
- {}
- };
-
- /// \brief Create and start a thread.
- ///
- /// Create a new thread and run body inside it.
- ///
- /// If you need to pass parameters to body, or return some result, you
- /// may just want to use boost::bind or alike to store them within the
- /// body functor.
- ///
- /// \note The main functor will be copied internally. You need to consider
- /// this when returning the result.
- ///
- /// The body should terminate by exiting the function. If it throws, it
- /// is considered an error. You should generally catch any exceptions form
- /// within there and handle them somehow.
- ///
- /// \param main The code to run inside the thread.
- ///
- /// \throw std::bad_alloc if allocation of the new thread or other
- /// resources fails.
- /// \throw isc::InvalidOperation for other errors (should not happen).
- Thread(const boost::function<void()>& main);
-
- /// \brief Destructor.
- ///
- /// It is completely legitimate to destroy the thread without calling
- /// wait() before. In such case, the thread will just live on until it
- /// terminates. However, if the thread dies due to exception, for example,
- /// it's up to you to detect that, no error is reported from this class.
- ///
- /// \throw isc::InvalidOperation in the rare case of OS reporting a
- /// problem. This should not happen unless you messed up with the raw
- /// thread by the low-level API.
- ~Thread();
-
- /// \brief Wait for the thread to terminate.
- ///
- /// Waits until the thread terminates. Must be called at most once.
- ///
- /// \throw isc::InvalidOperation if the OS API returns error. This usually
- /// mean a programmer error (like two threads trying to wait on each
- /// other).
- /// \throw isc::InvalidOperation calling wait a second time.
- /// \throw UncaughtException if the thread terminated by throwing an
- /// exception instead of just returning from the function.
- void wait();
-private:
- class Impl;
- Impl* impl_;
-};
-
-/// @brief Thread pointer type.
-typedef boost::shared_ptr<Thread> ThreadPtr;
-
-}
-}
-}
-
-#endif