diff --git a/src/platform/Linux/PlatformManagerImpl.cpp b/src/platform/Linux/PlatformManagerImpl.cpp index 68f3d58c1ac87b..dba3cb447d30d8 100644 --- a/src/platform/Linux/PlatformManagerImpl.cpp +++ b/src/platform/Linux/PlatformManagerImpl.cpp @@ -281,14 +281,14 @@ void PlatformManagerImpl::_Shutdown() } #if CHIP_DEVICE_CONFIG_WITH_GLIB_MAIN_LOOP -CHIP_ERROR PlatformManagerImpl::_GLibMatterContextInvokeSync(CHIP_ERROR (*func)(void *), void * userData) +void PlatformManagerImpl::_GLibMatterContextInvokeSync(LambdaBridge && bridge) { // Because of TSAN false positives, we need to use a mutex to synchronize access to all members of // the GLibMatterContextInvokeData object (including constructor and destructor). This is a temporary // workaround until TSAN-enabled GLib will be used in our CI. std::unique_lock lock(mGLibMainLoopCallbackIndirectionMutex); - GLibMatterContextInvokeData invokeData{ func, userData }; + GLibMatterContextInvokeData invokeData{ std::move(bridge) }; lock.unlock(); @@ -300,15 +300,11 @@ CHIP_ERROR PlatformManagerImpl::_GLibMatterContextInvokeSync(CHIP_ERROR (*func)( // XXX: Temporary workaround for TSAN false positives. std::unique_lock lock_(PlatformMgrImpl().mGLibMainLoopCallbackIndirectionMutex); - auto mFunc = data->mFunc; - auto mUserData = data->mFuncUserData; - lock_.unlock(); - auto result = mFunc(mUserData); + data->bridge(); lock_.lock(); - data->mDone = true; - data->mFuncResult = result; + data->mDone = true; data->mDoneCond.notify_one(); return G_SOURCE_REMOVE; @@ -316,10 +312,7 @@ CHIP_ERROR PlatformManagerImpl::_GLibMatterContextInvokeSync(CHIP_ERROR (*func)( &invokeData, nullptr); lock.lock(); - invokeData.mDoneCond.wait(lock, [&invokeData]() { return invokeData.mDone; }); - - return invokeData.mFuncResult; } #endif // CHIP_DEVICE_CONFIG_WITH_GLIB_MAIN_LOOP diff --git a/src/platform/Linux/PlatformManagerImpl.h b/src/platform/Linux/PlatformManagerImpl.h index 5669ce5dd28d62..d50687bf43420c 100644 --- a/src/platform/Linux/PlatformManagerImpl.h +++ b/src/platform/Linux/PlatformManagerImpl.h @@ -23,6 +23,7 @@ #pragma once +#include "lib/core/CHIPError.h" #include #include @@ -69,7 +70,21 @@ class PlatformManagerImpl final : public PlatformManager, public Internal::Gener template CHIP_ERROR GLibMatterContextInvokeSync(CHIP_ERROR (*func)(T *), T * userData) { - return _GLibMatterContextInvokeSync((CHIP_ERROR(*)(void *)) func, (void *) userData); + struct + { + CHIP_ERROR returnValue = CHIP_NO_ERROR; + CHIP_ERROR (*functionToCall)(T *); + T * userData; + } context; + + context.functionToCall = func; + context.userData = userData; + + LambdaBridge bridge; + bridge.Initialize([&context]() { context.returnValue = context.functionToCall(context.userData); }); + + _GLibMatterContextInvokeSync(std::move(bridge)); + return context.returnValue; } unsigned int GLibMatterContextAttachSource(GSource * source) @@ -102,9 +117,7 @@ class PlatformManagerImpl final : public PlatformManager, public Internal::Gener struct GLibMatterContextInvokeData { - CHIP_ERROR (*mFunc)(void *); - void * mFuncUserData; - CHIP_ERROR mFuncResult; + LambdaBridge bridge; // Sync primitives to wait for the function to be executed std::condition_variable mDoneCond; bool mDone = false; @@ -113,10 +126,13 @@ class PlatformManagerImpl final : public PlatformManager, public Internal::Gener /** * @brief Invoke a function on the Matter GLib context. * - * @note This function does not provide type safety for the user data. Please, - * use the GLibMatterContextInvokeSync() template function instead. + * @param[in] bridge a LambdaBridge object that holds the lambda to be invoked within the GLib context. + * + * @note This function moves the LambdaBridge into the GLib context for invocation. + * The LambdaBridge is created and initialised in GLibMatterContextInvokeSync(). + * use the GLibMatterContextInvokeSync() template function instead of this one. */ - CHIP_ERROR _GLibMatterContextInvokeSync(CHIP_ERROR (*func)(void *), void * userData); + void _GLibMatterContextInvokeSync(LambdaBridge && bridge); // XXX: Mutex for guarding access to glib main event loop callback indirection // synchronization primitives. This is a workaround to suppress TSAN warnings. diff --git a/src/platform/NuttX/PlatformManagerImpl.cpp b/src/platform/NuttX/PlatformManagerImpl.cpp index d590f58c343232..a44f0846e6522f 100644 --- a/src/platform/NuttX/PlatformManagerImpl.cpp +++ b/src/platform/NuttX/PlatformManagerImpl.cpp @@ -281,14 +281,14 @@ void PlatformManagerImpl::_Shutdown() } #if CHIP_DEVICE_CONFIG_WITH_GLIB_MAIN_LOOP -CHIP_ERROR PlatformManagerImpl::_GLibMatterContextInvokeSync(CHIP_ERROR (*func)(void *), void * userData) +void PlatformManagerImpl::_GLibMatterContextInvokeSync(LambdaBridge && bridge) { // Because of TSAN false positives, we need to use a mutex to synchronize access to all members of // the GLibMatterContextInvokeData object (including constructor and destructor). This is a temporary // workaround until TSAN-enabled GLib will be used in our CI. std::unique_lock lock(mGLibMainLoopCallbackIndirectionMutex); - GLibMatterContextInvokeData invokeData{ func, userData }; + GLibMatterContextInvokeData invokeData{ std::move(bridge) }; lock.unlock(); @@ -300,15 +300,11 @@ CHIP_ERROR PlatformManagerImpl::_GLibMatterContextInvokeSync(CHIP_ERROR (*func)( // XXX: Temporary workaround for TSAN false positives. std::unique_lock lock_(PlatformMgrImpl().mGLibMainLoopCallbackIndirectionMutex); - auto mFunc = data->mFunc; - auto mUserData = data->mFuncUserData; - lock_.unlock(); - auto result = mFunc(mUserData); + data->bridge(); lock_.lock(); - data->mDone = true; - data->mFuncResult = result; + data->mDone = true; data->mDoneCond.notify_one(); return G_SOURCE_REMOVE; @@ -318,8 +314,6 @@ CHIP_ERROR PlatformManagerImpl::_GLibMatterContextInvokeSync(CHIP_ERROR (*func)( lock.lock(); invokeData.mDoneCond.wait(lock, [&invokeData]() { return invokeData.mDone; }); - - return invokeData.mFuncResult; } #endif // CHIP_DEVICE_CONFIG_WITH_GLIB_MAIN_LOOP diff --git a/src/platform/NuttX/PlatformManagerImpl.h b/src/platform/NuttX/PlatformManagerImpl.h index 5669ce5dd28d62..f911ccb3c73a21 100644 --- a/src/platform/NuttX/PlatformManagerImpl.h +++ b/src/platform/NuttX/PlatformManagerImpl.h @@ -69,7 +69,21 @@ class PlatformManagerImpl final : public PlatformManager, public Internal::Gener template CHIP_ERROR GLibMatterContextInvokeSync(CHIP_ERROR (*func)(T *), T * userData) { - return _GLibMatterContextInvokeSync((CHIP_ERROR(*)(void *)) func, (void *) userData); + struct + { + CHIP_ERROR returnValue = CHIP_NO_ERROR; + CHIP_ERROR (*functionToCall)(T *); + T * userData; + } context; + + context.functionToCall = func; + context.userData = userData; + + LambdaBridge bridge; + bridge.Initialize([&context]() { context.returnValue = context.functionToCall(context.userData); }); + + _GLibMatterContextInvokeSync(std::move(bridge)); + return context.returnValue; } unsigned int GLibMatterContextAttachSource(GSource * source) @@ -102,9 +116,7 @@ class PlatformManagerImpl final : public PlatformManager, public Internal::Gener struct GLibMatterContextInvokeData { - CHIP_ERROR (*mFunc)(void *); - void * mFuncUserData; - CHIP_ERROR mFuncResult; + LambdaBridge bridge; // Sync primitives to wait for the function to be executed std::condition_variable mDoneCond; bool mDone = false; @@ -113,10 +125,13 @@ class PlatformManagerImpl final : public PlatformManager, public Internal::Gener /** * @brief Invoke a function on the Matter GLib context. * - * @note This function does not provide type safety for the user data. Please, - * use the GLibMatterContextInvokeSync() template function instead. + * @param[in] bridge a LambdaBridge object that holds the lambda to be invoked within the GLib context. + * + * @note This function moves the LambdaBridge into the GLib context for invocation. + * The LambdaBridge is created and initialised in GLibMatterContextInvokeSync(). + * use the GLibMatterContextInvokeSync() template function instead of this one. */ - CHIP_ERROR _GLibMatterContextInvokeSync(CHIP_ERROR (*func)(void *), void * userData); + void _GLibMatterContextInvokeSync(LambdaBridge && bridge); // XXX: Mutex for guarding access to glib main event loop callback indirection // synchronization primitives. This is a workaround to suppress TSAN warnings. diff --git a/src/platform/Tizen/PlatformManagerImpl.cpp b/src/platform/Tizen/PlatformManagerImpl.cpp index 58e1e014f41e28..91cb91be0a6a32 100644 --- a/src/platform/Tizen/PlatformManagerImpl.cpp +++ b/src/platform/Tizen/PlatformManagerImpl.cpp @@ -56,9 +56,7 @@ namespace { struct GLibMatterContextInvokeData { - CHIP_ERROR (*mFunc)(void *); - void * mFuncUserData; - CHIP_ERROR mFuncResult; + LambdaBridge bridge; // Sync primitives to wait for the function to be executed std::mutex mDoneMutex; std::condition_variable mDoneCond; @@ -144,18 +142,17 @@ void PlatformManagerImpl::_Shutdown() mGLibMainLoop = nullptr; } -CHIP_ERROR PlatformManagerImpl::_GLibMatterContextInvokeSync(CHIP_ERROR (*func)(void *), void * userData) +void PlatformManagerImpl::_GLibMatterContextInvokeSync(LambdaBridge && bridge) { - GLibMatterContextInvokeData invokeData{ func, userData }; + GLibMatterContextInvokeData invokeData{ std::move(bridge) }; g_main_context_invoke_full( g_main_loop_get_context(mGLibMainLoop), G_PRIORITY_HIGH_IDLE, [](void * userData_) { auto * data = reinterpret_cast(userData_); VerifyOrExit(g_main_context_get_thread_default() != nullptr, - ChipLogError(DeviceLayer, "GLib thread default main context is not set"); - data->mFuncResult = CHIP_ERROR_INCORRECT_STATE); - data->mFuncResult = data->mFunc(data->mFuncUserData); + ChipLogError(DeviceLayer, "GLib thread default main context is not set")); + data->bridge(); exit: data->mDoneMutex.lock(); data->mDone = true; @@ -167,8 +164,6 @@ CHIP_ERROR PlatformManagerImpl::_GLibMatterContextInvokeSync(CHIP_ERROR (*func)( std::unique_lock lock(invokeData.mDoneMutex); invokeData.mDoneCond.wait(lock, [&invokeData]() { return invokeData.mDone; }); - - return invokeData.mFuncResult; } } // namespace DeviceLayer diff --git a/src/platform/Tizen/PlatformManagerImpl.h b/src/platform/Tizen/PlatformManagerImpl.h index 8a3d0e450d9742..ebb61aa2b5d084 100644 --- a/src/platform/Tizen/PlatformManagerImpl.h +++ b/src/platform/Tizen/PlatformManagerImpl.h @@ -64,7 +64,21 @@ class PlatformManagerImpl final : public PlatformManager, public Internal::Gener template CHIP_ERROR GLibMatterContextInvokeSync(CHIP_ERROR (*func)(T *), T * userData) { - return _GLibMatterContextInvokeSync((CHIP_ERROR(*)(void *)) func, (void *) userData); + struct + { + CHIP_ERROR returnValue = CHIP_NO_ERROR; + CHIP_ERROR (*functionToCall)(T *); + T * userData; + } context; + + context.functionToCall = func; + context.userData = userData; + + LambdaBridge bridge; + bridge.Initialize([&context]() { context.returnValue = context.functionToCall(context.userData); }); + + _GLibMatterContextInvokeSync(std::move(bridge)); + return context.returnValue; } System::Clock::Timestamp GetStartTime() { return mStartTime; } @@ -87,10 +101,13 @@ class PlatformManagerImpl final : public PlatformManager, public Internal::Gener /** * @brief Invoke a function on the Matter GLib context. * - * @note This function does not provide type safety for the user data. Please, - * use the GLibMatterContextInvokeSync() template function instead. + * @param[in] bridge a LambdaBridge object that holds the lambda to be invoked within the GLib context. + * + * @note This function moves the LambdaBridge into the GLib context for invocation. + * The LambdaBridge is created and initialised in GLibMatterContextInvokeSync(). + * use the GLibMatterContextInvokeSync() template function instead of this one. */ - CHIP_ERROR _GLibMatterContextInvokeSync(CHIP_ERROR (*func)(void *), void * userData); + void _GLibMatterContextInvokeSync(LambdaBridge && bridge); GMainLoop * mGLibMainLoop; GThread * mGLibMainLoopThread;