From ab3733e38c8847564145a1ba183797f6d7c55db2 Mon Sep 17 00:00:00 2001 From: Gabriel Date: Wed, 7 Aug 2024 14:53:59 +0200 Subject: [PATCH] pthreads specific thread implementation to set stack size (#411) * first implementation of own wrapper around pthreads fixes github issue #410 * PThread wrapper uses interface similar to std::thread * also set stack guard size for debug builds * cleanup * move function call outside of CHECK() --- source/Lib/Utilities/NoMallocThreadPool.cpp | 90 +++++++++++++++++++-- source/Lib/Utilities/NoMallocThreadPool.h | 33 +++++++- source/Lib/vvenc/CMakeLists.txt | 4 + 3 files changed, 117 insertions(+), 10 deletions(-) diff --git a/source/Lib/Utilities/NoMallocThreadPool.cpp b/source/Lib/Utilities/NoMallocThreadPool.cpp index fb35de96e..ea0120c9c 100644 --- a/source/Lib/Utilities/NoMallocThreadPool.cpp +++ b/source/Lib/Utilities/NoMallocThreadPool.cpp @@ -47,11 +47,12 @@ POSSIBILITY OF SUCH DAMAGE. #include "NoMallocThreadPool.h" - -#if __linux -#include +#ifdef HAVE_PTHREADS +# include +# define THREAD_MIN_STACK_SIZE 1024 * 1024 #endif + //! \ingroup Utilities //! \{ @@ -63,12 +64,15 @@ thread_local std::unique_ptr ptls; NoMallocThreadPool::NoMallocThreadPool( int numThreads, const char * threadPoolName, const VVEncCfg* encCfg ) : m_poolName( threadPoolName ) - , m_threads ( numThreads < 0 ? std::thread::hardware_concurrency() : numThreads ) { - int tid = 0; - for( auto& t: m_threads ) + if( numThreads < 0 ) { - t = std::thread( &NoMallocThreadPool::threadProc, this, tid++, *encCfg ); + numThreads = std::thread::hardware_concurrency(); + } + + for( int i = 0; i < numThreads; ++i ) + { + m_threads.emplace_back( &NoMallocThreadPool::threadProc, this, i, *encCfg ); } } @@ -270,6 +274,78 @@ bool NoMallocThreadPool::processTask( int threadId, NoMallocThreadPool::Slot& ta return true; } +#ifdef HAVE_PTHREADS + +template +NoMallocThreadPool::PThread::PThread( TFunc&& func, TArgs&&... args ) +{ + using WrappedCall = std::function; + std::unique_ptr call = std::make_unique( std::bind( func, args... ) ); + + using PThreadsStartFn = void* (*) ( void* ); + PThreadsStartFn threadFn = []( void* p ) -> void* + { + std::unique_ptr call( static_cast( p ) ); + + ( *call )(); + + return nullptr; + }; + + pthread_attr_t attr; + int ret = pthread_attr_init( &attr ); + CHECK( ret != 0, "pthread_attr_init() failed" ); + + try + { + size_t currStackSize = 0; + ret = pthread_attr_getstacksize( &attr, &currStackSize ); + CHECK( ret != 0, "pthread_attr_getstacksize() failed" ); + + if( currStackSize < THREAD_MIN_STACK_SIZE ) + { + ret = pthread_attr_setstacksize( &attr, THREAD_MIN_STACK_SIZE ); + CHECK( ret != 0, "pthread_attr_setstacksize() failed" ); + +# ifdef _DEBUG + ret = pthread_attr_setguardsize( &attr, 1024 * 1024 ); // set stack guard size to 1MB to more reliably deteck stack overflows + CHECK( ret != 0, "pthread_attr_setguardsize() failed" ); +# endif + } + m_joinable = 0 == pthread_create( &m_id, &attr, threadFn, call.get() ); + CHECK( !m_joinable, "pthread_create() faild" ); + + call.release(); // will now be freed by the thread + + pthread_attr_destroy( &attr ); + } + catch( ... ) + { + pthread_attr_destroy( &attr ); + throw; + } +} + +NoMallocThreadPool::PThread& NoMallocThreadPool::PThread::operator=( PThread&& other ) +{ + m_id = other.m_id; + m_joinable = other.m_joinable; + other.m_id = 0; + other.m_joinable = false; + return *this; +} + +void NoMallocThreadPool::PThread::join() +{ + if( m_joinable ) + { + m_joinable = false; + pthread_join( m_id, nullptr ); + } +} + +#endif // HAVE_PTHREADS + } // namespace vvenc //! \} diff --git a/source/Lib/Utilities/NoMallocThreadPool.h b/source/Lib/Utilities/NoMallocThreadPool.h index 3a4d8e2db..60599cb38 100644 --- a/source/Lib/Utilities/NoMallocThreadPool.h +++ b/source/Lib/Utilities/NoMallocThreadPool.h @@ -50,7 +50,6 @@ POSSIBILITY OF SUCH DAMAGE. #include #include #include -#include #include #include "CommonLib/CommonDef.h" @@ -366,7 +365,6 @@ class NoMallocThreadPool Iterator grow() { std::unique_lock l( m_resizeMutex ); // prevent concurrent growth of the queue. Read access while growing is no problem -// std::cerr << __PRETTY_FUNCTION__ << std::endl; m_lastChunk->m_next = new Chunk( &m_firstChunk ); m_lastChunk = m_lastChunk->m_next; @@ -485,13 +483,42 @@ class NoMallocThreadPool #endif private: +#ifdef HAVE_PTHREADS + struct PThread + { + PThread() = default; + ~PThread() = default; + + PThread( const PThread& ) = delete; + PThread& operator=( const PThread& ) = delete; + + PThread( PThread&& other ) { *this = std::move( other ); }; + PThread& operator=( PThread&& other ); + + template + PThread( TFunc&& func, TArgs&&... args ); + + bool joinable() { return m_joinable; } + void join(); + + private: + pthread_t m_id = 0; + bool m_joinable = false; + }; +#endif // HAVE_PTHREADS + +#if HAVE_PTHREADS + using ThreadImpl = PThread; +#else + using ThreadImpl = std::thread; +#endif using TaskIterator = ChunkedTaskQueue::Iterator; // members std::string m_poolName; std::atomic_bool m_exitThreads{ false }; - std::vector m_threads; + std::vector m_threads; ChunkedTaskQueue m_tasks; TaskIterator m_nextFillSlot = m_tasks.begin(); #if ADD_TASK_THREAD_SAFE diff --git a/source/Lib/vvenc/CMakeLists.txt b/source/Lib/vvenc/CMakeLists.txt index 208023156..aca376b6f 100644 --- a/source/Lib/vvenc/CMakeLists.txt +++ b/source/Lib/vvenc/CMakeLists.txt @@ -170,6 +170,10 @@ else() target_link_libraries( ${LIB_NAME} PRIVATE Threads::Threads ) endif() +if( CMAKE_USE_PTHREADS_INIT ) + target_compile_definitions( ${LIB_NAME} PRIVATE HAVE_PTHREADS ) +endif() + # set the folder where to place the projects set_target_properties( ${LIB_NAME} PROPERTIES VERSION ${PROJECT_VERSION}