diff options
author | Dan Goodliffe <dan@randomdan.homeip.net> | 2021-10-10 18:41:14 +0100 |
---|---|---|
committer | Dan Goodliffe <dan@randomdan.homeip.net> | 2021-10-10 18:41:14 +0100 |
commit | 7a080c917cc6128d46f3eca94413990cc007669d (patch) | |
tree | b69bcbf2a6208f6e5da145eb3fca299cebe548b8 | |
parent | Add stub header for all header only files (diff) | |
download | libadhocutil-7a080c917cc6128d46f3eca94413990cc007669d.tar.bz2 libadhocutil-7a080c917cc6128d46f3eca94413990cc007669d.tar.xz libadhocutil-7a080c917cc6128d46f3eca94413990cc007669d.zip |
Build with an STL semaphore when available
-rw-r--r-- | libadhocutil/lockHelpers.h | 2 | ||||
-rw-r--r-- | libadhocutil/polyfill-semaphore.cpp (renamed from libadhocutil/semaphore.cpp) | 15 | ||||
-rw-r--r-- | libadhocutil/polyfill-semaphore.h | 52 | ||||
-rw-r--r-- | libadhocutil/resourcePool.h | 22 | ||||
-rw-r--r-- | libadhocutil/resourcePool.impl.h | 34 | ||||
-rw-r--r-- | libadhocutil/semaphore.h | 36 | ||||
-rw-r--r-- | libadhocutil/unittests/testCache.cpp | 2 | ||||
-rw-r--r-- | libadhocutil/unittests/testResourcePool.cpp | 34 | ||||
-rw-r--r-- | libadhocutil/unittests/testSemaphore.cpp | 50 |
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(); } |