1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257
|
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef IPC_IPC_SYNC_CHANNEL_H_
#define IPC_IPC_SYNC_CHANNEL_H_
#include <memory>
#include <string>
#include <vector>
#include "base/component_export.h"
#include "base/containers/circular_deque.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/ref_counted.h"
#include "base/synchronization/lock.h"
#include "base/synchronization/waitable_event_watcher.h"
#include "base/task/single_thread_task_runner.h"
#include "ipc/ipc_channel_handle.h"
#include "ipc/ipc_channel_proxy.h"
#include "ipc/ipc_sync_message.h"
#include "ipc/ipc_sync_message_filter.h"
#include "mojo/public/c/system/types.h"
#include "mojo/public/cpp/system/simple_watcher.h"
namespace base {
class RunLoop;
class WaitableEvent;
} // namespace base
namespace mojo {
class SyncHandleRegistry;
}
namespace IPC {
class SyncMessage;
// This is similar to ChannelProxy, with the added feature of supporting sending
// synchronous messages.
//
// Overview of how the sync channel works
// --------------------------------------
// When the sending thread sends a synchronous message, we create a bunch
// of tracking info (created in Send, stored in the PendingSyncMsg
// structure) associated with the message that we identify by the unique
// "MessageId" on the SyncMessage. Among the things we save is the
// "Deserializer" which is provided by the sync message. This object is in
// charge of reading the parameters from the reply message and putting them in
// the output variables provided by its caller.
//
// The info gets stashed in a queue since we could have a nested stack of sync
// messages (each side could send sync messages in response to sync messages,
// so it works like calling a function). The message is sent to the I/O thread
// for dispatch and the original thread blocks waiting for the reply.
//
// SyncContext maintains the queue in a threadsafe way and listens for replies
// on the I/O thread. When a reply comes in that matches one of the messages
// it's looking for (using the unique message ID), it will execute the
// deserializer stashed from before, and unblock the original thread.
//
//
// Significant complexity results from the fact that messages are still coming
// in while the original thread is blocked. Normal async messages are queued
// and dispatched after the blocking call is complete. Sync messages must
// be dispatched in a reentrant manner to avoid deadlock.
//
//
// Note that care must be taken that the lifetime of the ipc_thread argument
// is more than this object. If the message loop goes away while this object
// is running and it's used to send a message, then it will use the invalid
// message loop pointer to proxy it to the ipc thread.
class COMPONENT_EXPORT(IPC) SyncChannel : public ChannelProxy {
public:
class ReceivedSyncMsgQueue;
enum RestrictDispatchGroup {
kRestrictDispatchGroup_None = 0,
};
// Creates and initializes a sync channel. If create_pipe_now is specified,
// the channel will be initialized synchronously.
// The naming pattern follows IPC::Channel.
static std::unique_ptr<SyncChannel> Create(
const IPC::ChannelHandle& channel_handle,
IPC::Channel::Mode mode,
Listener* listener,
const scoped_refptr<base::SingleThreadTaskRunner>& ipc_task_runner,
const scoped_refptr<base::SingleThreadTaskRunner>& listener_task_runner,
bool create_pipe_now,
base::WaitableEvent* shutdown_event);
// Creates an uninitialized sync channel. Call ChannelProxy::Init to
// initialize the channel. This two-step setup allows message filters to be
// added before any messages are sent or received.
static std::unique_ptr<SyncChannel> Create(
Listener* listener,
const scoped_refptr<base::SingleThreadTaskRunner>& ipc_task_runner,
const scoped_refptr<base::SingleThreadTaskRunner>& listener_task_runner,
base::WaitableEvent* shutdown_event);
void AddListenerTaskRunner(
int32_t routing_id,
scoped_refptr<base::SingleThreadTaskRunner> task_runner);
void RemoveListenerTaskRunner(int32_t routing_id);
SyncChannel(const SyncChannel&) = delete;
SyncChannel& operator=(const SyncChannel&) = delete;
~SyncChannel() override;
bool Send(Message* message) override;
// Sets the dispatch group for this channel, to only allow re-entrant dispatch
// of messages to other channels in the same group.
//
// Normally, any unblocking message coming from any channel can be dispatched
// when any (possibly other) channel is blocked on sending a message. This is
// needed in some cases to unblock certain loops (e.g. necessary when some
// processes share a window hierarchy), but may cause re-entrancy issues in
// some cases where such loops are not possible. This flags allows the tagging
// of some particular channels to only re-enter in known correct cases.
//
// Incoming messages on channels belonging to a group that is not
// kRestrictDispatchGroup_None will only be dispatched while a sync message is
// being sent on a channel of the *same* group.
// Incoming messages belonging to the kRestrictDispatchGroup_None group (the
// default) will be dispatched in any case.
void SetRestrictDispatchChannelGroup(int group);
// Creates a new IPC::SyncMessageFilter and adds it to this SyncChannel.
// This should be used instead of directly constructing a new
// SyncMessageFilter.
scoped_refptr<IPC::SyncMessageFilter> CreateSyncMessageFilter();
protected:
friend class ReceivedSyncMsgQueue;
// SyncContext holds the per object data for SyncChannel, so that SyncChannel
// can be deleted while it's being used in a different thread. See
// ChannelProxy::Context for more information.
class SyncContext : public Context {
public:
SyncContext(
Listener* listener,
const scoped_refptr<base::SingleThreadTaskRunner>& ipc_task_runner,
const scoped_refptr<base::SingleThreadTaskRunner>& listener_task_runner,
base::WaitableEvent* shutdown_event);
// Adds information about an outgoing sync message to the context so that
// we know how to deserialize the reply.
bool Push(SyncMessage* sync_msg);
// Cleanly remove the top deserializer (and throw it away). Returns the
// result of the Send call for that message.
bool Pop();
// Returns a Mojo Event that signals when a sync send is complete or timed
// out or the process shut down.
base::WaitableEvent* GetSendDoneEvent();
// Returns a Mojo Event that signals when an incoming message that's not the
// pending reply needs to get dispatched (by calling DispatchMessages.)
base::WaitableEvent* GetDispatchEvent();
void DispatchMessages();
// Checks if the given message is blocking the listener thread because of a
// synchronous send. If it is, the thread is unblocked and true is
// returned. Otherwise the function returns false.
bool TryToUnblockListener(const Message* msg);
base::WaitableEvent* shutdown_event() { return shutdown_event_; }
ReceivedSyncMsgQueue* received_sync_msgs() {
return received_sync_msgs_.get();
}
void set_restrict_dispatch_group(int group) {
restrict_dispatch_group_ = group;
}
int restrict_dispatch_group() const {
return restrict_dispatch_group_;
}
void OnSendDoneEventSignaled(base::RunLoop* nested_loop,
base::WaitableEvent* event);
private:
~SyncContext() override;
// ChannelProxy methods that we override.
// Called on the listener thread.
void Clear() override;
// Called on the IPC thread.
bool OnMessageReceived(const Message& msg) override;
void OnChannelError() override;
void OnChannelOpened() override;
void OnChannelClosed() override;
// Cancels all pending Send calls.
void CancelPendingSends();
void OnShutdownEventSignaled(base::WaitableEvent* event);
using PendingSyncMessageQueue = base::circular_deque<PendingSyncMsg>;
PendingSyncMessageQueue deserializers_;
bool reject_new_deserializers_ = false;
base::Lock deserializers_lock_;
scoped_refptr<ReceivedSyncMsgQueue> received_sync_msgs_;
raw_ptr<base::WaitableEvent, AcrossTasksDanglingUntriaged> shutdown_event_;
base::WaitableEventWatcher shutdown_watcher_;
base::WaitableEventWatcher::EventCallback shutdown_watcher_callback_;
int restrict_dispatch_group_;
};
private:
SyncChannel(
Listener* listener,
const scoped_refptr<base::SingleThreadTaskRunner>& ipc_task_runner,
const scoped_refptr<base::SingleThreadTaskRunner>& listener_task_runner,
base::WaitableEvent* shutdown_event);
void OnDispatchEventSignaled(base::WaitableEvent* event);
SyncContext* sync_context() {
return reinterpret_cast<SyncContext*>(context());
}
// Waits for a reply, timeout or process shutdown.
static void WaitForReply(mojo::SyncHandleRegistry* registry,
SyncContext* context);
// Starts the dispatch watcher.
void StartWatching();
// ChannelProxy overrides:
void OnChannelInit() override;
scoped_refptr<mojo::SyncHandleRegistry> sync_handle_registry_;
// Used to signal events between the IPC and listener threads.
base::WaitableEventWatcher dispatch_watcher_;
base::WaitableEventWatcher::EventCallback dispatch_watcher_callback_;
// Tracks SyncMessageFilters created before complete channel initialization.
std::vector<scoped_refptr<SyncMessageFilter>> pre_init_sync_message_filters_;
};
} // namespace IPC
#endif // IPC_IPC_SYNC_CHANNEL_H_
|