diff --git a/Include/httpClient/pal.h b/Include/httpClient/pal.h index af94bd93..7919636b 100644 --- a/Include/httpClient/pal.h +++ b/Include/httpClient/pal.h @@ -13,6 +13,7 @@ #include #include #include +#include #include @@ -494,3 +495,23 @@ enum class HCWebSocketCloseStatus : uint32_t }; } + +// On some platforms, std::mutex default construction creates named mutexes which have a low +// system-wide limit. DefaultUnnamedMutex forces unnamed mutex construction on affected platforms. +// Define HC_USE_UNNAMED_MUTEX in your platform build props to enable the workaround. +#if defined(HC_USE_UNNAMED_MUTEX) +class DefaultUnnamedMutex : public std::mutex +{ +public: + DefaultUnnamedMutex() noexcept : std::mutex(nullptr) {} + ~DefaultUnnamedMutex() noexcept = default; + DefaultUnnamedMutex(DefaultUnnamedMutex const&) = delete; + DefaultUnnamedMutex& operator=(DefaultUnnamedMutex const&) = delete; + void lock() { std::mutex::lock(); } + bool try_lock() { return std::mutex::try_lock(); } + void unlock() { std::mutex::unlock(); } + native_handle_type native_handle() { return std::mutex::native_handle(); } +}; +#else +using DefaultUnnamedMutex = std::mutex; +#endif diff --git a/Source/Global/NetworkState.h b/Source/Global/NetworkState.h index bfd5364b..66b1abfa 100644 --- a/Source/Global/NetworkState.h +++ b/Source/Global/NetworkState.h @@ -76,7 +76,7 @@ class NetworkState static void CALLBACK HttpProviderCleanupComplete(XAsyncBlock* async); bool ScheduleCleanup(); - std::mutex m_mutex; + DefaultUnnamedMutex m_mutex; UniquePtr m_httpProvider; diff --git a/Source/Global/global.cpp b/Source/Global/global.cpp index 63c0375a..2b3e84b8 100644 --- a/Source/Global/global.cpp +++ b/Source/Global/global.cpp @@ -25,11 +25,11 @@ HRESULT http_singleton::singleton_access( _Out_ std::shared_ptr& singleton ) noexcept { - static std::mutex s_mutex; + static DefaultUnnamedMutex s_mutex; static std::shared_ptr s_singleton{ nullptr }; static uint8_t s_useCount{ 0 }; - std::lock_guard lock{ s_mutex }; + std::lock_guard lock{ s_mutex }; switch (mode) { case singleton_access_mode::create: diff --git a/Source/Task/AsyncLib.cpp b/Source/Task/AsyncLib.cpp index 17d5389d..023d07a8 100644 --- a/Source/Task/AsyncLib.cpp +++ b/Source/Task/AsyncLib.cpp @@ -45,7 +45,7 @@ struct AsyncState XAsyncBlock providerAsyncBlock { }; XAsyncBlock* userAsyncBlock = nullptr; XTaskQueueHandle queue = nullptr; - std::mutex waitMutex; + DefaultUnnamedMutex waitMutex; std::condition_variable waitCondition; bool waitSatisfied = false; diff --git a/Source/Task/AtomicVector.h b/Source/Task/AtomicVector.h index 5a2a0f20..3227ff75 100644 --- a/Source/Task/AtomicVector.h +++ b/Source/Task/AtomicVector.h @@ -94,7 +94,7 @@ class AtomicVector private: - std::mutex m_lock; + DefaultUnnamedMutex m_lock; std::vector m_buffers[2]; std::atomic m_indexAndRef { 0 }; }; diff --git a/Source/Task/TaskQueueImpl.h b/Source/Task/TaskQueueImpl.h index 6525d66f..7e4bfa5b 100644 --- a/Source/Task/TaskQueueImpl.h +++ b/Source/Task/TaskQueueImpl.h @@ -114,7 +114,7 @@ class SubmitCallback }; std::atomic m_nextToken{ 0 }; - std::mutex m_lock; + DefaultUnnamedMutex m_lock; CallbackRegistration m_buffer1[SUBMIT_CALLBACK_MAX]; CallbackRegistration m_buffer2[SUBMIT_CALLBACK_MAX]; CallbackRegistration* m_buffers[2]= { m_buffer1, m_buffer2 }; @@ -149,7 +149,7 @@ class QueueWaitRegistry std::atomic m_nextToken{ 0 }; StaticArray m_callbacks; - std::mutex m_lock; + DefaultUnnamedMutex m_lock; }; class TaskQueuePortImpl: public Api @@ -257,12 +257,12 @@ class TaskQueuePortImpl: public Api std::atomic m_processingSerializedTbCallback{ 0 }; std::atomic m_processingCallback{ 0 }; std::condition_variable m_processingCallbackCv; - std::mutex m_lock; + DefaultUnnamedMutex m_lock; std::unique_ptr> m_queueList; std::unique_ptr> m_pendingList; std::unique_ptr> m_terminationList; std::unique_ptr> m_pendingTerminationList; - std::mutex m_terminationLock; + DefaultUnnamedMutex m_terminationLock; OS::WaitTimer m_timer; OS::ThreadPool m_threadPool; std::atomic m_timerDue = { UINT64_MAX }; @@ -465,7 +465,7 @@ class TaskQueueImpl : public Api struct TerminationData { bool terminated; - std::mutex lock; + DefaultUnnamedMutex lock; std::condition_variable cv; }; diff --git a/Source/Task/ThreadPool_stl.cpp b/Source/Task/ThreadPool_stl.cpp index dc7443bf..a429e3e9 100644 --- a/Source/Task/ThreadPool_stl.cpp +++ b/Source/Task/ThreadPool_stl.cpp @@ -220,12 +220,12 @@ namespace OS std::atomic m_refs{ 1 }; - std::mutex m_wakeLock; + DefaultUnnamedMutex m_wakeLock; std::condition_variable m_wake; uint32_t m_calls{ 0 }; bool m_terminate{ false }; - std::mutex m_activeLock; + DefaultUnnamedMutex m_activeLock; std::condition_variable m_active; uint32_t m_activeCalls{ 0 }; diff --git a/Source/Task/WaitTimer_stl.cpp b/Source/Task/WaitTimer_stl.cpp index 5f87d256..f4e098c7 100644 --- a/Source/Task/WaitTimer_stl.cpp +++ b/Source/Task/WaitTimer_stl.cpp @@ -53,7 +53,7 @@ namespace OS TimerEntry const& Peek() const noexcept; TimerEntry Pop() noexcept; - std::mutex m_mutex; + DefaultUnnamedMutex m_mutex; std::condition_variable m_cv; std::vector m_queue; // used as a heap std::thread m_t; @@ -64,7 +64,7 @@ namespace OS namespace { std::shared_ptr g_timerQueue; - std::mutex g_timerQueueMutex; + DefaultUnnamedMutex g_timerQueueMutex; } TimerQueue::~TimerQueue() diff --git a/Source/WebSocket/hcwebsocket.h b/Source/WebSocket/hcwebsocket.h index de1249b6..daee11c0 100644 --- a/Source/WebSocket/hcwebsocket.h +++ b/Source/WebSocket/hcwebsocket.h @@ -181,7 +181,7 @@ class WebSocket : public std::enable_shared_from_this void* context{ nullptr }; }; - std::mutex m_stateMutex; + DefaultUnnamedMutex m_stateMutex; enum class State { Initial,