From 016175ea3992a796142d58a3df17a06bd7a7a256 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Sat, 16 Oct 2021 12:58:38 +0100 Subject: Refactored semaphore/resource pool --- libadhocutil/polyfill-semaphore.h | 1 + libadhocutil/resourcePool.cpp | 30 ++++++++++++++++ libadhocutil/resourcePool.h | 55 ++++++++++++++++++----------- libadhocutil/resourcePool.impl.h | 31 ++++------------ libadhocutil/unittests/testResourcePool.cpp | 30 ++-------------- 5 files changed, 75 insertions(+), 72 deletions(-) diff --git a/libadhocutil/polyfill-semaphore.h b/libadhocutil/polyfill-semaphore.h index 273d0ec..9279b26 100644 --- a/libadhocutil/polyfill-semaphore.h +++ b/libadhocutil/polyfill-semaphore.h @@ -5,6 +5,7 @@ // http://stackoverflow.com/questions/4792449/c0x-has-no-semaphores-how-to-synchronize-threads #include "visibility.h" +#include #include #include #include diff --git a/libadhocutil/resourcePool.cpp b/libadhocutil/resourcePool.cpp index cc2cec0..5e9bbb0 100644 --- a/libadhocutil/resourcePool.cpp +++ b/libadhocutil/resourcePool.cpp @@ -1,7 +1,37 @@ #include "resourcePool.h" #include "compileTimeFormatter.h" +#ifdef __cpp_lib_semaphore +# include +#else +# include "polyfill-semaphore.h" +#endif namespace AdHoc { + ResourcePoolBase::ResourcePoolBase(std::ptrdiff_t maxSize, std::size_t keep_) : + keep {keep_}, poolSize {std::make_unique(maxSize)} + { + } + + void + ResourcePoolBase::release() + { + poolSize->release(); + } + + void + ResourcePoolBase::acquire() + { + poolSize->acquire(); + } + + bool + ResourcePoolBase::try_acquire_for(std::chrono::milliseconds timeout) + { + return poolSize->try_acquire_for(timeout); + } + + ResourcePoolBase::~ResourcePoolBase() = default; + TimeOutOnResourcePool::TimeOutOnResourcePool(const char * const n) : name(n) { } AdHocFormatter(TimeOutOnResourcePoolMsg, "Timeout getting a resource from pool of %?"); diff --git a/libadhocutil/resourcePool.h b/libadhocutil/resourcePool.h index 2fcaf6b..1a29dc9 100644 --- a/libadhocutil/resourcePool.h +++ b/libadhocutil/resourcePool.h @@ -5,10 +5,9 @@ #include "exception.h" #ifdef __cpp_lib_semaphore # include -#else -# include "polyfill-semaphore.h" #endif #include "visibility.h" +#include #include #include #include @@ -17,13 +16,9 @@ #include #include #include +// IWYU pragma: no_include "polyfill-semaphore.h" namespace AdHoc { -#ifdef __cpp_lib_semaphore - using SemaphoreType = std::counting_semaphore<>; -#else - using SemaphoreType = Semaphore; -#endif template class ResourcePool; /// A handle to a resource allocated from a ResourcePool. @@ -59,20 +54,45 @@ namespace AdHoc { std::shared_ptr resource; }; + class DLL_PUBLIC ResourcePoolBase { + public: + /// 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. + ResourcePoolBase(std::ptrdiff_t maxSize, std::size_t keep); + virtual ~ResourcePoolBase(); + + /// Standard move/copy support + SPECIAL_MEMBERS_DELETE(ResourcePoolBase); + + void acquire(); + bool try_acquire_for(std::chrono::milliseconds); + void release(); + +#ifdef __cpp_lib_semaphore + using SemaphoreType = std::counting_semaphore<>; +#else + using SemaphoreType = class Semaphore; +#endif + protected: + mutable std::shared_mutex lock; + std::size_t keep; + + private: + std::unique_ptr poolSize; + }; + /// A fully featured resource pool for sharing and reusing a finite set of /// resources, possibly across multiple threads. - template class DLL_PUBLIC ResourcePool { + template class DLL_PUBLIC ResourcePool : ResourcePoolBase { public: friend class ResourceHandle; - /// 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::ptrdiff_t maxSize, std::size_t keep); - virtual ~ResourcePool(); + using ResourcePoolBase::ResourcePoolBase; + ~ResourcePool() override; /// Standard move/copy support - SPECIAL_MEMBERS_DEFAULT_MOVE_NO_COPY(ResourcePool); + SPECIAL_MEMBERS_DELETE(ResourcePool); /// Get a resource from the pool (maybe cached, maybe constructed afresh) ResourceHandle get(); @@ -91,10 +111,6 @@ 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::ptrdiff_t freeCount() const; -#endif protected: /// Create a new resource instance to add to the pool. @@ -114,9 +130,6 @@ namespace AdHoc { DLL_PRIVATE static void removeFrom(const std::shared_ptr &, InUse &); DLL_PRIVATE ResourceHandle getOne(); - mutable std::shared_mutex lock; - SemaphoreType poolSize; - std::size_t keep; Available available; InUse inUse; }; diff --git a/libadhocutil/resourcePool.impl.h b/libadhocutil/resourcePool.impl.h index e1791be..f2a62b4 100644 --- a/libadhocutil/resourcePool.impl.h +++ b/libadhocutil/resourcePool.impl.h @@ -4,12 +4,8 @@ #include "lockHelpers.h" #include "resourcePool.h" // IWYU pragma: export #include "safeMapFind.h" -#ifdef __cpp_lib_semaphore -# include -#else -# include "polyfill-semaphore.h" -#endif #include +#include #include #include #include @@ -118,9 +114,6 @@ namespace AdHoc { // // ResourcePool // - - template ResourcePool::ResourcePool(std::ptrdiff_t max, std::size_t k) : poolSize(max), keep(k) { } - template ResourcePool::~ResourcePool() { for (auto & r : inUse) { @@ -156,16 +149,6 @@ namespace AdHoc { return available.size(); } -#ifndef __cpp_lib_semaphore - template - std::ptrdiff_t - ResourcePool::freeCount() const - { - SharedLock(lock); - return poolSize.freeCount(); - } -#endif - template ResourceHandle ResourcePool::getMine() @@ -186,12 +169,12 @@ namespace AdHoc { ResourceHandle ResourcePool::get() { - poolSize.acquire(); + acquire(); try { return ResourceHandle(getOne()); } catch (...) { - poolSize.release(); + release(); throw; } } @@ -200,14 +183,14 @@ namespace AdHoc { ResourceHandle ResourcePool::get(const std::chrono::milliseconds timeout) { - if (!poolSize.try_acquire_for(timeout)) { + if (!try_acquire_for(timeout)) { throw TimeOutOnResourcePoolT(); } try { return getOne(); } catch (...) { - poolSize.release(); + release(); throw; } } @@ -256,7 +239,7 @@ namespace AdHoc { } catch (...) { } - poolSize.release(); + release(); } template @@ -265,7 +248,7 @@ namespace AdHoc { { Lock(lock); removeFrom(r, inUse); - poolSize.release(); + release(); } template diff --git a/libadhocutil/unittests/testResourcePool.cpp b/libadhocutil/unittests/testResourcePool.cpp index a5f06a2..cecfb46 100644 --- a/libadhocutil/unittests/testResourcePool.cpp +++ b/libadhocutil/unittests/testResourcePool.cpp @@ -7,7 +7,7 @@ #ifdef __cpp_lib_semaphore # include #else -# include "polyfill-semaphore.h" +# include "polyfill-semaphore.h" // IWYU pragma: keep #endif #include #include @@ -334,7 +334,7 @@ BOOST_AUTO_TEST_CASE(threading1, *boost::unit_test::timeout(10)) } static void -acquireAndKeepFor1Second(TRPSmall * pool, AdHoc::SemaphoreType & s) +acquireAndKeepFor1Second(TRPSmall * pool, AdHoc::ResourcePoolBase::SemaphoreType & s) { auto r = pool->get(); static std::mutex m; @@ -348,7 +348,7 @@ acquireAndKeepFor1Second(TRPSmall * pool, AdHoc::SemaphoreType & s) BOOST_AUTO_TEST_CASE(threading2) { TRPSmall pool; - AdHoc::SemaphoreType s {0}; + AdHoc::ResourcePoolBase::SemaphoreType s {0}; std::thread t1([&pool, &s]() { acquireAndKeepFor1Second(&pool, s); }); @@ -420,32 +420,14 @@ 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) @@ -456,15 +438,9 @@ 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) -- cgit v1.2.3