Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions score/mw/com/impl/bindings/lola/proxy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -600,7 +600,7 @@ bool Proxy::IsEventProvided(const std::string_view event_name) const noexcept
return event_exists;
}

score::Result<void> Proxy::SetupMethods()
score::Result<void> Proxy::SetupMethods(const std::size_t additional_shm_size_bytes)
{
auto enabled_method_data = GetMethodIdAndQueueSizeForEnabledMethods();

Expand Down Expand Up @@ -672,7 +672,7 @@ score::Result<void> Proxy::SetupMethods()
}

const auto type_erased_element_infos = GetTypeErasedElementInfoForEnabledMethods(enabled_method_data);
const auto required_shm_size = CalculateRequiredShmSize(type_erased_element_infos);
const auto required_shm_size = CalculateRequiredShmSize(type_erased_element_infos) + additional_shm_size_bytes;

const auto skeleton_shm_permissions = GetSkeletonShmPermissions();
method_shm_resource_ = memory::shared::SharedMemoryFactory::Create(
Expand Down
19 changes: 18 additions & 1 deletion score/mw/com/impl/bindings/lola/proxy.h
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,16 @@ class Proxy : public ProxyBinding
/// \return True if the event name exists, otherwise, false
bool IsEventProvided(const std::string_view event_name) const noexcept override;

score::Result<void> SetupMethods() override;
/// \brief Sets up the shared memory for all the methods of the proxy, notifies the skeleton to open the shared
/// memory and perform any setup on skeleton side.
///
/// After creating the shared memory, the proxy sends a blocking message which waits for a reply via message passing
/// to the skeleton. The skeleton will then open the shared memory and perform any setup on its side. The proxy will
/// then wait for an acknowledgement from the skeleton that it has completed its setup.
///
/// \return result which contains an error if setup on the proxy or skeleton side failed or if the message passing
/// communication with the skeleton failed.
score::Result<void> SetupMethods(const std::size_t additional_shm_size_bytes = 0) override;

QualityType GetQualityType() const noexcept;

Expand All @@ -192,6 +201,14 @@ class Proxy : public ProxyBinding
/// \note Idempotent: calling it more than once is safe.
void FinalizeDeinitialize() override;

memory::shared::ManagedMemoryResource& GetMethodMemoryResource() noexcept
{
SCORE_LANGUAGE_FUTURECPP_PRECONDITION_PRD_MESSAGE(
method_shm_resource_ != nullptr,
"Proxy::GetControlMemoryResource: Methods managed memory resource pointer is null");
return *method_shm_resource_;
}

private:
static std::atomic<ProxyInstanceIdentifier::ProxyInstanceCounter> current_proxy_instance_counter_;

Expand Down
7 changes: 4 additions & 3 deletions score/mw/com/impl/bindings/mock_binding/proxy.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

#include "score/result/result.h"
#include <gmock/gmock.h>
#include <cstddef>

namespace score::mw::com::impl::mock_binding
{
Expand All @@ -29,7 +30,7 @@ class Proxy : public ProxyBinding
~Proxy() override = default;

MOCK_METHOD(bool, IsEventProvided, (const std::string_view), (const, noexcept, override));
MOCK_METHOD(Result<void>, SetupMethods, (), (override));
MOCK_METHOD(Result<void>, SetupMethods, (std::size_t), (override));
MOCK_METHOD(void, PrepareDeinitialize, (), (override));
MOCK_METHOD(void, FinalizeDeinitialize, (), (override));
};
Expand All @@ -45,9 +46,9 @@ class ProxyFacade : public ProxyBinding
return proxy_.IsEventProvided(event_name);
}

Result<void> SetupMethods() override
Result<void> SetupMethods(std::size_t additional_shm_size_bytes) override
{
return proxy_.SetupMethods();
return proxy_.SetupMethods(additional_shm_size_bytes);
}

void PrepareDeinitialize() override
Expand Down
37 changes: 32 additions & 5 deletions score/mw/com/impl/methods/proxy_method.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@
#ifndef SCORE_MW_COM_IMPL_METHODS_PROXY_METHOD_H
#define SCORE_MW_COM_IMPL_METHODS_PROXY_METHOD_H

#include "score/mw/com/impl/bindings/lola/proxy.h"
#include "score/mw/com/impl/com_error.h"
#include "score/mw/com/impl/methods/method_signature_element_ptr.h"
#include "score/mw/com/impl/methods/proxy_method_binding.h"
#include "score/mw/com/impl/proxy_binding.h"
#include "score/mw/com/impl/util/type_erased_storage.h"

