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();  } | 
