Use LambdaBridge to safely invoke functions in GLib Matter context (#35777)

* using LambdaBridge to cast function pointer

* adding support for Lambda that returns a CHIP_ERROR to LambdaBridge

* pass an out pointer to capture function return result

* Tizen Platform: Using LambdaBridge to safely invoke functions in GLib Matter context

* Update to not modify lambdabridge

* Potentiall cleaner code

* Update code to make it compile

* Update tizen code too

* Tizen platform fix

* Duplicating Changes to NuttX platform

* Tizen Platform Fix

---------

Co-authored-by: Andrei Litvin <andreilitvin@google.com>
diff --git a/src/platform/Linux/PlatformManagerImpl.cpp b/src/platform/Linux/PlatformManagerImpl.cpp
index 68f3d58..dba3cb4 100644
--- a/src/platform/Linux/PlatformManagerImpl.cpp
+++ b/src/platform/Linux/PlatformManagerImpl.cpp
@@ -281,14 +281,14 @@
 }
 
 #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<std::mutex> lock(mGLibMainLoopCallbackIndirectionMutex);
 
-    GLibMatterContextInvokeData invokeData{ func, userData };
+    GLibMatterContextInvokeData invokeData{ std::move(bridge) };
 
     lock.unlock();
 
@@ -300,15 +300,11 @@
             // XXX: Temporary workaround for TSAN false positives.
             std::unique_lock<std::mutex> 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 @@
         &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 5669ce5..d50687b 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 <condition_variable>
 #include <mutex>
 
@@ -69,7 +70,21 @@
     template <typename T>
     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 @@
 
     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 @@
     /**
      * @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 d590f58..a44f084 100644
--- a/src/platform/NuttX/PlatformManagerImpl.cpp
+++ b/src/platform/NuttX/PlatformManagerImpl.cpp
@@ -281,14 +281,14 @@
 }
 
 #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<std::mutex> lock(mGLibMainLoopCallbackIndirectionMutex);
 
-    GLibMatterContextInvokeData invokeData{ func, userData };
+    GLibMatterContextInvokeData invokeData{ std::move(bridge) };
 
     lock.unlock();
 
@@ -300,15 +300,11 @@
             // XXX: Temporary workaround for TSAN false positives.
             std::unique_lock<std::mutex> 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 @@
     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 5669ce5..f911ccb 100644
--- a/src/platform/NuttX/PlatformManagerImpl.h
+++ b/src/platform/NuttX/PlatformManagerImpl.h
@@ -69,7 +69,21 @@
     template <typename T>
     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 @@
 
     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 @@
     /**
      * @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 58e1e01..91cb91b 100644
--- a/src/platform/Tizen/PlatformManagerImpl.cpp
+++ b/src/platform/Tizen/PlatformManagerImpl.cpp
@@ -56,9 +56,7 @@
 
 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 @@
     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<GLibMatterContextInvokeData *>(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 @@
 
     std::unique_lock<std::mutex> 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 8a3d0e4..ebb61aa 100644
--- a/src/platform/Tizen/PlatformManagerImpl.h
+++ b/src/platform/Tizen/PlatformManagerImpl.h
@@ -64,7 +64,21 @@
     template <typename T>
     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 @@
     /**
      * @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;