Bug 1794282 - Part 1: Introduce IPC::ChannelCapability, r=ipc-reviewers,mccr8

This patch introduces a new combined capability, ChannelCapability, which
combines the previous Mutex and IOThread capabilities into a single common
capability. This capability can be asserted using the `Note` methods when
holding the specific inner capabilities to either allow shared access to
guarded variables while holding a single capability, or exclusive access when
holding all capabilities.

This is similar to the MutexSingleWriter pattern, however this implementation
is more flexible, as it also allows interacting with each individual inner
capability individually.

This patch just migrates existing attributes and mutex accesses to their new
names. Changes to behaviour will happen in part 2.

Differential Revision: https://phabricator.services.mozilla.com/D160532
This commit is contained in:
Nika Layzell 2022-11-14 18:48:48 +00:00
parent d41c1cd077
commit 4c109c60e7
5 changed files with 226 additions and 136 deletions

View File

@ -0,0 +1,61 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef CHROME_COMMON_IPC_CHANNEL_CAPABILITY_H_
#define CHROME_COMMON_IPC_CHANNEL_CAPABILITY_H_
#include "mozilla/ThreadSafety.h"
#include "mozilla/Mutex.h"
#include "mozilla/EventTargetCapability.h"
#include "nsISerialEventTarget.h"
namespace IPC {
// A thread-safety capability used in IPC channel implementations. Combines an
// EventTargetCapability and a Mutex to allow using each independently, as well
// as combined together.
//
// The ChannelCapability grants shared access if on the IOThread or if the send
// mutex is held, and only allows exclusive access if both on the IO thread and
// holding the send mutex. This is similar to a `MutexSingleWriter`, but more
// flexible due to providing access to each sub-capability.
class MOZ_CAPABILITY("channel cap") ChannelCapability {
public:
using Mutex = mozilla::Mutex;
using Thread = mozilla::EventTargetCapability<nsISerialEventTarget>;
ChannelCapability(const char* mutex_name, nsISerialEventTarget* io_thread)
: send_mutex_(mutex_name), io_thread_(io_thread) {}
const Thread& IOThread() const MOZ_RETURN_CAPABILITY(io_thread_) {
return io_thread_;
}
Mutex& SendMutex() MOZ_RETURN_CAPABILITY(send_mutex_) { return send_mutex_; }
// Note that we're on the IO thread, and thus have shared access to values
// guarded by the channel capability for the thread-safety analysis.
void NoteOnIOThread() const MOZ_REQUIRES(io_thread_)
MOZ_ASSERT_SHARED_CAPABILITY(this) {}
// Note that we're holding the send mutex, and thus have shared access to
// values guarded by the channel capability for the thread-safety analysis.
void NoteSendMutex() const MOZ_REQUIRES(send_mutex_)
MOZ_ASSERT_SHARED_CAPABILITY(this) {}
// Note that we're holding the send mutex while on the IO thread, and thus
// have exclusive access to values guarded by the channel capability for the
// thread-safety analysis.
void NoteExclusiveAccess() const MOZ_REQUIRES(io_thread_, send_mutex_)
MOZ_ASSERT_CAPABILITY(this) {}
private:
mozilla::Mutex send_mutex_;
mozilla::EventTargetCapability<nsISerialEventTarget> io_thread_;
};
} // namespace IPC
#endif // CHROME_COMMON_IPC_CHANNEL_CAPABILITY_H_

View File

