You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
270 lines
9.2 KiB
270 lines
9.2 KiB
// Copyright 2015 The Chromium Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#ifndef IPC_IPC_MESSAGE_TEMPLATES_H_
|
|
#define IPC_IPC_MESSAGE_TEMPLATES_H_
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <tuple>
|
|
#include <type_traits>
|
|
#include <utility>
|
|
|
|
#include "base/logging.h"
|
|
#include "base/trace_event/trace_event.h"
|
|
#include "base/tuple.h"
|
|
#include "build/build_config.h"
|
|
#include "ipc/ipc_message.h"
|
|
#include "ipc/ipc_message_utils.h"
|
|
|
|
namespace IPC {
|
|
|
|
template <typename Tuple, size_t... Ns>
|
|
auto TupleForwardImpl(Tuple&& tuple, std::index_sequence<Ns...>) -> decltype(
|
|
std::forward_as_tuple(std::get<Ns>(std::forward<Tuple>(tuple))...)) {
|
|
return std::forward_as_tuple(std::get<Ns>(std::forward<Tuple>(tuple))...);
|
|
}
|
|
|
|
// Transforms std::tuple contents to the forwarding form.
|
|
// Example:
|
|
// std::tuple<int, int&, const int&, int&&>&&
|
|
// -> std::tuple<int&&, int&, const int&, int&&>.
|
|
// const std::tuple<int, const int&, int&&>&
|
|
// -> std::tuple<const int&, int&, const int&, int&>.
|
|
//
|
|
// TupleForward(std::make_tuple(a, b, c)) is equivalent to
|
|
// std::forward_as_tuple(a, b, c).
|
|
template <typename Tuple>
|
|
auto TupleForward(Tuple&& tuple) -> decltype(TupleForwardImpl(
|
|
std::forward<Tuple>(tuple),
|
|
std::make_index_sequence<std::tuple_size<std::decay_t<Tuple>>::value>())) {
|
|
return TupleForwardImpl(
|
|
std::forward<Tuple>(tuple),
|
|
std::make_index_sequence<std::tuple_size<std::decay_t<Tuple>>::value>());
|
|
}
|
|
|
|
// This function is for all the async IPCs that don't pass an extra parameter
|
|
// using IPC_BEGIN_MESSAGE_MAP_WITH_PARAM.
|
|
template <typename ObjT, typename Method, typename P, typename Tuple>
|
|
void DispatchToMethod(ObjT* obj, Method method, P*, Tuple&& tuple) {
|
|
base::DispatchToMethod(obj, method, std::forward<Tuple>(tuple));
|
|
}
|
|
|
|
template <typename ObjT,
|
|
typename Method,
|
|
typename P,
|
|
typename Tuple,
|
|
size_t... Ns>
|
|
void DispatchToMethodImpl(ObjT* obj,
|
|
Method method,
|
|
P* parameter,
|
|
Tuple&& tuple,
|
|
std::index_sequence<Ns...>) {
|
|
(obj->*method)(parameter, std::get<Ns>(std::forward<Tuple>(tuple))...);
|
|
}
|
|
|
|
// The following function is for async IPCs which have a dispatcher with an
|
|
// extra parameter specified using IPC_BEGIN_MESSAGE_MAP_WITH_PARAM.
|
|
template <typename ObjT, typename P, typename... Args, typename Tuple>
|
|
std::enable_if_t<sizeof...(Args) == std::tuple_size<std::decay_t<Tuple>>::value>
|
|
DispatchToMethod(ObjT* obj,
|
|
void (ObjT::*method)(P*, Args...),
|
|
P* parameter,
|
|
Tuple&& tuple) {
|
|
constexpr size_t size = std::tuple_size<std::decay_t<Tuple>>::value;
|
|
DispatchToMethodImpl(obj, method, parameter, std::forward<Tuple>(tuple),
|
|
std::make_index_sequence<size>());
|
|
}
|
|
|
|
enum class MessageKind {
|
|
CONTROL,
|
|
ROUTED,
|
|
};
|
|
|
|
// Routing is a helper struct so MessageT's private common constructor has a
|
|
// different type signature than the public "int32_t routing_id" one.
|
|
struct Routing {
|
|
explicit Routing(int32_t id) : id(id) {}
|
|
int32_t id;
|
|
};
|
|
|
|
// We want to restrict MessageT's constructors so that a routing_id is always
|
|
// provided for ROUTED messages and never provided for CONTROL messages, so
|
|
// use the SFINAE technique from N4387's "Implementation Hint" section.
|
|
#if defined(COMPILER_MSVC)
|
|
// MSVC 2013 doesn't support default arguments for template member functions
|
|
// of templated classes, so there we have to rely on the DCHECKs instead.
|
|
// TODO(mdempsky): Reevaluate once MSVC 2015.
|
|
#define IPC_MESSAGET_SFINAE(x)
|
|
#else
|
|
#define IPC_MESSAGET_SFINAE(x) \
|
|
template <bool X = (x), typename std::enable_if<X, bool>::type = false>
|
|
#endif
|
|
|
|
// MessageT is the common template used for all user-defined message types.
|
|
// It's intended to be used via the macros defined in ipc_message_macros.h.
|
|
template <typename Meta,
|
|
typename InTuple = typename Meta::InTuple,
|
|
typename OutTuple = typename Meta::OutTuple>
|
|
class MessageT;
|
|
|
|
// Asynchronous message partial specialization.
|
|
template <typename Meta, typename... Ins>
|
|
class MessageT<Meta, std::tuple<Ins...>, void> : public Message {
|
|
public:
|
|
using Param = std::tuple<Ins...>;
|
|
enum { ID = Meta::ID };
|
|
|
|
// TODO(mdempsky): Remove. Uses of MyMessage::Schema::Param can be replaced
|
|
// with just MyMessage::Param.
|
|
using Schema = MessageT;
|
|
|
|
IPC_MESSAGET_SFINAE(Meta::kKind == MessageKind::CONTROL)
|
|
MessageT(const Ins&... ins) : MessageT(Routing(MSG_ROUTING_CONTROL), ins...) {
|
|
DCHECK(Meta::kKind == MessageKind::CONTROL) << Meta::kName;
|
|
}
|
|
|
|
IPC_MESSAGET_SFINAE(Meta::kKind == MessageKind::ROUTED)
|
|
MessageT(int32_t routing_id, const Ins&... ins)
|
|
: MessageT(Routing(routing_id), ins...) {
|
|
DCHECK(Meta::kKind == MessageKind::ROUTED) << Meta::kName;
|
|
}
|
|
|
|
static bool Read(const Message* msg, Param* p);
|
|
static void Log(std::string* name, const Message* msg, std::string* l);
|
|
|
|
template <class T, class S, class P, class Method>
|
|
static bool Dispatch(const Message* msg,
|
|
T* obj,
|
|
S* sender,
|
|
P* parameter,
|
|
Method func) {
|
|
TRACE_EVENT0("ipc", Meta::kName);
|
|
Param p;
|
|
if (Read(msg, &p)) {
|
|
DispatchToMethod(obj, func, parameter, std::move(p));
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private:
|
|
MessageT(Routing routing, const Ins&... ins);
|
|
};
|
|
|
|
// Synchronous message partial specialization.
|
|
template <typename Meta, typename... Ins, typename... Outs>
|
|
class MessageT<Meta, std::tuple<Ins...>, std::tuple<Outs...>>
|
|
: public SyncMessage {
|
|
public:
|
|
using SendParam = std::tuple<Ins...>;
|
|
using ReplyParam = std::tuple<Outs...>;
|
|
enum { ID = Meta::ID };
|
|
|
|
// TODO(mdempsky): Remove. Uses of MyMessage::Schema::{Send,Reply}Param can
|
|
// be replaced with just MyMessage::{Send,Reply}Param.
|
|
using Schema = MessageT;
|
|
|
|
IPC_MESSAGET_SFINAE(Meta::kKind == MessageKind::CONTROL)
|
|
MessageT(const Ins&... ins, Outs*... outs)
|
|
: MessageT(Routing(MSG_ROUTING_CONTROL), ins..., outs...) {
|
|
DCHECK(Meta::kKind == MessageKind::CONTROL) << Meta::kName;
|
|
}
|
|
|
|
IPC_MESSAGET_SFINAE(Meta::kKind == MessageKind::ROUTED)
|
|
MessageT(int32_t routing_id, const Ins&... ins, Outs*... outs)
|
|
: MessageT(Routing(routing_id), ins..., outs...) {
|
|
DCHECK(Meta::kKind == MessageKind::ROUTED) << Meta::kName;
|
|
}
|
|
|
|
static bool ReadSendParam(const Message* msg, SendParam* p);
|
|
static bool ReadReplyParam(const Message* msg, ReplyParam* p);
|
|
static void WriteReplyParams(Message* reply, const Outs&... outs);
|
|
static void Log(std::string* name, const Message* msg, std::string* l);
|
|
|
|
template <class T, class S, class P, class Method>
|
|
static bool Dispatch(const Message* msg,
|
|
T* obj,
|
|
S* sender,
|
|
P* /* parameter */,
|
|
Method func) {
|
|
TRACE_EVENT0("ipc", Meta::kName);
|
|
SendParam send_params;
|
|
bool ok = ReadSendParam(msg, &send_params);
|
|
Message* reply = SyncMessage::GenerateReply(msg);
|
|
if (!ok) {
|
|
NOTREACHED() << "Error deserializing message " << msg->type();
|
|
reply->set_reply_error();
|
|
sender->Send(reply);
|
|
return false;
|
|
}
|
|
|
|
ReplyParam reply_params;
|
|
base::DispatchToMethod(obj, func, std::move(send_params), &reply_params);
|
|
WriteParam(reply, reply_params);
|
|
LogReplyParamsToMessage(reply_params, msg);
|
|
sender->Send(reply);
|
|
return true;
|
|
}
|
|
|
|
template <class T, class P, class Method>
|
|
static bool DispatchDelayReply(const Message* msg,
|
|
T* obj,
|
|
P* /* parameter */,
|
|
Method func) {
|
|
TRACE_EVENT0("ipc", Meta::kName);
|
|
SendParam send_params;
|
|
bool ok = ReadSendParam(msg, &send_params);
|
|
Message* reply = SyncMessage::GenerateReply(msg);
|
|
if (!ok) {
|
|
NOTREACHED() << "Error deserializing message " << msg->type();
|
|
reply->set_reply_error();
|
|
obj->Send(reply);
|
|
return false;
|
|
}
|
|
|
|
std::tuple<Message&> t = std::tie(*reply);
|
|
ConnectMessageAndReply(msg, reply);
|
|
base::DispatchToMethod(obj, func, std::move(send_params), &t);
|
|
return true;
|
|
}
|
|
|
|
template <class T, class P, class Method>
|
|
static bool DispatchWithParamDelayReply(const Message* msg,
|
|
T* obj,
|
|
P* parameter,
|
|
Method func) {
|
|
TRACE_EVENT0("ipc", Meta::kName);
|
|
SendParam send_params;
|
|
bool ok = ReadSendParam(msg, &send_params);
|
|
Message* reply = SyncMessage::GenerateReply(msg);
|
|
if (!ok) {
|
|
NOTREACHED() << "Error deserializing message " << msg->type();
|
|
reply->set_reply_error();
|
|
obj->Send(reply);
|
|
return false;
|
|
}
|
|
|
|
std::tuple<Message&> t = std::tie(*reply);
|
|
ConnectMessageAndReply(msg, reply);
|
|
std::tuple<P*> parameter_tuple(parameter);
|
|
base::DispatchToMethod(
|
|
obj, func,
|
|
std::tuple_cat(std::move(parameter_tuple), TupleForward(send_params)),
|
|
&t);
|
|
return true;
|
|
}
|
|
|
|
private:
|
|
MessageT(Routing routing, const Ins&... ins, Outs*... outs);
|
|
};
|
|
|
|
} // namespace IPC
|
|
|
|
#if defined(IPC_MESSAGE_IMPL)
|
|
#include "ipc/ipc_message_templates_impl.h"
|
|
#endif
|
|
|
|
#endif // IPC_IPC_MESSAGE_TEMPLATES_H_
|