#include "score/containers/dynamic_array.h"
Expand Down Expand Up @@ -146,12 +148,35 @@ score::Result<std::tuple<impl::MethodInArgPtr<ArgTypes>...>> AllocateImpl(
std::move(method_in_arg_ptr_tuple));
}

template <typename T>
void InitializeArg(void* arg_pointer, ProxyBinding& proxy_binding)
{
if constexpr (std::is_constructible_v<T, const memory::shared::PolymorphicOffsetPtrAllocator<T>&>)
{
// Temporary workaround to support using types which dynamically allocate memory at runtime (SWP-269486). These
// types must take a memory::shared::PolymorphicOffsetPtrAllocator<T> as the only argument in their constructor.
// We need to propagate the shared memory resource from the lola binding to the constructor of the type so that
// the dynamic memory allocation can be done in shared memory. This is a temporary workaround and is not public.
auto* const lola_proxy_binding = dynamic_cast<lola::Proxy*>(&proxy_binding);
SCORE_LANGUAGE_FUTURECPP_ASSERT_PRD_MESSAGE(lola_proxy_binding != nullptr,
"ProxyBinding must be of type lola::Proxy, since ArgType is "
"constructible with a PolymorphicOffsetPtrAllocator");
auto& methods_memory_resource = lola_proxy_binding->GetMethodMemoryResource();
memory::shared::PolymorphicOffsetPtrAllocator<T> allocator{methods_memory_resource};
score::cpp::ignore = new (arg_pointer) T{allocator};
}
else
{
score::cpp::ignore = new (arg_pointer) T{};
}
}

/// \brief Initializes all InArgs by calling the default constructor for each argument.
///
/// This step is important to avoid undefined behaviour (interpreting uninitialized memory) and also to ensure that any
/// non-trivially constructible types are properly initialized.
template <typename... ArgTypes>
Result<void> InitializeInArgs(ProxyMethodBinding& binding, const std::size_t queue_size)
Result<void> InitializeInArgs(ProxyBinding& proxy_binding, ProxyMethodBinding& binding, const std::size_t queue_size)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe a small hint (doxygen \param), why we hand down ProxyBinding here ...?

\param proxy_binding proxy binding is handed over for an interim solution for support of "semi dynamic" in-arg data-types. In case the data-type is using a PolymorphicOffsetPtrAllocator, we extract the corresponding memory_resource from the proxy_binding

