summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan Goodliffe <dan@randomdan.homeip.net>2021-10-10 18:41:14 +0100
committerDan Goodliffe <dan@randomdan.homeip.net>2021-10-10 18:41:14 +0100
commit7a080c917cc6128d46f3eca94413990cc007669d (patch)
treeb69bcbf2a6208f6e5da145eb3fca299cebe548b8
parentAdd stub header for all header only files (diff)
downloadlibadhocutil-7a080c917cc6128d46f3eca94413990cc007669d.tar.bz2
libadhocutil-7a080c917cc6128d46f3eca94413990cc007669d.tar.xz
libadhocutil-7a080c917cc6128d46f3eca94413990cc007669d.zip
Build with an STL semaphore when available
-rw-r--r--libadhocutil/lockHelpers.h2
-rw-r--r--libadhocutil/polyfill-semaphore.cpp (renamed from libadhocutil/semaphore.cpp)15
-rw-r--r--libadhocutil/polyfill-semaphore.h52
-rw-r--r--libadhocutil/resourcePool.h22
-rw-r--r--libadhocutil/resourcePool.impl.h34
-rw-r--r--libadhocutil/semaphore.h36
-rw-r--r--libadhocutil/unittests/testCache.cpp2
-rw-r--r--libadhocutil/unittests/testResourcePool.cpp34
-rw-r--r--libadhocutil/unittests/testSemaphore.cpp50
9 files changed, 154 insertions, 93 deletions
diff --git a/libadhocutil/lockHelpers.h b/libadhocutil/lockHelpers.h
index d306f9c..3b3bda8 100644
--- a/libadhocutil/lockHelpers.h
+++ b/libadhocutil/lockHelpers.h
@@ -1,6 +1,8 @@
#ifndef ADHOCUTIL_LOCKHELPERS_H
#define ADHOCUTIL_LOCKHELPERS_H
+#include <mutex>
+
#define LIBADHOC_LOCK_CONCAT2(a, b) a##b
#define LIBADHOC_LOCK_CONCAT(a, b) LIBADHOC_LOCK_CONCAT2(a, b)
#define LIBADHOC_LOCK_WITHLINE(a) LIBADHOC_LOCK_CONCAT(a, __LINE__)
diff --git a/libadhocutil/semaphore.cpp b/libadhocutil/polyfill-semaphore.cpp
index e6ec7d0..efe5be6 100644
--- a/libadhocutil/semaphore.cpp
+++ b/libadhocutil/polyfill-semaphore.cpp
@@ -1,11 +1,11 @@
-#include "semaphore.h"
+#include "polyfill-semaphore.h"
#include <chrono>
namespace AdHoc {
- Semaphore::Semaphore(std::size_t initial) : count(initial) { }
+ Semaphore::Semaphore(std::ptrdiff_t initial) : count(initial) { }
void
- Semaphore::notify()
+ Semaphore::release()
{
std::scoped_lock lock(mutex);
++count;
@@ -13,7 +13,7 @@ namespace AdHoc {
}
void
- Semaphore::wait()
+ Semaphore::acquire()
{
std::unique_lock lock(mutex);
while (!count) {
@@ -23,12 +23,11 @@ namespace AdHoc {
}
bool
- Semaphore::wait(unsigned int timeout)
+ Semaphore::try_acquire_for(std::chrono::milliseconds timeout)
{
- const auto expiry = std::chrono::milliseconds(timeout);
std::unique_lock lock(mutex);
while (!count) {
- if (condition.wait_for(lock, expiry) == std::cv_status::timeout) {
+ if (condition.wait_for(lock, timeout) == std::cv_status::timeout) {
return false;
}
}
@@ -36,7 +35,7 @@ namespace AdHoc {
return true;
}
- std::size_t
+ std::ptrdiff_t
Semaphore::freeCount() const
{
return count;
diff --git a/libadhocutil/polyfill-semaphore.h b/libadhocutil/polyfill-semaphore.h
new file mode 100644
index 0000000..273d0ec
--- /dev/null
+++ b/libadhocutil/polyfill-semaphore.h
@@ -0,0 +1,52 @@
+#ifndef ADHOCUTIL_SEMAPHORE_H
+#define ADHOCUTIL_SEMAPHORE_H
+
+// Borrowed from StackOverflow
+// http://stackoverflow.com/questions/4792449/c0x-has-no-semaphores-how-to-synchronize-threads
+
+#include "visibility.h"
+#include <condition_variable>
+#include <cstddef>
+#include <mutex>
+
+namespace AdHoc {
+ /// A portable semaphore with timeout support
+ class DLL_PUBLIC Semaphore {
+ public:
+ /// Construct a new semaphore with optional initial count.
+ explicit Semaphore(std::ptrdiff_t initial);
+
+ /// Notify one waiting thread.
+ void release();
+ /// Wait for a single count.
+ void acquire();
+ /// Wait for a single count with timeout.
+ /// @param ms Timeout how long to wait.
+ bool try_acquire_for(std::chrono::milliseconds ms);
+ /// Wait for a single count with timeout.
+ /// @param ms Timeout how long to wait.
+ bool
+ try_acquire_for(unsigned int ms)
+ {
+ return try_acquire_for(std::chrono::milliseconds(ms));
+ }
+ /// Wait for a single count with timeout.
+ /// @param ms Timeout how long to wait.
+ template<class Rep, class Period>
+ bool
+ try_acquire_for(const std::chrono::duration<Rep, Period> & rel_time)
+ {
+ return std::chrono::duration_cast<std::chrono::milliseconds>(rel_time);
+ }
+
+ /// Free
+ [[nodiscard]] std::ptrdiff_t freeCount() const;
+
+ private:
+ std::mutex mutex;
+ std::condition_variable condition;
+ std::ptrdiff_t count;
+ };
+}
+
+#endif
diff --git a/libadhocutil/resourcePool.h b/libadhocutil/resourcePool.h
index c7fed42..2fcaf6b 100644
--- a/libadhocutil/resourcePool.h
+++ b/libadhocutil/resourcePool.h
@@ -3,7 +3,11 @@
#include "c++11Helpers.h"
#include "exception.h"
-#include "semaphore.h"
+#ifdef __cpp_lib_semaphore
+# include <semaphore>
+#else
+# include "polyfill-semaphore.h"
+#endif
#include "visibility.h"
#include <cstddef>
#include <list>
@@ -15,6 +19,11 @@
#include <tuple>
namespace AdHoc {
+#ifdef __cpp_lib_semaphore
+ using SemaphoreType = std::counting_semaphore<>;
+#else
+ using SemaphoreType = Semaphore;
+#endif
template<typename Resource> class ResourcePool;
/// A handle to a resource allocated from a ResourcePool.
@@ -59,7 +68,7 @@ namespace AdHoc {
/// Create a new resource pool.
/// @param maxSize The upper limit of how many concurrent active resources there can be.
/// @param keep The number of resources to cache for reuse.
- ResourcePool(std::size_t maxSize, std::size_t keep);
+ ResourcePool(std::ptrdiff_t maxSize, std::size_t keep);
virtual ~ResourcePool();
/// Standard move/copy support
@@ -69,6 +78,9 @@ namespace AdHoc {
ResourceHandle<Resource> get();
/// Get a resource from the pool (with timeout on max size of pool)
/// @param ms Timeout in milliseconds.
+ ResourceHandle<Resource> get(const std::chrono::milliseconds ms);
+ /// Get a resource from the pool (with timeout on max size of pool)
+ /// @param ms Timeout in milliseconds.
ResourceHandle<Resource> get(unsigned int ms);
/// Get a new handle to the resource previous allocated to the current.
ResourceHandle<Resource> getMine();
@@ -79,8 +91,10 @@ namespace AdHoc {
std::size_t inUseCount() const;
/// Get number of available cached resources.
std::size_t availableCount() const;
+#ifndef __cpp_lib_semaphore
/// Get number of free slots.
- std::size_t freeCount() const;
+ std::ptrdiff_t freeCount() const;
+#endif
protected:
/// Create a new resource instance to add to the pool.
@@ -101,7 +115,7 @@ namespace AdHoc {
DLL_PRIVATE ResourceHandle<Resource> getOne();
mutable std::shared_mutex lock;
- Semaphore poolSize;
+ SemaphoreType poolSize;
std::size_t keep;
Available available;
InUse inUse;
diff --git a/libadhocutil/resourcePool.impl.h b/libadhocutil/resourcePool.impl.h
index eb34b22..5e89709 100644
--- a/libadhocutil/resourcePool.impl.h
+++ b/libadhocutil/resourcePool.impl.h
@@ -4,7 +4,11 @@
#include "lockHelpers.h"
#include "resourcePool.h" // IWYU pragma: export
#include "safeMapFind.h"
-#include "semaphore.h"
+#ifdef __cpp_lib_semaphore
+# include <semaphore>
+#else
+# include "polyfill-semaphore.h"
+#endif
#include <boost/assert.hpp>
#include <cstddef>
#include <exception>
@@ -114,7 +118,7 @@ namespace AdHoc {
// ResourcePool
//
- template<typename R> ResourcePool<R>::ResourcePool(std::size_t max, std::size_t k) : poolSize(max), keep(k) { }
+ template<typename R> ResourcePool<R>::ResourcePool(std::ptrdiff_t max, std::size_t k) : poolSize(max), keep(k) { }
template<typename R> ResourcePool<R>::~ResourcePool()
{
@@ -151,13 +155,15 @@ namespace AdHoc {
return available.size();
}
+#ifndef __cpp_lib_semaphore
template<typename R>
- std::size_t
+ std::ptrdiff_t
ResourcePool<R>::freeCount() const
{
SharedLock(lock);
return poolSize.freeCount();
}
+#endif
template<typename R>
ResourceHandle<R>
@@ -179,34 +185,41 @@ namespace AdHoc {
ResourceHandle<R>
ResourcePool<R>::get()
{
- poolSize.wait();
+ poolSize.acquire();
try {
return ResourceHandle(getOne());
}
catch (...) {
- poolSize.notify();
+ poolSize.release();
throw;
}
}
template<typename R>
ResourceHandle<R>
- ResourcePool<R>::get(unsigned int timeout)
+ ResourcePool<R>::get(const std::chrono::milliseconds timeout)
{
- if (!poolSize.wait(timeout)) {
+ if (!poolSize.try_acquire_for(timeout)) {
throw TimeOutOnResourcePoolT<R>();
}
try {
return getOne();
}
catch (...) {
- poolSize.notify();
+ poolSize.release();
throw;
}
}
template<typename R>
ResourceHandle<R>
+ ResourcePool<R>::get(unsigned int ms)
+ {
+ return get(std::chrono::milliseconds(ms));
+ }
+
+ template<typename R>
+ ResourceHandle<R>
ResourcePool<R>::getOne()
{
Lock(lock);
@@ -242,7 +255,7 @@ namespace AdHoc {
}
catch (...) {
}
- poolSize.notify();
+ poolSize.release();
}
template<typename R>
@@ -251,7 +264,7 @@ namespace AdHoc {
{
Lock(lock);
removeFrom(r, inUse);
- poolSize.notify();
+ poolSize.release();
}
template<typename R>
@@ -275,7 +288,6 @@ namespace AdHoc {
NoCurrentResourceT<R>::NoCurrentResourceT(const std::thread::id & id) : NoCurrentResource(id, typeid(R).name())
{
}
-
}
#endif
diff --git a/libadhocutil/semaphore.h b/libadhocutil/semaphore.h
deleted file mode 100644
index 8b03907..0000000
--- a/libadhocutil/semaphore.h
+++ /dev/null
@@ -1,36 +0,0 @@
-#ifndef ADHOCUTIL_SEMAPHORE_H
-#define ADHOCUTIL_SEMAPHORE_H
-
-// Borrowed from StackOverflow
-// http://stackoverflow.com/questions/4792449/c0x-has-no-semaphores-how-to-synchronize-threads
-
-#include "visibility.h"
-#include <condition_variable>
-#include <cstddef>
-#include <mutex>
-
-namespace AdHoc {
- /// A portable semaphore with timeout support
- class DLL_PUBLIC Semaphore {
- public:
- /// Construct a new semaphore with optional initial count.
- explicit Semaphore(std::size_t initial = 0);
-
- /// Notify one waiting thread.
- void notify();
- /// Wait for a single count.
- void wait();
- /// Wait for a single count with timeout.
- /// @param ms Timeout in milliseconds.
- bool wait(unsigned int ms);
- /// Free
- [[nodiscard]] std::size_t freeCount() const;
-
- private:
- std::mutex mutex;
- std::condition_variable condition;
- std::size_t count;
- };
-}
-
-#endif
diff --git a/libadhocutil/unittests/testCache.cpp b/libadhocutil/unittests/testCache.cpp
index c6ab6b4..0cdd0ea 100644
--- a/libadhocutil/unittests/testCache.cpp
+++ b/libadhocutil/unittests/testCache.cpp
@@ -8,12 +8,10 @@
#include <string>
#include <unistd.h>
-// NOLINTNEXTLINE(hicpp-special-member-functions)
class Obj {
public:
// cppcheck-suppress noExplicitConstructor; NOLINTNEXTLINE(hicpp-explicit-conversions)
Obj(int i) : v(i) { }
- void operator=(const Obj &) = delete;
bool
operator==(const int & i) const
{
diff --git a/libadhocutil/unittests/testResourcePool.cpp b/libadhocutil/unittests/testResourcePool.cpp
index baa965a..a5f06a2 100644
--- a/libadhocutil/unittests/testResourcePool.cpp
+++ b/libadhocutil/unittests/testResourcePool.cpp
@@ -4,7 +4,11 @@
#include "c++11Helpers.h"
#include "lockHelpers.h"
#include "resourcePool.impl.h"
-#include "semaphore.h"
+#ifdef __cpp_lib_semaphore
+# include <semaphore>
+#else
+# include "polyfill-semaphore.h"
+#endif
#include <atomic>
#include <list>
#include <map>
@@ -330,21 +334,21 @@ BOOST_AUTO_TEST_CASE(threading1, *boost::unit_test::timeout(10))
}
static void
-acquireAndKeepFor1Second(TRPSmall * pool, AdHoc::Semaphore & s)
+acquireAndKeepFor1Second(TRPSmall * pool, AdHoc::SemaphoreType & s)
{
auto r = pool->get();
static std::mutex m;
ScopeLock(m) {
BOOST_CHECK(r);
}
- s.notify();
+ s.release();
sleep(1);
}
BOOST_AUTO_TEST_CASE(threading2)
{
TRPSmall pool;
- AdHoc::Semaphore s;
+ AdHoc::SemaphoreType s {0};
std::thread t1([&pool, &s]() {
acquireAndKeepFor1Second(&pool, s);
});
@@ -355,9 +359,9 @@ BOOST_AUTO_TEST_CASE(threading2)
acquireAndKeepFor1Second(&pool, s);
});
- s.wait();
- s.wait();
- s.wait();
+ s.acquire();
+ s.acquire();
+ s.acquire();
BOOST_REQUIRE_THROW(pool.get(100), AdHoc::TimeOutOnResourcePoolT<MockResource>);
BOOST_REQUIRE_EQUAL(3, pool.inUseCount());
@@ -416,20 +420,32 @@ BOOST_AUTO_TEST_CASE(createFail)
BOOST_REQUIRE_EQUAL(0, MockResource::count);
BOOST_REQUIRE_EQUAL(0, pool.availableCount());
BOOST_REQUIRE_EQUAL(0, pool.inUseCount());
+#ifndef __cpp_lib_semaphore
BOOST_REQUIRE_EQUAL(3, pool.freeCount());
+#endif
BOOST_REQUIRE_THROW(pool.get(), std::exception);
+#ifndef __cpp_lib_semaphore
BOOST_REQUIRE_EQUAL(3, pool.freeCount());
+#endif
BOOST_REQUIRE_THROW(pool.get(0), std::exception);
+#ifndef __cpp_lib_semaphore
BOOST_REQUIRE_EQUAL(3, pool.freeCount());
+#endif
BOOST_REQUIRE_THROW(pool.get(100), std::exception);
+#ifndef __cpp_lib_semaphore
BOOST_REQUIRE_EQUAL(3, pool.freeCount());
+#endif
BOOST_REQUIRE_EQUAL(0, MockResource::count);
BOOST_REQUIRE_EQUAL(0, pool.availableCount());
BOOST_REQUIRE_EQUAL(0, pool.inUseCount());
BOOST_REQUIRE_THROW(pool.get(), std::exception);
+#ifndef __cpp_lib_semaphore
BOOST_REQUIRE_EQUAL(3, pool.freeCount());
+#endif
BOOST_REQUIRE_THROW(pool.get(), std::exception);
+#ifndef __cpp_lib_semaphore
BOOST_REQUIRE_EQUAL(3, pool.freeCount());
+#endif
}
BOOST_AUTO_TEST_CASE(returnFail)
@@ -440,11 +456,15 @@ BOOST_AUTO_TEST_CASE(returnFail)
BOOST_CHECK(rh);
BOOST_REQUIRE_EQUAL(0, pool.availableCount());
BOOST_REQUIRE_EQUAL(1, pool.inUseCount());
+#ifndef __cpp_lib_semaphore
BOOST_REQUIRE_EQUAL(2, pool.freeCount());
+#endif
}
BOOST_REQUIRE_EQUAL(0, pool.availableCount());
BOOST_REQUIRE_EQUAL(0, pool.inUseCount());
+#ifndef __cpp_lib_semaphore
BOOST_REQUIRE_EQUAL(3, pool.freeCount());
+#endif
}
BOOST_AUTO_TEST_CASE(exception_msgs)
diff --git a/libadhocutil/unittests/testSemaphore.cpp b/libadhocutil/unittests/testSemaphore.cpp
index 0de899e..999cff3 100644
--- a/libadhocutil/unittests/testSemaphore.cpp
+++ b/libadhocutil/unittests/testSemaphore.cpp
@@ -1,66 +1,66 @@
#define BOOST_TEST_MODULE Semaphore
#include <boost/test/unit_test.hpp>
-#include <semaphore.h>
+#include <polyfill-semaphore.h>
#include <thread>
#include <unistd.h>
BOOST_AUTO_TEST_CASE(addRemoveOne)
{
- AdHoc::Semaphore s;
- s.notify();
- s.wait();
+ AdHoc::Semaphore s {0};
+ s.release();
+ s.acquire();
}
BOOST_AUTO_TEST_CASE(initial)
{
AdHoc::Semaphore s(2);
- s.wait();
- s.wait();
+ s.acquire();
+ s.acquire();
// cppcheck-suppress assertWithSideEffect
- BOOST_REQUIRE_EQUAL(false, s.wait(0));
+ BOOST_REQUIRE_EQUAL(false, s.try_acquire_for(0));
}
BOOST_AUTO_TEST_CASE(addRemoveSome)
{
- AdHoc::Semaphore s;
+ AdHoc::Semaphore s {0};
BOOST_REQUIRE_EQUAL(0, s.freeCount());
- s.notify();
+ s.release();
BOOST_REQUIRE_EQUAL(1, s.freeCount());
- s.notify();
+ s.release();
BOOST_REQUIRE_EQUAL(2, s.freeCount());
- s.notify();
+ s.release();
BOOST_REQUIRE_EQUAL(3, s.freeCount());
- s.wait();
+ s.acquire();
BOOST_REQUIRE_EQUAL(2, s.freeCount());
- s.wait();
+ s.acquire();
BOOST_REQUIRE_EQUAL(1, s.freeCount());
- s.wait();
+ s.acquire();
BOOST_REQUIRE_EQUAL(0, s.freeCount());
}
BOOST_AUTO_TEST_CASE(addRemoveTimeOut)
{
- AdHoc::Semaphore s;
- s.notify();
- s.wait();
+ AdHoc::Semaphore s {0};
+ s.release();
+ s.acquire();
// cppcheck-suppress assertWithSideEffect
- BOOST_REQUIRE_EQUAL(false, s.wait(100));
+ BOOST_REQUIRE_EQUAL(false, s.try_acquire_for(100));
// cppcheck-suppress assertWithSideEffect
- BOOST_REQUIRE_EQUAL(false, s.wait(0));
+ BOOST_REQUIRE_EQUAL(false, s.try_acquire_for(0));
}
BOOST_AUTO_TEST_CASE(addRemoveWait)
{
- AdHoc::Semaphore s;
- s.notify();
- s.wait();
+ AdHoc::Semaphore s {0};
+ s.release();
+ s.acquire();
std::thread th([&s]() {
usleep(100000);
- s.notify();
+ s.release();
});
// cppcheck-suppress assertWithSideEffect
- BOOST_REQUIRE_EQUAL(false, s.wait(1));
- s.wait();
+ BOOST_REQUIRE_EQUAL(false, s.try_acquire_for(1));
+ s.acquire();
th.join();
}