Skip to content

Commit

Permalink
pass an out pointer to capture function return result
Browse files Browse the repository at this point in the history
  • Loading branch information
Alami-Amine committed Sep 26, 2024
1 parent 38d3ed7 commit af70f8a
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 40 deletions.
46 changes: 20 additions & 26 deletions src/lib/support/LambdaBridge.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,47 +28,41 @@ class LambdaBridge
public:
// Use initialize instead of constructor because this class has to be trivial
template <typename Lambda>
void Initialize(const Lambda & lambda)
void Initialize(const Lambda & lambda, CHIP_ERROR * Error_Value = nullptr)
{
// LambdaBridge accepts either Lambdas without arguments or those with `CHIP_ERROR *` as argument.
mpError = Error_Value;

// memcpy is used to move the lambda into the event queue, so it must be trivially copyable
static_assert(std::is_trivially_copyable<Lambda>::value, "lambda must be trivially copyable");
static_assert(sizeof(Lambda) <= CHIP_CONFIG_LAMBDA_EVENT_SIZE, "lambda too large");
static_assert(CHIP_CONFIG_LAMBDA_EVENT_ALIGN % alignof(Lambda) == 0, "lambda align too large");

// Implicit cast a capture-less lambda into a raw function pointer.
if constexpr (std::is_same_v<decltype(lambda()), void>)
{
mLambdaVoidProxy = [](const LambdaStorage & body) { (*reinterpret_cast<const Lambda *>(&body))(); };
}
else if constexpr (std::is_same_v<decltype(lambda()), CHIP_ERROR>)
{
mLambdaProxy = [](const LambdaStorage & body) { return (*reinterpret_cast<const Lambda *>(&body))(); };
}
mLambdaProxy = [](const LambdaStorage & body, CHIP_ERROR * apError) {
// Check if lambda has CHIP_ERROR * as argument, if not, call it without arguments
if constexpr (std::is_invocable<Lambda, CHIP_ERROR *>::value)
{
// Call the lambda with CHIP_ERROR* argument
(*reinterpret_cast<const Lambda *>(&body))(apError);
}
else
{
// Call the lambda with no arguments
(*reinterpret_cast<const Lambda *>(&body))();
}
};

::memcpy(&mLambdaBody, &lambda, sizeof(Lambda));
}

void operator()() const
{
if (mLambdaVoidProxy != nullptr)
{
mLambdaVoidProxy(mLambdaBody);
}
}
CHIP_ERROR CallLambdaWithErrorReturn() const
{
if (mLambdaProxy != nullptr)
{
return mLambdaProxy(mLambdaBody);
}
return CHIP_ERROR_INTERNAL; // Return an error if the proxy is not for CHIP_ERROR
}
void operator()() const { mLambdaProxy(mLambdaBody, mpError); }

private:
using LambdaStorage = std::aligned_storage_t<CHIP_CONFIG_LAMBDA_EVENT_SIZE, CHIP_CONFIG_LAMBDA_EVENT_ALIGN>;
void (*mLambdaVoidProxy)(const LambdaStorage & body);
CHIP_ERROR (*mLambdaProxy)(const LambdaStorage & body);
void (*mLambdaProxy)(const LambdaStorage & body, CHIP_ERROR * mpError);
LambdaStorage mLambdaBody;
CHIP_ERROR * mpError;
};

static_assert(std::is_trivial<LambdaBridge>::value, "LambdaBridge is not trivial");
Expand Down
10 changes: 3 additions & 7 deletions src/platform/Linux/PlatformManagerImpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,7 @@ void PlatformManagerImpl::_Shutdown()
}

#if CHIP_DEVICE_CONFIG_WITH_GLIB_MAIN_LOOP
CHIP_ERROR PlatformManagerImpl::_GLibMatterContextInvokeSync(LambdaBridge && bridge)
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
Expand All @@ -301,22 +301,18 @@ CHIP_ERROR PlatformManagerImpl::_GLibMatterContextInvokeSync(LambdaBridge && bri
std::unique_lock<std::mutex> lock_(PlatformMgrImpl().mGLibMainLoopCallbackIndirectionMutex);

lock_.unlock();
auto result = data->bridge.CallLambdaWithErrorReturn();
data->bridge();
lock_.lock();

data->mDone = true;
data->mFuncResult = result;
data->mDone = true;
data->mDoneCond.notify_one();

return G_SOURCE_REMOVE;
},
&invokeData, nullptr);

lock.lock();

invokeData.mDoneCond.wait(lock, [&invokeData]() { return invokeData.mDone; });

return invokeData.mFuncResult;
}
#endif // CHIP_DEVICE_CONFIG_WITH_GLIB_MAIN_LOOP

Expand Down
21 changes: 14 additions & 7 deletions src/platform/Linux/PlatformManagerImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,11 +69,16 @@ class PlatformManagerImpl final : public PlatformManager, public Internal::Gener
template <typename T>
CHIP_ERROR GLibMatterContextInvokeSync(CHIP_ERROR (*func)(T *), T * userData)
{
auto lambda = [func, userData]() -> CHIP_ERROR { return func(userData); };
CHIP_ERROR LambdaErrorReturn;

// wrapping the function pointer and userData into a Lambda, to be stored and passed as a LambdaBridge
auto lambda = [func, userData](CHIP_ERROR * apErrorReturn) { *apErrorReturn = func(userData); };

LambdaBridge bridge;
bridge.Initialize(lambda);
return _GLibMatterContextInvokeSync(std::move(bridge));
bridge.Initialize(lambda, &LambdaErrorReturn);
_GLibMatterContextInvokeSync(std::move(bridge));

return LambdaErrorReturn;
}

unsigned int GLibMatterContextAttachSource(GSource * source)
Expand Down Expand Up @@ -107,7 +112,6 @@ class PlatformManagerImpl final : public PlatformManager, public Internal::Gener
struct GLibMatterContextInvokeData
{
LambdaBridge bridge;
CHIP_ERROR mFuncResult;
// Sync primitives to wait for the function to be executed
std::condition_variable mDoneCond;
bool mDone = false;
Expand All @@ -116,10 +120,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(LambdaBridge && bridge);
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.
Expand Down

0 comments on commit af70f8a

Please sign in to comment.