@ -9,6 +9,7 @@
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include "mozilla/Mutex.h"
#if defined(OS_MACOSX)
# include <mach/message.h>
# include <mach/port.h>
@ -143,7 +144,8 @@ void Channel::SetClientChannelFd(int fd) { gClientChannelFd = fd; }
Channel::ChannelImpl::ChannelImpl(const ChannelId& channel_id, Mode mode,
Listener* listener)
: io_thread_(MessageLoopForIO::current()->SerialEventTarget()) {
: chan_cap_("ChannelImpl::SendMutex",
MessageLoopForIO::current()->SerialEventTarget()) {
Init(mode, listener);
if (!CreatePipe(mode)) {
@ -158,7 +160,8 @@ Channel::ChannelImpl::ChannelImpl(const ChannelId& channel_id, Mode mode,
Channel::ChannelImpl::ChannelImpl(ChannelHandle pipe, Mode mode,
Listener* listener)
: io_thread_(MessageLoopForIO::current()->SerialEventTarget()) {
: chan_cap_("ChannelImpl::SendMutex",
MessageLoopForIO::current()->SerialEventTarget()) {
Init(mode, listener);
SetPipe(pipe.release());
@ -166,7 +169,7 @@ Channel::ChannelImpl::ChannelImpl(ChannelHandle pipe, Mode mode,
}
void Channel::ChannelImpl::SetPipe(int fd) {
io_thread_.AssertOnCurrentThread();
IOThread().AssertOnCurrentThread();
pipe_ = fd;
pipe_buf_len_ = 0;
@ -249,8 +252,8 @@ bool Channel::ChannelImpl::EnqueueHelloMessage() {
}
bool Channel::ChannelImpl::Connect() {
io_thread_.AssertOnCurrentThread();
mozilla::MutexAutoLock lock(mutex_);
IOThread().AssertOnCurrentThread();
mozilla::MutexAutoLock lock(SendMutex());
return ConnectLocked();
}
@ -525,7 +528,7 @@ bool Channel::ChannelImpl::ProcessIncomingMessages() {
// The Hello message contains only the process id.
other_pid_ = MessageIterator(m).NextInt();
int32_t other_pid = other_pid_;
mozilla::MutexAutoUnlock unlock(mutex_);
mozilla::MutexAutoUnlock unlock(SendMutex());
listener_->OnChannelConnected(other_pid);
#if defined(OS_MACOSX)
} else if (m.routing_id() == MSG_ROUTING_NONE &&
@ -540,7 +543,7 @@ bool Channel::ChannelImpl::ProcessIncomingMessages() {
return false;
}
#endif
mozilla::MutexAutoUnlock unlock(mutex_);
mozilla::MutexAutoUnlock unlock(SendMutex());
listener_->OnMessageReceived(std::move(incoming_message_));
}
@ -561,7 +564,7 @@ bool Channel::ChannelImpl::ProcessIncomingMessages() {
}
bool Channel::ChannelImpl::ProcessOutgoingMessages() {
// NOTE: This method may be called on threads other than `io_thread_`.
// NOTE: This method may be called on threads other than `IOThread()`.
DCHECK(!waiting_connect_); // Why are we trying to send messages if there's
// no connection?
is_blocked_on_write_ = false;
@ -707,7 +710,7 @@ bool Channel::ChannelImpl::ProcessOutgoingMessages() {
// Because this is likely to result in a busy-wait, we'll try to make
// it easier for the receiver to make progress, but only if we're on
// the I/O thread already.
if (io_thread_.IsOnCurrentThread()) {
if (IOThread().IsOnCurrentThread()) {
sched_yield();
}
break;
@ -735,7 +738,7 @@ bool Channel::ChannelImpl::ProcessOutgoingMessages() {
}
is_blocked_on_write_ = true;
if (io_thread_.IsOnCurrentThread()) {
if (IOThread().IsOnCurrentThread()) {
// If we're on the I/O thread already, tell libevent to call us back
// when things are unblocked.
MessageLoopForIO::current()->WatchFileDescriptor(
@ -746,7 +749,7 @@ bool Channel::ChannelImpl::ProcessOutgoingMessages() {
// Otherwise, emulate being called back from libevent on the I/O thread,
// which will re-try the write, and then potentially start watching if
// still necessary.
io_thread_.Dispatch(mozilla::NewRunnableMethod<int>(
IOThread().Dispatch(mozilla::NewRunnableMethod<int>(
"ChannelImpl::ContinueProcessOutgoing", this,
&ChannelImpl::OnFileCanWriteWithoutBlocking, -1));
}
@ -785,8 +788,8 @@ bool Channel::ChannelImpl::ProcessOutgoingMessages() {
}
bool Channel::ChannelImpl::Send(mozilla::UniquePtr<Message> message) {
// NOTE: This method may be called on threads other than `io_thread_`.
mozilla::MutexAutoLock lock(mutex_);
// NOTE: This method may be called on threads other than `IOThread()`.
mozilla::MutexAutoLock lock(SendMutex());
#ifdef IPC_MESSAGE_DEBUG_EXTRA
DLOG(INFO) << "sending message @" << message.get() << " on channel @" << this
@ -824,14 +827,14 @@ bool Channel::ChannelImpl::Send(mozilla::UniquePtr<Message> message) {
void Channel::ChannelImpl::GetClientFileDescriptorMapping(int* src_fd,
int* dest_fd) const {
io_thread_.AssertOnCurrentThread();
IOThread().AssertOnCurrentThread();
DCHECK(mode_ == MODE_SERVER);
*src_fd = client_pipe_;
*dest_fd = gClientChannelFd;
}
void Channel::ChannelImpl::CloseClientFileDescriptor() {
io_thread_.AssertOnCurrentThread();
IOThread().AssertOnCurrentThread();
if (client_pipe_ != -1) {
IGNORE_EINTR(close(client_pipe_));
client_pipe_ = -1;
@ -840,8 +843,8 @@ void Channel::ChannelImpl::CloseClientFileDescriptor() {
// Called by libevent when we can read from th pipe without blocking.
void Channel::ChannelImpl::OnFileCanReadWithoutBlocking(int fd) {
io_thread_.AssertOnCurrentThread();
mozilla::ReleasableMutexAutoLock lock(mutex_);
IOThread().AssertOnCurrentThread();
mozilla::ReleasableMutexAutoLock lock(SendMutex());
if (!waiting_connect_ && fd == pipe_ && pipe_ != -1) {
if (!ProcessIncomingMessages()) {
@ -886,8 +889,8 @@ void Channel::ChannelImpl::OutputQueuePop() {
// Called by libevent when we can write to the pipe without blocking.
void Channel::ChannelImpl::OnFileCanWriteWithoutBlocking(int fd) {
RefPtr<ChannelImpl> grip(this);
io_thread_.AssertOnCurrentThread();
mozilla::ReleasableMutexAutoLock lock(mutex_);
IOThread().AssertOnCurrentThread();
mozilla::ReleasableMutexAutoLock lock(SendMutex());
if (pipe_ != -1 && !ProcessOutgoingMessages()) {
CloseLocked();
lock.Unlock();
@ -896,8 +899,8 @@ void Channel::ChannelImpl::OnFileCanWriteWithoutBlocking(int fd) {
}
void Channel::ChannelImpl::Close() {
io_thread_.AssertOnCurrentThread();
mozilla::MutexAutoLock lock(mutex_);
IOThread().AssertOnCurrentThread();
mozilla::MutexAutoLock lock(SendMutex());
CloseLocked();
}
@ -937,8 +940,8 @@ void Channel::ChannelImpl::CloseLocked() {
#if defined(OS_MACOSX)
void Channel::ChannelImpl::SetOtherMachTask(task_t task) {
io_thread_.AssertOnCurrentThread();
mozilla::MutexAutoLock lock(mutex_);
IOThread().AssertOnCurrentThread();
mozilla::MutexAutoLock lock(SendMutex());
if (NS_WARN_IF(pipe_ == -1)) {
return;
@ -951,8 +954,8 @@ void Channel::ChannelImpl::SetOtherMachTask(task_t task) {
}
void Channel::ChannelImpl::StartAcceptingMachPorts(Mode mode) {
io_thread_.AssertOnCurrentThread();
mozilla::MutexAutoLock lock(mutex_);
IOThread().AssertOnCurrentThread();
mozilla::MutexAutoLock lock(SendMutex());
if (accept_mach_ports_) {
MOZ_ASSERT(privileged_ == (MODE_SERVER == mode));

View File

@ -8,6 +8,7 @@
#define CHROME_COMMON_IPC_CHANNEL_POSIX_H_
#include "chrome/common/ipc_channel.h"
#include "chrome/common/ipc_channel_capability.h"
#include <sys/socket.h> // for CMSG macros
@ -19,9 +20,7 @@
#include "base/message_loop.h"
#include "base/task.h"
#include "mozilla/EventTargetCapability.h"
#include "mozilla/Maybe.h"
#include "mozilla/Mutex.h"
#include "mozilla/Queue.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/UniquePtrExtensions.h"
@ -34,59 +33,60 @@ namespace IPC {
class Channel::ChannelImpl : public MessageLoopForIO::Watcher {
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING_WITH_DELETE_ON_EVENT_TARGET(
ChannelImpl, io_thread_.GetEventTarget());
ChannelImpl, IOThread().GetEventTarget());
using ChannelId = Channel::ChannelId;
// Mirror methods of Channel, see ipc_channel.h for description.
ChannelImpl(const ChannelId& channel_id, Mode mode, Listener* listener);
ChannelImpl(ChannelHandle pipe, Mode mode, Listener* listener);
bool Connect() MOZ_EXCLUDES(mutex_);
void Close() MOZ_EXCLUDES(mutex_);
bool Connect() MOZ_EXCLUDES(SendMutex());
void Close() MOZ_EXCLUDES(SendMutex());
Listener* set_listener(Listener* listener) {
io_thread_.AssertOnCurrentThread();
IOThread().AssertOnCurrentThread();
Listener* old = listener_;
listener_ = listener;
return old;
}
// NOTE: `Send` may be called on threads other than the I/O thread.
bool Send(mozilla::UniquePtr<Message> message) MOZ_EXCLUDES(mutex_);
bool Send(mozilla::UniquePtr<Message> message) MOZ_EXCLUDES(SendMutex());
void GetClientFileDescriptorMapping(int* src_fd, int* dest_fd) const;
void CloseClientFileDescriptor();
int32_t OtherPid() MOZ_EXCLUDES(mutex_) {
io_thread_.AssertOnCurrentThread();
mozilla::MutexAutoLock lock(mutex_);
int32_t OtherPid() MOZ_EXCLUDES(SendMutex()) {
IOThread().AssertOnCurrentThread();
mozilla::MutexAutoLock lock(SendMutex());
return other_pid_;
}
// See the comment in ipc_channel.h for info on IsClosed()
// NOTE: `IsClosed` may be called on threads other than the I/O thread.
bool IsClosed() MOZ_EXCLUDES(mutex_) {
mozilla::MutexAutoLock lock(mutex_);
bool IsClosed() MOZ_EXCLUDES(SendMutex()) {
mozilla::MutexAutoLock lock(SendMutex());
return pipe_ == -1;
}
#if defined(OS_MACOSX)
void SetOtherMachTask(task_t task) MOZ_EXCLUDES(mutex_);
void SetOtherMachTask(task_t task) MOZ_EXCLUDES(SendMutex());
void StartAcceptingMachPorts(Mode mode) MOZ_EXCLUDES(mutex_);
void StartAcceptingMachPorts(Mode mode) MOZ_EXCLUDES(SendMutex());
#endif
private:
~ChannelImpl() { Close(); }
void Init(Mode mode, Listener* listener) MOZ_REQUIRES(mutex_, io_thread_);
bool CreatePipe(Mode mode) MOZ_REQUIRES(mutex_, io_thread_);
void SetPipe(int fd) MOZ_REQUIRES(mutex_, io_thread_);
bool PipeBufHasSpaceAfter(size_t already_written) MOZ_REQUIRES(mutex_);
bool EnqueueHelloMessage() MOZ_REQUIRES(mutex_, io_thread_);
bool ConnectLocked() MOZ_REQUIRES(mutex_, io_thread_);
void CloseLocked() MOZ_REQUIRES(mutex_, io_thread_);
void Init(Mode mode, Listener* listener)
MOZ_REQUIRES(SendMutex(), IOThread());
bool CreatePipe(Mode mode) MOZ_REQUIRES(SendMutex(), IOThread());
void SetPipe(int fd) MOZ_REQUIRES(SendMutex(), IOThread());
bool PipeBufHasSpaceAfter(size_t already_written) MOZ_REQUIRES(SendMutex());
bool EnqueueHelloMessage() MOZ_REQUIRES(SendMutex(), IOThread());
bool ConnectLocked() MOZ_REQUIRES(SendMutex(), IOThread());
void CloseLocked() MOZ_REQUIRES(SendMutex(), IOThread());
bool ProcessIncomingMessages() MOZ_REQUIRES(mutex_, io_thread_);
bool ProcessOutgoingMessages() MOZ_REQUIRES(mutex_);
bool ProcessIncomingMessages() MOZ_REQUIRES(SendMutex(), IOThread());
bool ProcessOutgoingMessages() MOZ_REQUIRES(SendMutex());
// MessageLoopForIO::Watcher implementation.
virtual void OnFileCanReadWithoutBlocking(int fd) override;
@ -94,31 +94,42 @@ class Channel::ChannelImpl : public MessageLoopForIO::Watcher {
#if defined(OS_MACOSX)
void CloseDescriptors(uint32_t pending_fd_id)
MOZ_REQUIRES(mutex_, io_thread_);
MOZ_REQUIRES(SendMutex(), IOThread());
// Called on a Message immediately before it is sent/recieved to transfer
// handles to the remote process, or accept handles from the remote process.
bool AcceptMachPorts(Message& msg) MOZ_REQUIRES(mutex_, io_thread_);
bool TransferMachPorts(Message& msg) MOZ_REQUIRES(mutex_);
bool AcceptMachPorts(Message& msg) MOZ_REQUIRES(SendMutex(), IOThread());
bool TransferMachPorts(Message& msg) MOZ_REQUIRES(SendMutex());
#endif
void OutputQueuePush(mozilla::UniquePtr<Message> msg) MOZ_REQUIRES(mutex_);
void OutputQueuePop() MOZ_REQUIRES(mutex_);
void OutputQueuePush(mozilla::UniquePtr<Message> msg)
MOZ_REQUIRES(SendMutex());
void OutputQueuePop() MOZ_REQUIRES(SendMutex());
mozilla::Mutex mutex_{"ChannelImpl"};
const mozilla::EventTargetCapability<nsISerialEventTarget> io_thread_;
const ChannelCapability::Thread& IOThread() const
MOZ_RETURN_CAPABILITY(chan_cap_.IOThread()) {
return chan_cap_.IOThread();
}
Mode mode_ MOZ_GUARDED_BY(io_thread_);
ChannelCapability::Mutex& SendMutex()
MOZ_RETURN_CAPABILITY(chan_cap_.SendMutex()) {
return chan_cap_.SendMutex();
}
// Compound capability of a Mutex and the IO thread.
ChannelCapability chan_cap_;
Mode mode_ MOZ_GUARDED_BY(IOThread());
// After accepting one client connection on our server socket we want to
// stop listening.
MessageLoopForIO::FileDescriptorWatcher read_watcher_
MOZ_GUARDED_BY(io_thread_);
MOZ_GUARDED_BY(IOThread());
MessageLoopForIO::FileDescriptorWatcher write_watcher_
MOZ_GUARDED_BY(io_thread_);
MOZ_GUARDED_BY(IOThread());
// Indicates whether we're currently blocked waiting for a write to complete.
bool is_blocked_on_write_ MOZ_GUARDED_BY(mutex_);
bool is_blocked_on_write_ MOZ_GUARDED_BY(SendMutex());
// If sending a message blocks then we use this iterator to keep track of
// where in the message we are. It gets reset when the message is finished
@ -127,24 +138,24 @@ class Channel::ChannelImpl : public MessageLoopForIO::Watcher {
Pickle::BufferList::IterImpl iter_;
mozilla::Span<const mozilla::UniqueFileHandle> handles_;
};
mozilla::Maybe<PartialWrite> partial_write_ MOZ_GUARDED_BY(mutex_);
mozilla::Maybe<PartialWrite> partial_write_ MOZ_GUARDED_BY(SendMutex());
int pipe_ MOZ_GUARDED_BY(mutex_);
int pipe_ MOZ_GUARDED_BY(SendMutex());
// The client end of our socketpair().
int client_pipe_ MOZ_GUARDED_BY(io_thread_);
int client_pipe_ MOZ_GUARDED_BY(IOThread());
// The SO_SNDBUF value of pipe_, or 0 if unknown.
unsigned pipe_buf_len_ MOZ_GUARDED_BY(mutex_);
unsigned pipe_buf_len_ MOZ_GUARDED_BY(SendMutex());
Listener* listener_ MOZ_GUARDED_BY(io_thread_);
Listener* listener_ MOZ_GUARDED_BY(IOThread());
// Messages to be sent are queued here.
mozilla::Queue<mozilla::UniquePtr<Message>, 64> output_queue_
MOZ_GUARDED_BY(mutex_);
MOZ_GUARDED_BY(SendMutex());
// We read from the pipe into these buffers.
size_t input_buf_offset_ MOZ_GUARDED_BY(io_thread_);
mozilla::UniquePtr<char[]> input_buf_ MOZ_GUARDED_BY(io_thread_);
mozilla::UniquePtr<char[]> input_cmsg_buf_ MOZ_GUARDED_BY(io_thread_);
size_t input_buf_offset_ MOZ_GUARDED_BY(IOThread());
mozilla::UniquePtr<char[]> input_buf_ MOZ_GUARDED_BY(IOThread());
mozilla::UniquePtr<char[]> input_cmsg_buf_ MOZ_GUARDED_BY(IOThread());
// The control message buffer will hold all of the file descriptors that will
// be read in during a single recvmsg call. Message::WriteFileDescriptor
@ -165,18 +176,18 @@ class Channel::ChannelImpl : public MessageLoopForIO::Watcher {
// Large incoming messages that span multiple pipe buffers get built-up in the
// buffers of this message.
mozilla::UniquePtr<Message> incoming_message_ MOZ_GUARDED_BY(io_thread_);
std::vector<int> input_overflow_fds_ MOZ_GUARDED_BY(io_thread_);
mozilla::UniquePtr<Message> incoming_message_ MOZ_GUARDED_BY(IOThread());
std::vector<int> input_overflow_fds_ MOZ_GUARDED_BY(IOThread());
// Will be set to `true` until `Connect()` has been called and communication
// is ready. For privileged connections on macOS, this will not be cleared
// until the peer mach port has been provided to allow transferring mach
// ports.
bool waiting_connect_ MOZ_GUARDED_BY(mutex_) = true;
bool waiting_connect_ MOZ_GUARDED_BY(SendMutex()) = true;
// We keep track of the PID of the other side of this channel so that we can
// record this when generating logs of IPC messages.
int32_t other_pid_ MOZ_GUARDED_BY(mutex_) = -1;
int32_t other_pid_ MOZ_GUARDED_BY(SendMutex()) = -1;
#if defined(OS_MACOSX)
struct PendingDescriptors {
@ -184,19 +195,19 @@ class Channel::ChannelImpl : public MessageLoopForIO::Watcher {
nsTArray<mozilla::UniqueFileHandle> handles;
};
std::list<PendingDescriptors> pending_fds_ MOZ_GUARDED_BY(mutex_);
std::list<PendingDescriptors> pending_fds_ MOZ_GUARDED_BY(SendMutex());
// A generation ID for RECEIVED_FD messages.
uint32_t last_pending_fd_id_ MOZ_GUARDED_BY(mutex_);
uint32_t last_pending_fd_id_ MOZ_GUARDED_BY(SendMutex());
// Whether or not to accept mach ports from a remote process, and whether this
// process is the privileged side of a IPC::Channel which can transfer mach
// ports.
bool accept_mach_ports_ MOZ_GUARDED_BY(mutex_) = false;
bool privileged_ MOZ_GUARDED_BY(mutex_) = false;
bool accept_mach_ports_ MOZ_GUARDED_BY(SendMutex()) = false;
bool privileged_ MOZ_GUARDED_BY(SendMutex()) = false;
// If available, the task port for the remote process.
mozilla::UniqueMachSendRight other_task_ MOZ_GUARDED_BY(mutex_);
mozilla::UniqueMachSendRight other_task_ MOZ_GUARDED_BY(SendMutex());
#endif
DISALLOW_COPY_AND_ASSIGN(ChannelImpl);

View File

@ -48,7 +48,8 @@ Channel::ChannelImpl::State::~State() {
Channel::ChannelImpl::ChannelImpl(const ChannelId& channel_id, Mode mode,
Listener* listener)
: io_thread_(MessageLoopForIO::current()->SerialEventTarget()),
: chan_cap_("ChannelImpl::SendMutex",
MessageLoopForIO::current()->SerialEventTarget()),
ALLOW_THIS_IN_INITIALIZER_LIST(input_state_(this)),
ALLOW_THIS_IN_INITIALIZER_LIST(output_state_(this)) {
Init(mode, listener);
@ -63,7 +64,8 @@ Channel::ChannelImpl::ChannelImpl(const ChannelId& channel_id, Mode mode,
Channel::ChannelImpl::ChannelImpl(ChannelHandle pipe, Mode mode,
Listener* listener)
: io_thread_(MessageLoopForIO::current()->SerialEventTarget()),
: chan_cap_("ChannelImpl::SendMutex",
MessageLoopForIO::current()->SerialEventTarget()),
ALLOW_THIS_IN_INITIALIZER_LIST(input_state_(this)),
ALLOW_THIS_IN_INITIALIZER_LIST(output_state_(this)) {
Init(mode, listener);
@ -105,8 +107,8 @@ void Channel::ChannelImpl::OutputQueuePop() {
}
void Channel::ChannelImpl::Close() {
io_thread_.AssertOnCurrentThread();
mozilla::MutexAutoLock lock(mutex_);
IOThread().AssertOnCurrentThread();
mozilla::MutexAutoLock lock(SendMutex());
CloseLocked();
}
@ -138,7 +140,7 @@ void Channel::ChannelImpl::CloseLocked() {
// It's OK to unlock here, as calls to `Send` from other threads will be
// rejected, due to `pipe_` having been cleared.
while (input_state_.is_pending || output_state_.is_pending) {
mozilla::MutexAutoUnlock unlock(mutex_);
mozilla::MutexAutoUnlock unlock(SendMutex());
MessageLoopForIO::current()->WaitForIOCompletion(INFINITE, this);
}
@ -148,7 +150,7 @@ void Channel::ChannelImpl::CloseLocked() {
}
bool Channel::ChannelImpl::Send(mozilla::UniquePtr<Message> message) {
mozilla::MutexAutoLock lock(mutex_);
mozilla::MutexAutoLock lock(SendMutex());
#ifdef IPC_MESSAGE_DEBUG_EXTRA
DLOG(INFO) << "sending message @" << message.get() << " on channel @" << this
@ -255,8 +257,8 @@ bool Channel::ChannelImpl::EnqueueHelloMessage() {
}
bool Channel::ChannelImpl::Connect() {
io_thread_.AssertOnCurrentThread();
mozilla::MutexAutoLock lock(mutex_);
IOThread().AssertOnCurrentThread();
mozilla::MutexAutoLock lock(SendMutex());
if (pipe_ == INVALID_HANDLE_VALUE) return false;
@ -277,7 +279,7 @@ bool Channel::ChannelImpl::Connect() {
// to `this`, we indicate to OnIOCompleted that this is the special
// initialization signal, while keeping a reference through the
// `RunnableMethod`.
io_thread_.Dispatch(
IOThread().Dispatch(
mozilla::NewRunnableMethod<MessageLoopForIO::IOContext*, DWORD, DWORD>(
"ContinueConnect", this, &ChannelImpl::OnIOCompleted,
&input_state_.context, 0, 0));
@ -465,14 +467,14 @@ bool Channel::ChannelImpl::ProcessIncomingMessages(
}
int32_t other_pid = other_pid_;
mozilla::MutexAutoUnlock unlock(mutex_);
mozilla::MutexAutoUnlock unlock(SendMutex());
listener_->OnChannelConnected(other_pid);
} else {
mozilla::LogIPCMessage::Run run(&m);
if (!AcceptHandles(m)) {
return false;
}
mozilla::MutexAutoUnlock unlock(mutex_);
mozilla::MutexAutoUnlock unlock(SendMutex());
listener_->OnMessageReceived(std::move(incoming_message_));
}
@ -576,8 +578,8 @@ void Channel::ChannelImpl::OnIOCompleted(MessageLoopForIO::IOContext* context,
// outside of the lock.
RefPtr<ChannelImpl> was_pending;
io_thread_.AssertOnCurrentThread();
mozilla::ReleasableMutexAutoLock lock(mutex_);
IOThread().AssertOnCurrentThread();
mozilla::ReleasableMutexAutoLock lock(SendMutex());
bool ok;
if (context == &input_state_.context) {
was_pending = input_state_.is_pending.forget();
@ -615,8 +617,8 @@ void Channel::ChannelImpl::OnIOCompleted(MessageLoopForIO::IOContext* context,
}
void Channel::ChannelImpl::StartAcceptingHandles(Mode mode) {
io_thread_.AssertOnCurrentThread();
mozilla::MutexAutoLock lock(mutex_);
IOThread().AssertOnCurrentThread();
mozilla::MutexAutoLock lock(SendMutex());
if (accept_handles_) {
MOZ_ASSERT(privileged_ == (mode == MODE_SERVER));

View File

@ -8,6 +8,7 @@
#define CHROME_COMMON_IPC_CHANNEL_WIN_H_
#include "chrome/common/ipc_channel.h"
#include "chrome/common/ipc_channel_capability.h"
#include "chrome/common/ipc_message.h"
#include <atomic>
@ -28,7 +29,7 @@ namespace IPC {
class Channel::ChannelImpl : public MessageLoopForIO::IOHandler {
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING_WITH_DELETE_ON_EVENT_TARGET(
ChannelImpl, io_thread_.GetEventTarget());
ChannelImpl, IOThread().GetEventTarget());
using ChannelId = Channel::ChannelId;
using ChannelHandle = Channel::ChannelHandle;
@ -36,73 +37,85 @@ class Channel::ChannelImpl : public MessageLoopForIO::IOHandler {
// Mirror methods of Channel, see ipc_channel.h for description.
ChannelImpl(const ChannelId& channel_id, Mode mode, Listener* listener);
ChannelImpl(ChannelHandle pipe, Mode mode, Listener* listener);
bool Connect() MOZ_EXCLUDES(mutex_);
void Close() MOZ_EXCLUDES(mutex_);
void StartAcceptingHandles(Mode mode) MOZ_EXCLUDES(mutex_);
bool Connect() MOZ_EXCLUDES(SendMutex());
void Close() MOZ_EXCLUDES(SendMutex());
void StartAcceptingHandles(Mode mode) MOZ_EXCLUDES(SendMutex());
Listener* set_listener(Listener* listener) {
io_thread_.AssertOnCurrentThread();
IOThread().AssertOnCurrentThread();
Listener* old = listener_;
listener_ = listener;
return old;
}
// NOTE: `Send` may be called on threads other than the I/O thread.
bool Send(mozilla::UniquePtr<Message> message) MOZ_EXCLUDES(mutex_);
bool Send(mozilla::UniquePtr<Message> message) MOZ_EXCLUDES(SendMutex());
int32_t OtherPid() MOZ_EXCLUDES(mutex_) {
io_thread_.AssertOnCurrentThread();
mozilla::MutexAutoLock lock(mutex_);
int32_t OtherPid() MOZ_EXCLUDES(SendMutex()) {
IOThread().AssertOnCurrentThread();
mozilla::MutexAutoLock lock(SendMutex());
return other_pid_;
}
// See the comment in ipc_channel.h for info on IsClosed()
// NOTE: `IsClosed` may be called on threads other than the I/O thread.
bool IsClosed() MOZ_EXCLUDES(mutex_) {
mozilla::MutexAutoLock lock(mutex_);
bool IsClosed() MOZ_EXCLUDES(SendMutex()) {
mozilla::MutexAutoLock lock(SendMutex());
return pipe_ == INVALID_HANDLE_VALUE;
}
private:
~ChannelImpl() {
io_thread_.AssertOnCurrentThread();
IOThread().AssertOnCurrentThread();
if (pipe_ != INVALID_HANDLE_VALUE ||
other_process_ != INVALID_HANDLE_VALUE) {
Close();
}
}
void Init(Mode mode, Listener* listener) MOZ_REQUIRES(mutex_, io_thread_);
void Init(Mode mode, Listener* listener)
MOZ_REQUIRES(SendMutex(), IOThread());
void OutputQueuePush(mozilla::UniquePtr<Message> msg) MOZ_REQUIRES(mutex_);
void OutputQueuePop() MOZ_REQUIRES(mutex_);
void OutputQueuePush(mozilla::UniquePtr<Message> msg)
MOZ_REQUIRES(SendMutex());
void OutputQueuePop() MOZ_REQUIRES(SendMutex());
const ChannelId PipeName(const ChannelId& channel_id, int32_t* secret) const;
bool CreatePipe(const ChannelId& channel_id, Mode mode)
MOZ_REQUIRES(mutex_, io_thread_);
bool EnqueueHelloMessage() MOZ_REQUIRES(mutex_, io_thread_);
void CloseLocked() MOZ_REQUIRES(mutex_, io_thread_);
MOZ_REQUIRES(SendMutex(), IOThread());
bool EnqueueHelloMessage() MOZ_REQUIRES(SendMutex(), IOThread());
void CloseLocked() MOZ_REQUIRES(SendMutex(), IOThread());
bool ProcessConnection() MOZ_REQUIRES(mutex_, io_thread_);
bool ProcessConnection() MOZ_REQUIRES(SendMutex(), IOThread());
bool ProcessIncomingMessages(MessageLoopForIO::IOContext* context,
DWORD bytes_read, bool was_pending)
MOZ_REQUIRES(mutex_, io_thread_);
MOZ_REQUIRES(SendMutex(), IOThread());
bool ProcessOutgoingMessages(MessageLoopForIO::IOContext* context,
DWORD bytes_written, bool was_pending)
MOZ_REQUIRES(mutex_);
MOZ_REQUIRES(SendMutex());
// Called on a Message immediately before it is sent/recieved to transfer
// handles to the remote process, or accept handles from the remote process.
bool AcceptHandles(Message& msg) MOZ_REQUIRES(mutex_, io_thread_);
bool TransferHandles(Message& msg) MOZ_REQUIRES(mutex_);
bool AcceptHandles(Message& msg) MOZ_REQUIRES(SendMutex(), IOThread());
bool TransferHandles(Message& msg) MOZ_REQUIRES(SendMutex());
// MessageLoop::IOHandler implementation.
virtual void OnIOCompleted(MessageLoopForIO::IOContext* context,
DWORD bytes_transfered, DWORD error);
private:
mozilla::Mutex mutex_{"ChannelImpl"};
const mozilla::EventTargetCapability<nsISerialEventTarget> io_thread_;
const ChannelCapability::Thread& IOThread() const
MOZ_RETURN_CAPABILITY(chan_cap_.IOThread()) {
return chan_cap_.IOThread();
}
Mode mode_ MOZ_GUARDED_BY(io_thread_);
ChannelCapability::Mutex& SendMutex()
MOZ_RETURN_CAPABILITY(chan_cap_.SendMutex()) {
return chan_cap_.SendMutex();
}
private:
// Compound capability of a Mutex and the IO thread.
ChannelCapability chan_cap_;
Mode mode_ MOZ_GUARDED_BY(IOThread());
struct State {
explicit State(ChannelImpl* channel);
@ -113,64 +126,64 @@ class Channel::ChannelImpl : public MessageLoopForIO::IOHandler {
RefPtr<ChannelImpl> is_pending;
};
State input_state_ MOZ_GUARDED_BY(io_thread_);
State output_state_ MOZ_GUARDED_BY(mutex_);
State input_state_ MOZ_GUARDED_BY(IOThread());
State output_state_ MOZ_GUARDED_BY(SendMutex());
HANDLE pipe_ MOZ_GUARDED_BY(mutex_) = INVALID_HANDLE_VALUE;
HANDLE pipe_ MOZ_GUARDED_BY(SendMutex()) = INVALID_HANDLE_VALUE;
Listener* listener_ MOZ_GUARDED_BY(io_thread_) = nullptr;
Listener* listener_ MOZ_GUARDED_BY(IOThread()) = nullptr;
// Messages to be sent are queued here.
mozilla::Queue<mozilla::UniquePtr<Message>, 64> output_queue_
MOZ_GUARDED_BY(mutex_);
MOZ_GUARDED_BY(SendMutex());
// If sending a message blocks then we use this iterator to keep track of
// where in the message we are. It gets reset when the message is finished
// sending.
mozilla::Maybe<Pickle::BufferList::IterImpl> partial_write_iter_
MOZ_GUARDED_BY(mutex_);
MOZ_GUARDED_BY(SendMutex());
// We read from the pipe into this buffer
mozilla::UniquePtr<char[]> input_buf_ MOZ_GUARDED_BY(io_thread_);
size_t input_buf_offset_ MOZ_GUARDED_BY(io_thread_) = 0;
mozilla::UniquePtr<char[]> input_buf_ MOZ_GUARDED_BY(IOThread());
size_t input_buf_offset_ MOZ_GUARDED_BY(IOThread()) = 0;
// Large incoming messages that span multiple pipe buffers get built-up in the
// buffers of this message.
mozilla::UniquePtr<Message> incoming_message_ MOZ_GUARDED_BY(io_thread_);
mozilla::UniquePtr<Message> incoming_message_ MOZ_GUARDED_BY(IOThread());
// Will be set to `true` until `Connect()` has been called, and, if in
// server-mode, the client has connected. The `input_state_` is used to wait
// for the client to connect in overlapped mode.
bool waiting_connect_ MOZ_GUARDED_BY(mutex_) = true;
bool waiting_connect_ MOZ_GUARDED_BY(SendMutex()) = true;
// This flag is set when processing incoming messages. It is used to
// avoid recursing through ProcessIncomingMessages, which could cause
// problems. TODO(darin): make this unnecessary
bool processing_incoming_ MOZ_GUARDED_BY(io_thread_) = false;
bool processing_incoming_ MOZ_GUARDED_BY(IOThread()) = false;
// We keep track of the PID of the other side of this channel so that we can
// record this when generating logs of IPC messages.
int32_t other_pid_ MOZ_GUARDED_BY(mutex_) = -1;
int32_t other_pid_ MOZ_GUARDED_BY(SendMutex()) = -1;
// This is a unique per-channel value used to authenticate the client end of
// a connection. If the value is non-zero, the client passes it in the hello
// and the host validates. (We don't send the zero value to preserve IPC
// compatibility with existing clients that don't validate the channel.)
int32_t shared_secret_ MOZ_GUARDED_BY(io_thread_) = 0;
int32_t shared_secret_ MOZ_GUARDED_BY(IOThread()) = 0;
// In server-mode, we wait for the channel at the other side of the pipe to
// send us back our shared secret, if we are using one.
bool waiting_for_shared_secret_ MOZ_GUARDED_BY(io_thread_) = false;
bool waiting_for_shared_secret_ MOZ_GUARDED_BY(IOThread()) = false;
// Whether or not to accept handles from a remote process, and whether this
// process is the privileged side of a IPC::Channel which can transfer
// handles.
bool accept_handles_ MOZ_GUARDED_BY(mutex_) = false;
bool privileged_ MOZ_GUARDED_BY(mutex_) = false;
bool accept_handles_ MOZ_GUARDED_BY(SendMutex()) = false;
bool privileged_ MOZ_GUARDED_BY(SendMutex()) = false;
// A privileged process handle used to transfer HANDLEs to and from the remote
// process. This will only be used if `privileged_` is set.
HANDLE other_process_ MOZ_GUARDED_BY(mutex_) = INVALID_HANDLE_VALUE;
HANDLE other_process_ MOZ_GUARDED_BY(SendMutex()) = INVALID_HANDLE_VALUE;
DISALLOW_COPY_AND_ASSIGN(ChannelImpl);
};