From f346fb0093169b508e97e88ebd011c062001c545 Mon Sep 17 00:00:00 2001 From: Mathias Kraus Date: Thu, 10 Feb 2022 22:49:46 +0100 Subject: [PATCH] iox-#27 Add BaseServer for typed and untyped API --- .../error_handling/error_handling.hpp | 2 + .../internal/popo/base_server.hpp | 155 +++++++++++++ .../internal/popo/base_server.inl | 205 ++++++++++++++++++ 3 files changed, 362 insertions(+) create mode 100644 iceoryx_posh/include/iceoryx_posh/internal/popo/base_server.hpp create mode 100644 iceoryx_posh/include/iceoryx_posh/internal/popo/base_server.inl diff --git a/iceoryx_hoofs/include/iceoryx_hoofs/error_handling/error_handling.hpp b/iceoryx_hoofs/include/iceoryx_hoofs/error_handling/error_handling.hpp index f6a6386c5d2..0309d6f97bc 100644 --- a/iceoryx_hoofs/include/iceoryx_hoofs/error_handling/error_handling.hpp +++ b/iceoryx_hoofs/include/iceoryx_hoofs/error_handling/error_handling.hpp @@ -83,6 +83,8 @@ namespace iox error(POPO__BASE_SUBSCRIBER_OVERRIDING_WITH_STATE_SINCE_HAS_DATA_OR_DATA_RECEIVED_ALREADY_ATTACHED) \ error(POPO__BASE_CLIENT_OVERRIDING_WITH_EVENT_SINCE_HAS_RESPONSE_OR_RESPONSE_RECEIVED_ALREADY_ATTACHED) \ error(POPO__BASE_CLIENT_OVERRIDING_WITH_STATE_SINCE_HAS_RESPONSE_OR_RESPONSE_RECEIVED_ALREADY_ATTACHED) \ + error(POPO__BASE_SERVER_OVERRIDING_WITH_EVENT_SINCE_HAS_REQUEST_OR_REQUEST_RECEIVED_ALREADY_ATTACHED) \ + error(POPO__BASE_SERVER_OVERRIDING_WITH_STATE_SINCE_HAS_REQUEST_OR_REQUEST_RECEIVED_ALREADY_ATTACHED) \ error(POPO__CHUNK_QUEUE_POPPER_CHUNK_WITH_INCOMPATIBLE_CHUNK_HEADER_VERSION) \ error(POPO__CHUNK_DISTRIBUTOR_OVERFLOW_OF_QUEUE_CONTAINER) \ error(POPO__CHUNK_DISTRIBUTOR_CLEANUP_DEADLOCK_BECAUSE_BAD_APPLICATION_TERMINATION) \ diff --git a/iceoryx_posh/include/iceoryx_posh/internal/popo/base_server.hpp b/iceoryx_posh/include/iceoryx_posh/internal/popo/base_server.hpp new file mode 100644 index 00000000000..fed0a48618d --- /dev/null +++ b/iceoryx_posh/include/iceoryx_posh/internal/popo/base_server.hpp @@ -0,0 +1,155 @@ +// Copyright (c) 2020 by Robert Bosch GmbH. All rights reserved. +// Copyright (c) 2020 - 2022 by Apex.AI Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +#ifndef IOX_POSH_POPO_BASE_SERVER_HPP +#define IOX_POSH_POPO_BASE_SERVER_HPP + +#include "iceoryx_hoofs/cxx/expected.hpp" +#include "iceoryx_posh/capro/service_description.hpp" +#include "iceoryx_posh/internal/popo/ports/server_port_user.hpp" +#include "iceoryx_posh/popo/server_options.hpp" +#include "iceoryx_posh/popo/trigger_handle.hpp" +#include "iceoryx_posh/runtime/posh_runtime.hpp" + +namespace iox +{ +namespace popo +{ +using uid_t = UniquePortId; + +/// @brief The BaseServer class contains the common implementation for the different server +/// @param[in] Port type of the underlying port, required for testing specializations. +template +class BaseServer +{ + public: + virtual ~BaseServer() noexcept; + + BaseServer(const BaseServer& other) = delete; + BaseServer& operator=(const BaseServer&) = delete; + BaseServer(BaseServer&& rhs) = delete; + BaseServer& operator=(BaseServer&& rhs) = delete; + + /// + /// @brief Get the UID of the server. + /// @return The server's UID. + /// + uid_t getUid() const noexcept; + + /// + /// @brief Get the service description of the server. + /// @return The service description. + /// + capro::ServiceDescription getServiceDescription() const noexcept; + + /// + /// @brief Offer the service to be connected to. + /// + void offer() noexcept; + + /// + /// @brief Stop offering the service. + /// + void stopOffer() noexcept; + + /// + /// @brief Check if the server is offering. + /// @return True if service is currently being offered. + /// + bool isOffered() const noexcept; + + /// + /// @brief Check if the server has clients + /// @return True if currently has subscribers to the service. + /// + bool hasClients() const noexcept; + + /// + /// @brief Check if requests are available. + /// @return True if requests are available. + /// + bool hasRequests() const noexcept; + + /// + /// @brief Check if requests has been missed since the last call of this method. + /// @return True if requests has been missed. + /// @details Requests may be missed due to overflowing receive queue. + /// + bool hasMissedRequests() noexcept; + + /// @brief Releases any unread queued requests. + void releaseQueuedRequests() noexcept; + + friend class NotificationAttorney; + + protected: + using SelfType = BaseServer; + using PortType = Port; + + BaseServer() noexcept = default; // Required for testing. + BaseServer(const capro::ServiceDescription& service, const ServerOptions& serverOptions) noexcept; + + /// @brief Only usable by the WaitSet, not for public use. Invalidates the internal triggerHandle. + /// @param[in] uniqueTriggerId the id of the corresponding trigger + void invalidateTrigger(const uint64_t uniqueTriggerId) noexcept; + + /// @brief Only usable by the WaitSet, not for public use. Attaches the triggerHandle to the internal trigger. + /// @param[in] triggerHandle rvalue reference to the triggerHandle. This class takes the ownership of that handle. + /// @param[in] serverState the state which should be attached + void enableState(iox::popo::TriggerHandle&& triggerHandle, const ServerState serverState) noexcept; + + /// @brief Only usable by the WaitSet, not for public use. Returns method pointer to the event corresponding + /// hasTriggered method callback + /// @param[in] serverState the state to which the hasTriggeredCallback is required + WaitSetIsConditionSatisfiedCallback + getCallbackForIsStateConditionSatisfied(const ServerState serverState) const noexcept; + + /// @brief Only usable by the WaitSet, not for public use. Resets the internal triggerHandle + /// @param[in] serverState the state which should be detached + void disableState(const ServerState serverState) noexcept; + + /// @brief Only usable by the WaitSet, not for public use. Attaches the triggerHandle to the internal trigger. + /// @param[in] triggerHandle rvalue reference to the triggerHandle. This class takes the ownership of that handle. + /// @param[in] serverEvent the event which should be attached + void enableEvent(iox::popo::TriggerHandle&& triggerHandle, const ServerEvent serverEvent) noexcept; + + /// @brief Only usable by the WaitSet, not for public use. Resets the internal triggerHandle + /// @param[in] serverEvent the event which should be detached + void disableEvent(const ServerEvent serverEvent) noexcept; + + /// + /// @brief port + /// @return const accessor of the underlying port + /// + const Port& port() const noexcept; + + /// + /// @brief port + /// @return accessor of the underlying port + /// + Port& port() noexcept; + + Port m_port{nullptr}; // NOLINT(cppcoreguidelines-non-private-member-variables-in-classes) + TriggerHandle m_trigger; // NOLINT(cppcoreguidelines-non-private-member-variables-in-classes) +}; + +} // namespace popo +} // namespace iox + +#include "iceoryx_posh/internal/popo/base_server.inl" + +#endif // IOX_POSH_POPO_BASE_SERVER_HPP diff --git a/iceoryx_posh/include/iceoryx_posh/internal/popo/base_server.inl b/iceoryx_posh/include/iceoryx_posh/internal/popo/base_server.inl new file mode 100644 index 00000000000..719c528edcd --- /dev/null +++ b/iceoryx_posh/include/iceoryx_posh/internal/popo/base_server.inl @@ -0,0 +1,205 @@ +// Copyright (c) 2020 by Robert Bosch GmbH. All rights reserved. +// Copyright (c) 2020 - 2022 by Apex.AI Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +#ifndef IOX_POSH_POPO_BASE_SERVER_INL +#define IOX_POSH_POPO_BASE_SERVER_INL + +#include "iceoryx_posh/internal/popo/base_server.hpp" + +namespace iox +{ +namespace popo +{ +template +inline BaseServer::BaseServer(const capro::ServiceDescription& service, + const ServerOptions& serverOptions) noexcept + : m_port(*iox::runtime::PoshRuntime::getInstance().getMiddlewareServer(service, serverOptions)) +{ +} + +template +inline BaseServer::~BaseServer() noexcept +{ + m_port.destroy(); +} + +template +inline uid_t BaseServer::getUid() const noexcept +{ + return m_port.getUniqueID(); +} + +template +inline capro::ServiceDescription BaseServer::getServiceDescription() const noexcept +{ + return m_port.getCaProServiceDescription(); +} + +template +inline void BaseServer::offer() noexcept +{ + m_port.offer(); +} + +template +inline void BaseServer::stopOffer() noexcept +{ + m_port.stopOffer(); +} + +template +inline bool BaseServer::isOffered() const noexcept +{ + return m_port.isOffered(); +} + +template +inline bool BaseServer::hasClients() const noexcept +{ + return m_port.hasClients(); +} + +template +inline bool BaseServer::hasRequests() const noexcept +{ + return m_port.hasNewRequests(); +} + +template +inline bool BaseServer::hasMissedRequests() noexcept +{ + return m_port.hasLostRequestsSinceLastCall(); +} + +template +inline void BaseServer::releaseQueuedRequests() noexcept +{ + m_port.releaseQueuedRequests(); +} + +template +inline void BaseServer::invalidateTrigger(const uint64_t uniqueTriggerId) noexcept +{ + if (m_trigger.getUniqueId() == uniqueTriggerId) + { + m_port.unsetConditionVariable(); + m_trigger.invalidate(); + } +} + +template +inline void BaseServer::enableState(iox::popo::TriggerHandle&& triggerHandle, + const ServerState serverState) noexcept +{ + switch (serverState) + { + case ServerState::HAS_REQUEST: + if (m_trigger) + { + LogWarn() + << "The server is already attached with either the ServerState::HAS_REQUEST or " + "ServerEvent::REQUEST_RECEIVED to a WaitSet/Listener. Detaching it from previous one and " + "attaching it to the new one with ServerState::HAS_REQUEST. Best practice is to call detach first."; + + errorHandler( + Error::kPOPO__BASE_SERVER_OVERRIDING_WITH_STATE_SINCE_HAS_REQUEST_OR_REQUEST_RECEIVED_ALREADY_ATTACHED, + nullptr, + ErrorLevel::MODERATE); + } + m_trigger = std::move(triggerHandle); + m_port.setConditionVariable(*m_trigger.getConditionVariableData(), m_trigger.getUniqueId()); + break; + } +} + +template +inline WaitSetIsConditionSatisfiedCallback +BaseServer::getCallbackForIsStateConditionSatisfied(const ServerState serverState) const noexcept +{ + switch (serverState) + { + case ServerState::HAS_REQUEST: + return {*this, &SelfType::hasRequests}; + } + return {}; +} + +template +inline void BaseServer::disableState(const ServerState serverState) noexcept +{ + switch (serverState) + { + case ServerState::HAS_REQUEST: + m_trigger.reset(); + m_port.unsetConditionVariable(); + break; + } +} + +template +inline void BaseServer::enableEvent(iox::popo::TriggerHandle&& triggerHandle, + const ServerEvent serverEvent) noexcept +{ + switch (serverEvent) + { + case ServerEvent::REQUEST_RECEIVED: + if (m_trigger) + { + LogWarn() + << "The server is already attached with either the ServerState::HAS_REQUEST or " + "ServerEvent::REQUEST_RECEIVED to a WaitSet/Listener. Detaching it from previous one and " + "attaching it to the new one with ServerEvent::REQUEST_RECEIVED. Best practice is to call detach " + "first."; + errorHandler( + Error::kPOPO__BASE_SERVER_OVERRIDING_WITH_EVENT_SINCE_HAS_REQUEST_OR_REQUEST_RECEIVED_ALREADY_ATTACHED, + nullptr, + ErrorLevel::MODERATE); + } + m_trigger = std::move(triggerHandle); + m_port.setConditionVariable(*m_trigger.getConditionVariableData(), m_trigger.getUniqueId()); + break; + } +} + +template +inline void BaseServer::disableEvent(const ServerEvent serverEvent) noexcept +{ + switch (serverEvent) + { + case ServerEvent::REQUEST_RECEIVED: + m_trigger.reset(); + m_port.unsetConditionVariable(); + break; + } +} + +template +const Port& BaseServer::port() const noexcept +{ + return m_port; +} + +template +Port& BaseServer::port() noexcept +{ + return m_port; +} + +} // namespace popo +} // namespace iox + +#endif // IOX_POSH_POPO_BASE_SERVER_INL