{
for (std::size_t queue_index = 0U; queue_index < queue_size; ++queue_index)
{
Expand All @@ -165,8 +190,8 @@ Result<void> InitializeInArgs(ProxyMethodBinding& binding, const std::size_t que
// std::apply takes a callable and a tuple. It calls the callable with the arguments from the unpacked tuple.
// E.g. In this case, it will call the lambda, fn, with: `fn(get<0>(args), get<1>(args), ..., get<n>(args))`
std::apply(
[](typename std::add_pointer<ArgTypes>::type... arg_pointers) {
((score::cpp::ignore = new (arg_pointers) ArgTypes{}), ...);
[&proxy_binding](std::add_pointer_t<ArgTypes>... arg_pointers) {
(InitializeArg<ArgTypes>(arg_pointers, proxy_binding), ...);
},
deserialized_arg_pointers);
}
Expand All @@ -178,7 +203,9 @@ Result<void> InitializeInArgs(ProxyMethodBinding& binding, const std::size_t que
/// This step is important to avoid undefined behaviour (interpreting uninitialized memory) and also to ensure that any
/// non-trivially constructible types are properly initialized.
template <typename ReturnType>
Result<void> InitializeReturnValue(ProxyMethodBinding& binding, const std::size_t queue_size)
Result<void> InitializeReturnValue(ProxyBinding& proxy_binding,
ProxyMethodBinding& binding,
const std::size_t queue_size)
{
for (std::size_t queue_index = 0U; queue_index < queue_size; ++queue_index)
{
Expand All @@ -187,7 +214,7 @@ Result<void> InitializeReturnValue(ProxyMethodBinding& binding, const std::size_
{
return Unexpected(allocated_return_value_storage.error());
}
score::cpp::ignore = new (allocated_return_value_storage->data()) ReturnType{};
InitializeArg<ReturnType>(allocated_return_value_storage->data(), proxy_binding);
}
return {};
}
Expand Down
4 changes: 3 additions & 1 deletion score/mw/com/impl/methods/proxy_method_base.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
namespace score::mw::com::impl
{

class ProxyBinding;

class ProxyMethodBase : public EnableReferenceToMoveableFromThis<ProxyMethodBase>
{
public:
Expand Down Expand Up @@ -58,7 +60,7 @@ class ProxyMethodBase : public EnableReferenceToMoveableFromThis<ProxyMethodBase
/// reinitialized on every method call. This potentially would have performance benefits but more importantly this
/// allows us to support "semi-dynamic" types in which a type dynamically allocates once on construction and the
/// constructor is then never called again.
virtual Result<void> InitializeInArgsAndReturnValues() = 0;
virtual Result<void> InitializeInArgsAndReturnValues(ProxyBinding& proxy_binding) = 0;

protected:
/// \brief Size of the call-queue is currently fixed to 1! As soon as we are going to support larger call-queues,
Expand Down
23 changes: 14 additions & 9 deletions score/mw/com/impl/methods/proxy_method_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,11 @@ class ProxyMethodTestFixture : public ::testing::Test
return &moved_method->second.get().Get();
}

ProxyBinding& GetProxyBinding()
{
return *ProxyBaseView{proxy_base_}.GetBinding();
}

alignas(8) std::array<std::byte, 1024> method_in_args_buffer_{};
alignas(8) std::array<std::byte, 1024> method_return_type_buffer_{};
ConfigurationStore config_store_{InstanceSpecifier::Create(std::string{"/my_dummy_instance_specifier"}).value(),
Expand Down Expand Up @@ -829,7 +834,7 @@ TEST_F(ProxyMethodWithNonTrivialConstructibleInArgsAndReturnFixture, InitializeI
this->GivenAValidProxyMethod();

// When calling InitializeInArgsAndReturnValues
const auto result = this->unit_->InitializeInArgsAndReturnValues();
const auto result = this->unit_->InitializeInArgsAndReturnValues(this->GetProxyBinding());

// Then a valid result is returned
EXPECT_TRUE(result.has_value());
Expand Down Expand Up @@ -858,7 +863,7 @@ TEST_F(ProxyMethodWithNonTrivialConstructibleInArgsAndReturnFixture,
.WillOnce(Return(MakeUnexpected(ComErrc::kBindingFailure)));

// When calling InitializeInArgsAndReturnValues
const auto result = this->unit_->InitializeInArgsAndReturnValues();
const auto result = this->unit_->InitializeInArgsAndReturnValues(this->GetProxyBinding());

// Then an error is returned
ASSERT_FALSE(result.has_value());
Expand All @@ -875,7 +880,7 @@ TEST_F(ProxyMethodWithNonTrivialConstructibleInArgsAndReturnFixture,
.WillOnce(Return(MakeUnexpected(ComErrc::kBindingFailure)));

// When calling InitializeInArgsAndReturnValues
const auto result = this->unit_->InitializeInArgsAndReturnValues();
const auto result = this->unit_->InitializeInArgsAndReturnValues(this->GetProxyBinding());

// Then an error is returned
ASSERT_FALSE(result.has_value());
Expand All @@ -888,7 +893,7 @@ TEST_F(ProxyMethodWithNonTrivialConstructibleInArgsAndReturnFixture,
this->GivenAValidProxyMethod();

// When calling InitializeInArgsAndReturnValues
this->unit_->InitializeInArgsAndReturnValues();
this->unit_->InitializeInArgsAndReturnValues(this->GetProxyBinding());

// Then the zero copy call operator returns a pointer pointing to an initialized object (i.e. the non-trivial
// default constructor was called, initializing value to NonTriviallyConstructibleType::kInitialValue
Expand All @@ -910,7 +915,7 @@ TEST_F(ProxyMethodWithNonTrivialConstructibleInArgsAndReturnFixture,
this->GivenAValidProxyMethod();

// When calling InitializeInArgsAndReturnValues
this->unit_->InitializeInArgsAndReturnValues();
this->unit_->InitializeInArgsAndReturnValues(this->GetProxyBinding());

// Then the copy call operator returns a pointer pointing to an initialized object (i.e. the non-trivial
// default constructor was called, initializing value to NonTriviallyConstructibleType::kInitialValue
Expand All @@ -926,7 +931,7 @@ TEST_F(ProxyMethodWithNonTrivialConstructibleInArgsOnlyFixture, InitializeInArgs
this->GivenAValidProxyMethod();

// When calling InitializeInArgsAndReturnValues
this->unit_->InitializeInArgsAndReturnValues();
this->unit_->InitializeInArgsAndReturnValues(this->GetProxyBinding());

// Then Allocate returns a pointer pointing to an initialized object (i.e. the non-trivial default constructor was
// called, initializing value to NonTriviallyConstructibleType::kInitialValue
Expand All @@ -952,7 +957,7 @@ TEST_F(ProxyMethodWithNonTrivialConstructibleInArgsOnlyFixture,
.WillOnce(Return(MakeUnexpected(ComErrc::kBindingFailure)));

// When calling InitializeInArgsAndReturnValues
const auto result = this->unit_->InitializeInArgsAndReturnValues();
const auto result = this->unit_->InitializeInArgsAndReturnValues(this->GetProxyBinding());

// Then an error is returned
ASSERT_FALSE(result.has_value());
Expand All @@ -965,7 +970,7 @@ TEST_F(ProxyMethodWithNonTrivialConstructibleReturnOnlyFixture,
this->GivenAValidProxyMethod();

// When calling InitializeInArgsAndReturnValues
this->unit_->InitializeInArgsAndReturnValues();
this->unit_->InitializeInArgsAndReturnValues(this->GetProxyBinding());

// Then the copy call operator returns a pointer pointing to an initialized object (i.e. the non-trivial
// default constructor was called, initializing value to NonTriviallyConstructibleType::kInitialValue
Expand All @@ -986,7 +991,7 @@ TEST_F(ProxyMethodWithNonTrivialConstructibleReturnOnlyFixture,
.WillOnce(Return(MakeUnexpected(ComErrc::kBindingFailure)));

// When calling InitializeInArgsAndReturnValues
const auto result = this->unit_->InitializeInArgsAndReturnValues();
const auto result = this->unit_->InitializeInArgsAndReturnValues(this->GetProxyBinding());

// Then an error is returned
ASSERT_FALSE(result.has_value());
Expand Down
6 changes: 3 additions & 3 deletions score/mw/com/impl/methods/proxy_method_with_in_args.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ class ProxyMethod<void(ArgTypes...)> final : public ProxyMethodBase
ProxyMethod(ProxyMethod&&) noexcept = default;
ProxyMethod& operator=(ProxyMethod&&) noexcept = default;

Result<void> InitializeInArgsAndReturnValues() override;
Result<void> InitializeInArgsAndReturnValues(ProxyBinding& proxy_binding) override;

/// \brief Allocates the necessary storage for the argument values and the return value of a method call.
/// \return On success, a tuple of MethodInArgPtr for each argument type is returned. On failure, an error code is
Expand Down Expand Up @@ -158,10 +158,10 @@ score::Result<void> ProxyMethod<void(ArgTypes...)>::operator()(MethodInArgPtr<Ar
}

template <typename... ArgTypes>
Result<void> ProxyMethod<void(ArgTypes...)>::InitializeInArgsAndReturnValues()
Result<void> ProxyMethod<void(ArgTypes...)>::InitializeInArgsAndReturnValues(ProxyBinding& proxy_binding)
{
SCORE_LANGUAGE_FUTURECPP_ASSERT_PRD(binding_ != nullptr);
const auto init_in_args_result = detail::InitializeInArgs<ArgTypes...>(*binding_, kCallQueueSize);
const auto init_in_args_result = detail::InitializeInArgs<ArgTypes...>(proxy_binding, *binding_, kCallQueueSize);
if (!init_in_args_result.has_value())
{
return Unexpected(init_in_args_result.error());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include "score/mw/com/impl/methods/proxy_method_binding.h"
#include "score/mw/com/impl/plumbing/proxy_method_binding_factory.h"
#include "score/mw/com/impl/proxy_base.h"
#include "score/mw/com/impl/proxy_binding.h"
#include "score/mw/com/impl/util/type_erased_storage.h"

#include "score/containers/dynamic_array.h"
Expand Down Expand Up @@ -124,7 +125,7 @@ class ProxyMethod<ReturnType(ArgTypes...)> final : public ProxyMethodBase
ProxyMethod(ProxyMethod&&) noexcept = default;
ProxyMethod& operator=(ProxyMethod&&) noexcept = default;

Result<void> InitializeInArgsAndReturnValues() override;
Result<void> InitializeInArgsAndReturnValues(ProxyBinding& proxy_binding) override;

/// \brief Allocates the necessary storage for the argument values and the return value of a method call.
/// \return On success, a tuple of MethodInArgPtr for each argument type is returned. On failure, an error code is
Expand Down Expand Up @@ -221,16 +222,16 @@ score::Result<MethodReturnTypePtr<ReturnType>> ProxyMethod<ReturnType(ArgTypes..
}

template <typename ReturnType, typename... ArgTypes>
Result<void> ProxyMethod<ReturnType(ArgTypes...)>::InitializeInArgsAndReturnValues()
Result<void> ProxyMethod<ReturnType(ArgTypes...)>::InitializeInArgsAndReturnValues(ProxyBinding& proxy_binding)
{
SCORE_LANGUAGE_FUTURECPP_ASSERT_PRD(binding_ != nullptr);
const auto init_in_args_result = detail::InitializeInArgs<ArgTypes...>(*binding_, kCallQueueSize);
const auto init_in_args_result = detail::InitializeInArgs<ArgTypes...>(proxy_binding, *binding_, kCallQueueSize);
if (!init_in_args_result.has_value())
{
return Unexpected(init_in_args_result.error());
}

const auto init_return_result = detail::InitializeReturnValue<ReturnType>(*binding_, kCallQueueSize);
const auto init_return_result = detail::InitializeReturnValue<ReturnType>(proxy_binding, *binding_, kCallQueueSize);
if (!init_return_result.has_value())
{
return Unexpected(init_return_result.error());
Expand Down
6 changes: 3 additions & 3 deletions score/mw/com/impl/methods/proxy_method_with_return_type.h
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ class ProxyMethod<ReturnType()> final : public ProxyMethodBase
ProxyMethod(ProxyMethod&&) noexcept = default;
ProxyMethod& operator=(ProxyMethod&&) noexcept = default;

Result<void> InitializeInArgsAndReturnValues() override;
Result<void> InitializeInArgsAndReturnValues(ProxyBinding& proxy_binding) override;

/// \brief This is the call-operator of ProxyMethod with no arguments for a non-void ReturnType.
score::Result<MethodReturnTypePtr<ReturnType>> operator()();
Expand Down Expand Up @@ -172,10 +172,10 @@ score::Result<MethodReturnTypePtr<ReturnType>> ProxyMethod<ReturnType()>::operat
}

template <typename ReturnType>
Result<void> ProxyMethod<ReturnType()>::InitializeInArgsAndReturnValues()
Result<void> ProxyMethod<ReturnType()>::InitializeInArgsAndReturnValues(ProxyBinding& proxy_binding)
{
SCORE_LANGUAGE_FUTURECPP_ASSERT_PRD(binding_ != nullptr);
const auto init_return_result = detail::InitializeReturnValue<ReturnType>(*binding_, kCallQueueSize);
const auto init_return_result = detail::InitializeReturnValue<ReturnType>(proxy_binding, *binding_, kCallQueueSize);
if (!init_return_result.has_value())
{
return Unexpected(init_return_result.error());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ class ProxyMethod<void()> final : public ProxyMethodBase
ProxyMethod(ProxyMethod&&) noexcept = default;
ProxyMethod& operator=(ProxyMethod&&) noexcept = default;

Result<void> InitializeInArgsAndReturnValues() override
Result<void> InitializeInArgsAndReturnValues(ProxyBinding&) override
{
return {};
}
Expand Down
6 changes: 3 additions & 3 deletions score/mw/com/impl/proxy_base.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,9 +101,9 @@ score::Result<void> ProxyBase::StopFindService(const FindServiceHandle handle) n
return stop_find_service_result;
}

Result<void> ProxyBase::SetupMethods()
Result<void> ProxyBase::SetupMethods(const std::size_t additional_shm_size_bytes)
{
const auto result = proxy_binding_->SetupMethods();
const auto result = proxy_binding_->SetupMethods(additional_shm_size_bytes);
if (!result.has_value())
{
return MakeUnexpected<void>(result.error());
Expand All @@ -112,7 +112,7 @@ Result<void> ProxyBase::SetupMethods()
for (auto& method_key_value_pair : methods_)
{
auto& method = method_key_value_pair.second.get().Get();
const auto method_init_result = method.InitializeInArgsAndReturnValues();
const auto method_init_result = method.InitializeInArgsAndReturnValues(*proxy_binding_);
if (!method_init_result.has_value())
{
return MakeUnexpected<void>(method_init_result.error());
Expand Down
Loading
Loading