blob: 9adfa9c70298178a63cec59ec48b94f82e91ddf3 [file] [log] [blame]
// Copyright 2020 The Pigweed Authors
//
// 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
//
// https://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.
#pragma once
#include <cstddef>
#include <tuple>
#include "pw_containers/intrusive_list.h"
#include "pw_rpc/channel.h"
#include "pw_rpc/internal/call.h"
#include "pw_rpc/internal/channel.h"
#include "pw_rpc/internal/endpoint.h"
#include "pw_rpc/internal/lock.h"
#include "pw_rpc/internal/method.h"
#include "pw_rpc/internal/method_info.h"
#include "pw_rpc/internal/server_call.h"
#include "pw_rpc/service.h"
#include "pw_span/span.h"
#include "pw_status/status.h"
namespace pw::rpc {
class Server : public internal::Endpoint {
public:
_PW_RPC_CONSTEXPR Server(span<Channel> channels) : Endpoint(channels) {}
// Registers one or more services with the server. This should not be called
// directly with a Service; instead, use a generated class which inherits
// from it.
//
// This function may be called with any number of services. Combining
// registration into fewer calls is preferred so the RPC mutex is only
// locked/unlocked once.
template <typename... OtherServices>
void RegisterService(Service& service, OtherServices&... services)
PW_LOCKS_EXCLUDED(internal::rpc_lock()) {
internal::LockGuard lock(internal::rpc_lock());
services_.push_front(service); // Register the first service
// Register any additional services by expanding the parameter pack. This
// is a fold expression of the comma operator.
(services_.push_front(services), ...);
}
template <typename... OtherServices>
void UnregisterService(Service& service, OtherServices&... services)
PW_LOCKS_EXCLUDED(internal::rpc_lock()) {
internal::LockGuard lock(internal::rpc_lock());
UnregisterServiceLocked(service, static_cast<Service&>(services)...);
}
// Processes an RPC packet. The packet may contain an RPC request or a control
// packet, the result of which is processed in this function. Returns whether
// the packet was able to be processed:
//
// OK - The packet was processed by the server.
// DATA_LOSS - Failed to decode the packet.
// INVALID_ARGUMENT - The packet is intended for a client, not a server.
// UNAVAILABLE - No RPC channel with the requested ID was found.
Status ProcessPacket(ConstByteSpan packet_data)
PW_LOCKS_EXCLUDED(internal::rpc_lock());
private:
friend class internal::Call;
// Give call classes access to OpenContext.
friend class RawServerReaderWriter;
friend class RawServerWriter;
friend class RawServerReader;
friend class RawUnaryResponder;
template <typename, typename>
friend class NanopbServerReaderWriter;
template <typename>
friend class NanopbServerWriter;
template <typename, typename>
friend class NanopbServerReader;
template <typename>
friend class NanopbUnaryResponder;
template <typename, typename>
friend class PwpbServerReaderWriter;
template <typename>
friend class PwpbServerWriter;
template <typename, typename>
friend class PwpbServerReader;
template <typename>
friend class PwpbUnaryResponder;
// Creates a call context for a particular RPC. Unlike the CallContext
// constructor, this function checks the type of RPC at compile time.
template <auto kMethod,
MethodType kExpected,
typename ServiceImpl,
typename MethodImpl>
internal::CallContext OpenContext(uint32_t channel_id,
ServiceImpl& service,
const MethodImpl& method)
PW_EXCLUSIVE_LOCKS_REQUIRED(internal::rpc_lock()) {
using Info = internal::MethodInfo<kMethod>;
if constexpr (kExpected == MethodType::kUnary) {
static_assert(
Info::kType == kExpected,
"UnaryResponder objects may only be opened for unary RPCs.");
} else if constexpr (kExpected == MethodType::kServerStreaming) {
static_assert(
Info::kType == kExpected,
"ServerWriters may only be opened for server streaming RPCs.");
} else if constexpr (kExpected == MethodType::kClientStreaming) {
static_assert(
Info::kType == kExpected,
"ServerReaders may only be opened for client streaming RPCs.");
} else if constexpr (kExpected == MethodType::kBidirectionalStreaming) {
static_assert(Info::kType == kExpected,
"ServerReaderWriters may only be opened for bidirectional "
"streaming RPCs.");
}
return internal::CallContext(
*this, channel_id, service, method, internal::kOpenCallId);
}
std::tuple<Service*, const internal::Method*> FindMethod(
const internal::Packet& packet)
PW_EXCLUSIVE_LOCKS_REQUIRED(internal::rpc_lock());
void HandleClientStreamPacket(const internal::Packet& packet,
internal::Channel& channel,
internal::ServerCall* call) const
PW_UNLOCK_FUNCTION(internal::rpc_lock());
template <typename... OtherServices>
void UnregisterServiceLocked(Service& service, OtherServices&... services)
PW_EXCLUSIVE_LOCKS_REQUIRED(internal::rpc_lock()) {
services_.remove(service);
AbortCallsForService(service);
UnregisterServiceLocked(services...);
}
void UnregisterServiceLocked() {} // Base case; nothing left to do.
// Remove these internal::Endpoint functions from the public interface.
using Endpoint::active_call_count;
using Endpoint::GetInternalChannel;
IntrusiveList<Service> services_ PW_GUARDED_BY(internal::rpc_lock());
};
} // namespace pw::rpc