blob: b9533e69c4ca5e37775f7e178276e32d90a29d10 [file] [log] [blame]
agl@chromium.org1c6dcf22009-07-23 08:57:21 +09001// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "base/message_loop.h"
erg@chromium.orga7528522010-07-16 02:23:23 +09006#include "base/ref_counted.h"
7#include "base/scoped_ptr.h"
agl@chromium.org1c6dcf22009-07-23 08:57:21 +09008#include "base/thread.h"
9#include "ipc/ipc_channel_proxy.h"
10#include "ipc/ipc_logging.h"
11#include "ipc/ipc_message_utils.h"
12
13namespace IPC {
14
jcampan@chromium.org1c86b552009-07-29 07:09:45 +090015//------------------------------------------------------------------------------
16
17// This task ensures the message is deleted if the task is deleted without
18// having been run.
19class SendTask : public Task {
20 public:
21 SendTask(ChannelProxy::Context* context, Message* message)
22 : context_(context),
23 message_(message) {
24 }
25
26 virtual void Run() {
27 context_->OnSendMessage(message_.release());
28 }
29
30 private:
31 scoped_refptr<ChannelProxy::Context> context_;
32 scoped_ptr<Message> message_;
33
34 DISALLOW_COPY_AND_ASSIGN(SendTask);
35};
36
37//------------------------------------------------------------------------------
agl@chromium.org1c6dcf22009-07-23 08:57:21 +090038
erg@google.com20b66e32010-10-01 05:06:30 +090039ChannelProxy::MessageFilter::MessageFilter() {}
40
erg@google.com2ec53b42010-09-24 07:43:53 +090041ChannelProxy::MessageFilter::~MessageFilter() {}
42
43void ChannelProxy::MessageFilter::OnFilterAdded(Channel* channel) {}
44
45void ChannelProxy::MessageFilter::OnFilterRemoved() {}
46
47void ChannelProxy::MessageFilter::OnChannelConnected(int32 peer_pid) {}
48
49void ChannelProxy::MessageFilter::OnChannelError() {}
50
51void ChannelProxy::MessageFilter::OnChannelClosing() {}
52
53bool ChannelProxy::MessageFilter::OnMessageReceived(const Message& message) {
54 return false;
55}
56
mpcomplete@chromium.org78bc9752010-10-23 07:19:24 +090057void ChannelProxy::MessageFilter::OnDestruct() const {
erg@google.com2ec53b42010-09-24 07:43:53 +090058 delete this;
59}
60
61//------------------------------------------------------------------------------
62
agl@chromium.org1c6dcf22009-07-23 08:57:21 +090063ChannelProxy::Context::Context(Channel::Listener* listener,
agl@chromium.org1c6dcf22009-07-23 08:57:21 +090064 MessageLoop* ipc_message_loop)
65 : listener_message_loop_(MessageLoop::current()),
66 listener_(listener),
67 ipc_message_loop_(ipc_message_loop),
agl@chromium.org1c6dcf22009-07-23 08:57:21 +090068 peer_pid_(0),
69 channel_connected_called_(false) {
agl@chromium.org1c6dcf22009-07-23 08:57:21 +090070}
71
dmaclach@chromium.org058c4a72010-12-09 04:28:09 +090072void ChannelProxy::Context::CreateChannel(const IPC::ChannelHandle& handle,
agl@chromium.org1c6dcf22009-07-23 08:57:21 +090073 const Channel::Mode& mode) {
dmaclach@chromium.org058c4a72010-12-09 04:28:09 +090074 DCHECK(channel_.get() == NULL);
75 channel_id_ = handle.name;
76 channel_.reset(new Channel(handle, mode, this));
agl@chromium.org1c6dcf22009-07-23 08:57:21 +090077}
78
79bool ChannelProxy::Context::TryFilters(const Message& message) {
80#ifdef IPC_MESSAGE_LOG_ENABLED
satish@chromium.orgaa870602010-12-13 17:18:55 +090081 Logging* logger = Logging::GetInstance();
agl@chromium.org1c6dcf22009-07-23 08:57:21 +090082 if (logger->Enabled())
83 logger->OnPreDispatchMessage(message);
84#endif
85
86 for (size_t i = 0; i < filters_.size(); ++i) {
87 if (filters_[i]->OnMessageReceived(message)) {
88#ifdef IPC_MESSAGE_LOG_ENABLED
89 if (logger->Enabled())
90 logger->OnPostDispatchMessage(message, channel_id_);
91#endif
92 return true;
93 }
94 }
95 return false;
96}
97
98// Called on the IPC::Channel thread
jam@chromium.org8a2c7842010-12-24 15:19:28 +090099bool ChannelProxy::Context::OnMessageReceived(const Message& message) {
agl@chromium.org1c6dcf22009-07-23 08:57:21 +0900100 // First give a chance to the filters to process this message.
101 if (!TryFilters(message))
102 OnMessageReceivedNoFilter(message);
jam@chromium.org8a2c7842010-12-24 15:19:28 +0900103 return true;
agl@chromium.org1c6dcf22009-07-23 08:57:21 +0900104}
105
106// Called on the IPC::Channel thread
jam@chromium.org8a2c7842010-12-24 15:19:28 +0900107bool ChannelProxy::Context::OnMessageReceivedNoFilter(const Message& message) {
agl@chromium.org1c6dcf22009-07-23 08:57:21 +0900108 // NOTE: This code relies on the listener's message loop not going away while
109 // this thread is active. That should be a reasonable assumption, but it
110 // feels risky. We may want to invent some more indirect way of referring to
111 // a MessageLoop if this becomes a problem.
112 listener_message_loop_->PostTask(FROM_HERE, NewRunnableMethod(
113 this, &Context::OnDispatchMessage, message));
jam@chromium.org8a2c7842010-12-24 15:19:28 +0900114 return true;
agl@chromium.org1c6dcf22009-07-23 08:57:21 +0900115}
116
117// Called on the IPC::Channel thread
118void ChannelProxy::Context::OnChannelConnected(int32 peer_pid) {
jam@chromium.orge57135c2010-12-03 04:16:07 +0900119 // Add any pending filters. This avoids a race condition where someone
120 // creates a ChannelProxy, calls AddFilter, and then right after starts the
121 // peer process. The IO thread could receive a message before the task to add
122 // the filter is run on the IO thread.
123 OnAddFilter();
124
agl@chromium.org1c6dcf22009-07-23 08:57:21 +0900125 peer_pid_ = peer_pid;
126 for (size_t i = 0; i < filters_.size(); ++i)
127 filters_[i]->OnChannelConnected(peer_pid);
128
129 // See above comment about using listener_message_loop_ here.
130 listener_message_loop_->PostTask(FROM_HERE, NewRunnableMethod(
131 this, &Context::OnDispatchConnected));
132}
133
134// Called on the IPC::Channel thread
135void ChannelProxy::Context::OnChannelError() {
136 for (size_t i = 0; i < filters_.size(); ++i)
137 filters_[i]->OnChannelError();
138
139 // See above comment about using listener_message_loop_ here.
140 listener_message_loop_->PostTask(FROM_HERE, NewRunnableMethod(
141 this, &Context::OnDispatchError));
142}
143
144// Called on the IPC::Channel thread
145void ChannelProxy::Context::OnChannelOpened() {
146 DCHECK(channel_ != NULL);
147
148 // Assume a reference to ourselves on behalf of this thread. This reference
149 // will be released when we are closed.
150 AddRef();
151
152 if (!channel_->Connect()) {
153 OnChannelError();
154 return;
155 }
156
157 for (size_t i = 0; i < filters_.size(); ++i)
dmaclach@chromium.org058c4a72010-12-09 04:28:09 +0900158 filters_[i]->OnFilterAdded(channel_.get());
agl@chromium.org1c6dcf22009-07-23 08:57:21 +0900159}
160
161// Called on the IPC::Channel thread
162void ChannelProxy::Context::OnChannelClosed() {
163 // It's okay for IPC::ChannelProxy::Close to be called more than once, which
164 // would result in this branch being taken.
dmaclach@chromium.org058c4a72010-12-09 04:28:09 +0900165 if (!channel_.get())
agl@chromium.org1c6dcf22009-07-23 08:57:21 +0900166 return;
167
168 for (size_t i = 0; i < filters_.size(); ++i) {
169 filters_[i]->OnChannelClosing();
170 filters_[i]->OnFilterRemoved();
171 }
172
173 // We don't need the filters anymore.
174 filters_.clear();
175
dmaclach@chromium.org058c4a72010-12-09 04:28:09 +0900176 channel_.reset();
agl@chromium.org1c6dcf22009-07-23 08:57:21 +0900177
178 // Balance with the reference taken during startup. This may result in
179 // self-destruction.
180 Release();
181}
182
183// Called on the IPC::Channel thread
184void ChannelProxy::Context::OnSendMessage(Message* message) {
dmaclach@chromium.org058c4a72010-12-09 04:28:09 +0900185 if (!channel_.get()) {
ananta@chromium.org999f2972010-09-03 06:45:50 +0900186 delete message;
187 OnChannelClosed();
188 return;
189 }
agl@chromium.org1c6dcf22009-07-23 08:57:21 +0900190 if (!channel_->Send(message))
191 OnChannelError();
192}
193
194// Called on the IPC::Channel thread
jam@chromium.orge57135c2010-12-03 04:16:07 +0900195void ChannelProxy::Context::OnAddFilter() {
196 std::vector<scoped_refptr<MessageFilter> > filters;
197 {
198 AutoLock auto_lock(pending_filters_lock_);
199 filters.swap(pending_filters_);
200 }
agl@chromium.org1c6dcf22009-07-23 08:57:21 +0900201
jam@chromium.orge57135c2010-12-03 04:16:07 +0900202 for (size_t i = 0; i < filters.size(); ++i) {
203 filters_.push_back(filters[i]);
204
205 // If the channel has already been created, then we need to send this
206 // message so that the filter gets access to the Channel.
dmaclach@chromium.org058c4a72010-12-09 04:28:09 +0900207 if (channel_.get())
208 filters[i]->OnFilterAdded(channel_.get());
jam@chromium.orge57135c2010-12-03 04:16:07 +0900209 // Ditto for the peer process id.
210 if (peer_pid_)
211 filters[i]->OnChannelConnected(peer_pid_);
212 }
agl@chromium.org1c6dcf22009-07-23 08:57:21 +0900213}
214
215// Called on the IPC::Channel thread
216void ChannelProxy::Context::OnRemoveFilter(MessageFilter* filter) {
217 for (size_t i = 0; i < filters_.size(); ++i) {
218 if (filters_[i].get() == filter) {
219 filter->OnFilterRemoved();
220 filters_.erase(filters_.begin() + i);
221 return;
222 }
223 }
224
225 NOTREACHED() << "filter to be removed not found";
226}
227
228// Called on the listener's thread
jam@chromium.orge57135c2010-12-03 04:16:07 +0900229void ChannelProxy::Context::AddFilter(MessageFilter* filter) {
230 AutoLock auto_lock(pending_filters_lock_);
231 pending_filters_.push_back(make_scoped_refptr(filter));
232 ipc_message_loop_->PostTask(
233 FROM_HERE,
234 NewRunnableMethod(this, &Context::OnAddFilter));
235}
236
237// Called on the listener's thread
agl@chromium.org1c6dcf22009-07-23 08:57:21 +0900238void ChannelProxy::Context::OnDispatchMessage(const Message& message) {
239 if (!listener_)
240 return;
241
242 OnDispatchConnected();
243
244#ifdef IPC_MESSAGE_LOG_ENABLED
satish@chromium.orgaa870602010-12-13 17:18:55 +0900245 Logging* logger = Logging::GetInstance();
agl@chromium.org1c6dcf22009-07-23 08:57:21 +0900246 if (message.type() == IPC_LOGGING_ID) {
247 logger->OnReceivedLoggingMessage(message);
248 return;
249 }
250
251 if (logger->Enabled())
252 logger->OnPreDispatchMessage(message);
253#endif
254
255 listener_->OnMessageReceived(message);
256
257#ifdef IPC_MESSAGE_LOG_ENABLED
258 if (logger->Enabled())
259 logger->OnPostDispatchMessage(message, channel_id_);
260#endif
261}
262
263// Called on the listener's thread
264void ChannelProxy::Context::OnDispatchConnected() {
265 if (channel_connected_called_)
266 return;
267
268 channel_connected_called_ = true;
269 if (listener_)
270 listener_->OnChannelConnected(peer_pid_);
271}
272
273// Called on the listener's thread
274void ChannelProxy::Context::OnDispatchError() {
275 if (listener_)
276 listener_->OnChannelError();
277}
278
279//-----------------------------------------------------------------------------
280
dmaclach@chromium.org058c4a72010-12-09 04:28:09 +0900281ChannelProxy::ChannelProxy(const IPC::ChannelHandle& channel_handle,
jam@chromium.orge57135c2010-12-03 04:16:07 +0900282 Channel::Mode mode,
283 Channel::Listener* listener,
agl@chromium.org1c6dcf22009-07-23 08:57:21 +0900284 MessageLoop* ipc_thread)
jam@chromium.orge57135c2010-12-03 04:16:07 +0900285 : context_(new Context(listener, ipc_thread)) {
dmaclach@chromium.org058c4a72010-12-09 04:28:09 +0900286 Init(channel_handle, mode, ipc_thread, true);
agl@chromium.org1c6dcf22009-07-23 08:57:21 +0900287}
288
dmaclach@chromium.org058c4a72010-12-09 04:28:09 +0900289ChannelProxy::ChannelProxy(const IPC::ChannelHandle& channel_handle,
jam@chromium.orge57135c2010-12-03 04:16:07 +0900290 Channel::Mode mode,
291 MessageLoop* ipc_thread,
292 Context* context,
agl@chromium.org1c6dcf22009-07-23 08:57:21 +0900293 bool create_pipe_now)
294 : context_(context) {
dmaclach@chromium.org058c4a72010-12-09 04:28:09 +0900295 Init(channel_handle, mode, ipc_thread, create_pipe_now);
agl@chromium.org1c6dcf22009-07-23 08:57:21 +0900296}
297
erg@google.com2ec53b42010-09-24 07:43:53 +0900298ChannelProxy::~ChannelProxy() {
299 Close();
300}
301
dmaclach@chromium.org058c4a72010-12-09 04:28:09 +0900302void ChannelProxy::Init(const IPC::ChannelHandle& channel_handle,
303 Channel::Mode mode, MessageLoop* ipc_thread_loop,
304 bool create_pipe_now) {
305#if defined(OS_POSIX)
306 // When we are creating a server on POSIX, we need its file descriptor
307 // to be created immediately so that it can be accessed and passed
308 // to other processes. Forcing it to be created immediately avoids
309 // race conditions that may otherwise arise.
310 if (mode == Channel::MODE_SERVER) {
311 create_pipe_now = true;
312 }
313#endif // defined(OS_POSIX)
314
agl@chromium.org1c6dcf22009-07-23 08:57:21 +0900315 if (create_pipe_now) {
316 // Create the channel immediately. This effectively sets up the
317 // low-level pipe so that the client can connect. Without creating
318 // the pipe immediately, it is possible for a listener to attempt
319 // to connect and get an error since the pipe doesn't exist yet.
dmaclach@chromium.org058c4a72010-12-09 04:28:09 +0900320 context_->CreateChannel(channel_handle, mode);
agl@chromium.org1c6dcf22009-07-23 08:57:21 +0900321 } else {
322 context_->ipc_message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
dmaclach@chromium.org058c4a72010-12-09 04:28:09 +0900323 context_.get(), &Context::CreateChannel, channel_handle, mode));
agl@chromium.org1c6dcf22009-07-23 08:57:21 +0900324 }
325
326 // complete initialization on the background thread
327 context_->ipc_message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
328 context_.get(), &Context::OnChannelOpened));
329}
330
331void ChannelProxy::Close() {
332 // Clear the backpointer to the listener so that any pending calls to
333 // Context::OnDispatchMessage or OnDispatchError will be ignored. It is
334 // possible that the channel could be closed while it is receiving messages!
335 context_->Clear();
336
337 if (context_->ipc_message_loop()) {
338 context_->ipc_message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
339 context_.get(), &Context::OnChannelClosed));
340 }
341}
342
343bool ChannelProxy::Send(Message* message) {
344#ifdef IPC_MESSAGE_LOG_ENABLED
satish@chromium.orgaa870602010-12-13 17:18:55 +0900345 Logging::GetInstance()->OnSendMessage(message, context_->channel_id());
agl@chromium.org1c6dcf22009-07-23 08:57:21 +0900346#endif
347
jcampan@chromium.org1c86b552009-07-29 07:09:45 +0900348 context_->ipc_message_loop()->PostTask(FROM_HERE,
349 new SendTask(context_.get(), message));
agl@chromium.org1c6dcf22009-07-23 08:57:21 +0900350 return true;
351}
352
353void ChannelProxy::AddFilter(MessageFilter* filter) {
jam@chromium.orge57135c2010-12-03 04:16:07 +0900354 context_->AddFilter(filter);
agl@chromium.org1c6dcf22009-07-23 08:57:21 +0900355}
356
357void ChannelProxy::RemoveFilter(MessageFilter* filter) {
willchan@chromium.org32988472010-10-20 08:48:38 +0900358 context_->ipc_message_loop()->PostTask(
359 FROM_HERE, NewRunnableMethod(
360 context_.get(),
361 &Context::OnRemoveFilter,
362 make_scoped_refptr(filter)));
agl@chromium.org1c6dcf22009-07-23 08:57:21 +0900363}
364
nsylvain@chromium.orgc12dde32009-07-24 03:17:55 +0900365void ChannelProxy::ClearIPCMessageLoop() {
366 context()->ClearIPCMessageLoop();
367}
368
abarth@chromium.org07af1a62010-11-13 03:01:34 +0900369#if defined(OS_POSIX) && !defined(OS_NACL)
agl@chromium.org1c6dcf22009-07-23 08:57:21 +0900370// See the TODO regarding lazy initialization of the channel in
371// ChannelProxy::Init().
372// We assume that IPC::Channel::GetClientFileDescriptorMapping() is thread-safe.
373int ChannelProxy::GetClientFileDescriptor() const {
dmaclach@chromium.org058c4a72010-12-09 04:28:09 +0900374 Channel *channel = context_.get()->channel_.get();
375 // Channel must have been created first.
376 DCHECK(channel) << context_.get()->channel_id_;
agl@chromium.org1c6dcf22009-07-23 08:57:21 +0900377 return channel->GetClientFileDescriptor();
378}
379#endif
380
381//-----------------------------------------------------------------------------
382
383} // namespace IPC