Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(542)

Side by Side Diff: ipc/ipc_channel_nacl.cc

Issue 10174048: PPAPI/NaCl: Speculative implementation for ipc_channel_nacl.cc (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Fix review comments. Created 8 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « ipc/ipc_channel_nacl.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "ipc/ipc_channel_nacl.h" 5 #include "ipc/ipc_channel_nacl.h"
6 6
7 #include <errno.h>
8 #include <stddef.h>
9 #include <sys/nacl_imc_api.h>
10 #include <sys/nacl_syscalls.h>
11 #include <sys/types.h>
12
13 #include <algorithm>
14
15 #include "base/bind.h"
7 #include "base/file_util.h" 16 #include "base/file_util.h"
8 #include "base/logging.h" 17 #include "base/logging.h"
9 18 #include "base/message_loop_proxy.h"
10 // This file is currently a stub to get us linking. 19 #include "base/process_util.h"
11 // TODO(brettw) implement this. 20 #include "base/synchronization/lock.h"
21 #include "base/task_runner_util.h"
22 #include "base/threading/simple_thread.h"
23 #include "ipc/file_descriptor_set_posix.h"
24 #include "ipc/ipc_logging.h"
12 25
13 namespace IPC { 26 namespace IPC {
27 namespace {
28
29 scoped_ptr<std::vector<char> > ReadDataOnReaderThread(int pipe) {
30 DCHECK(pipe >= 0);
31 scoped_ptr<std::vector<char> > null_ptr;
32
33 if (pipe < 0)
34 return null_ptr.Pass();
35
36 scoped_ptr<std::vector<char> > buffer(
37 new std::vector<char>(Channel::kReadBufferSize));
38 struct NaClImcMsgHdr msg = {0};
39 struct NaClImcMsgIoVec iov = {&buffer->at(0), buffer->size()};
40 msg.iov = &iov;
41 msg.iov_length = 1;
42
43 int bytes_read = imc_recvmsg(pipe, &msg, 0);
44
45 if (bytes_read < 0) {
46 // TODO(dmichael): Define what errors actually happen in the NaClCustomDesc
47 // implementation and handle them appropriately. These are
48 // placeholders.
49 if (errno == ECONNRESET || errno == EPIPE) {
50 return null_ptr.Pass();
51 } else {
52 PLOG(ERROR) << "pipe error (" << pipe << ")";
53 return null_ptr.Pass();
54 }
55 } else if (bytes_read == 0) {
56 // The pipe has closed...
57 return null_ptr.Pass();
58 }
59 DCHECK(bytes_read);
60 buffer->resize(bytes_read);
61 return buffer.Pass();
62 }
63
64 } // namespace
65
66 class Channel::ChannelImpl::ReaderThreadRunner
67 : public base::DelegateSimpleThread::Delegate {
68 public:
69 // |pipe|: A file descriptor from which we will read using imc_recvmsg.
70 // |data_read_callback|: A callback we invoke (on the main thread) when we
71 // have read data. The callback is passed a buffer of
72 // data that was read.
73 // |failure_callback|: A callback we invoke when we have a failure reading
74 // from |pipe|.
75 // |main_message_loop|: A proxy for the main thread, where we will invoke the
76 // above callbacks.
77 ReaderThreadRunner(
78 int pipe,
79 base::Callback<void (scoped_ptr<std::vector<char> >)> data_read_callback,
80 base::Callback<void ()> failure_callback,
81 base::MessageLoopProxy* main_message_loop);
82
83 // DelegateSimpleThread implementation. Reads data from the pipe in a loop
84 // until either we are told to quit or a read fails.
85 virtual void Run() OVERRIDE;
86
87 private:
88 int pipe_;
89 base::Callback<void (scoped_ptr<std::vector<char> >)> data_read_callback_;
90 base::Callback<void ()> failure_callback_;
91 base::MessageLoopProxy* main_message_loop_;
92
93 DISALLOW_COPY_AND_ASSIGN(ReaderThreadRunner);
94 };
95
96 Channel::ChannelImpl::ReaderThreadRunner::ReaderThreadRunner(
97 int pipe,
98 base::Callback<void (scoped_ptr<std::vector<char> >)> data_read_callback,
99 base::Callback<void ()> failure_callback,
100 base::MessageLoopProxy* main_message_loop)
101 : pipe_(pipe),
102 data_read_callback_(data_read_callback),
103 failure_callback_(failure_callback),
104 main_message_loop_(main_message_loop) {
105 }
106
107 void Channel::ChannelImpl::ReaderThreadRunner::Run() {
108 while (true) {
109 scoped_ptr<std::vector<char> > buffer(ReadDataOnReaderThread(pipe_));
110 if (buffer.get()) {
111 main_message_loop_->PostTask(FROM_HERE,
112 base::Bind(data_read_callback_, base::Passed(buffer.Pass())));
113 } else {
114 main_message_loop_->PostTask(FROM_HERE, failure_callback_);
115 // Because the read failed, we know we're going to quit. Don't bother
116 // trying to read again.
117 return;
118 }
119 }
120 }
14 121
15 Channel::ChannelImpl::ChannelImpl(const IPC::ChannelHandle& channel_handle, 122 Channel::ChannelImpl::ChannelImpl(const IPC::ChannelHandle& channel_handle,
16 Mode mode, 123 Mode mode,
17 Listener* listener) 124 Listener* listener)
18 : ChannelReader(listener) { 125 : ChannelReader(listener),
126 mode_(mode),
127 peer_pid_(base::kNullProcessId),
128 waiting_connect_(true),
129 pipe_(-1),
130 pipe_name_(channel_handle.name),
131 weak_ptr_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {
132 if (!CreatePipe(channel_handle)) {
133 // The pipe may have been closed already.
134 const char *modestr = (mode_ & MODE_SERVER_FLAG) ? "server" : "client";
135 LOG(WARNING) << "Unable to create pipe named \"" << channel_handle.name
136 << "\" in " << modestr << " mode";
137 }
138 reader_thread_runner_.reset(
139 new ReaderThreadRunner(
140 pipe_,
141 base::Bind(&Channel::ChannelImpl::DidRecvMsg,
142 weak_ptr_factory_.GetWeakPtr()),
143 base::Bind(&Channel::ChannelImpl::ReadDidFail,
144 weak_ptr_factory_.GetWeakPtr()),
145 base::MessageLoopProxy::current()));
146 reader_thread_.reset(
147 new base::DelegateSimpleThread(reader_thread_runner_.get(),
148 "ipc_channel_nacl reader thread"));
19 } 149 }
20 150
21 Channel::ChannelImpl::~ChannelImpl() { 151 Channel::ChannelImpl::~ChannelImpl() {
22 Close(); 152 Close();
23 } 153 }
24 154
25 bool Channel::ChannelImpl::Connect() { 155 bool Channel::ChannelImpl::Connect() {
26 NOTIMPLEMENTED(); 156 if (pipe_ == -1) {
27 return false; 157 DLOG(INFO) << "Channel creation failed: " << pipe_name_;
158 return false;
159 }
160
161 reader_thread_->Start();
162 if (mode_ & MODE_CLIENT_FLAG) {
163 // If we are a client we want to send a hello message out immediately.
164 // In server mode we will send a hello message when we receive one from a
165 // client.
166 waiting_connect_ = false;
167 QueueHelloMessage();
168 } else if (mode_ & MODE_SERVER_FLAG) {
169 waiting_connect_ = true;
170 return true;
171 } else {
172 NOTREACHED();
173 return false;
174 }
28 } 175 }
29 176
30 void Channel::ChannelImpl::Close() { 177 void Channel::ChannelImpl::Close() {
31 NOTIMPLEMENTED(); 178 close(pipe_);
Mark Seaborn 2012/05/03 21:42:22 Doing close() before joining the thread is not the
179 reader_thread_->Join();
180 pipe_ = -1;
181 reader_thread_runner_.reset();
182 reader_thread_.reset();
183 read_queue_.clear();
184 output_queue_.clear();
32 } 185 }
33 186
34 bool Channel::ChannelImpl::Send(Message* message) { 187 bool Channel::ChannelImpl::Send(Message* message) {
35 NOTIMPLEMENTED(); 188 DVLOG(2) << "sending message @" << message << " on channel @" << this
36 } 189 << " with type " << message->type();
37 190 scoped_ptr<Message> message_ptr(message);
38 int Channel::ChannelImpl::GetClientFileDescriptor() const { 191
39 NOTIMPLEMENTED(); 192 #ifdef IPC_MESSAGE_LOG_ENABLED
40 return -1; 193 Logging::GetInstance()->OnSendMessage(message, "");
41 } 194 #endif // IPC_MESSAGE_LOG_ENABLED
42 195
43 int Channel::ChannelImpl::TakeClientFileDescriptor() { 196 output_queue_.push_back(linked_ptr<Message>(message));
44 NOTIMPLEMENTED(); 197 if (!waiting_connect_)
45 return -1; 198 return ProcessOutgoingMessages();
46 } 199
47 200 return true;
48 bool Channel::ChannelImpl::AcceptsConnections() const { 201 }
49 NOTIMPLEMENTED(); 202
50 return false; 203 void Channel::ChannelImpl::DidRecvMsg(scoped_ptr<std::vector<char> > buffer) {
51 } 204 // Close sets the pipe to -1. It's possible we'll get a buffer sent to us from
52 205 // the reader thread after Close is called. If so, we ignore it.
53 bool Channel::ChannelImpl::HasAcceptedConnection() const { 206 if (pipe_ == -1)
54 NOTIMPLEMENTED(); 207 return;
55 return false; 208 bool first_message_for_connection = waiting_connect_;
56 } 209 if (waiting_connect_) {
57 210 // In client mode, we should have already cleared waiting_connect_.
58 bool Channel::ChannelImpl::GetClientEuid(uid_t* client_euid) const { 211 DCHECK(mode_ & MODE_SERVER_FLAG);
59 NOTIMPLEMENTED(); 212 waiting_connect_ = false;
60 return false; 213 }
61 } 214
62 215 read_queue_.push_back(linked_ptr<std::vector<char> >(buffer.release()));
63 void Channel::ChannelImpl::ResetToAcceptingConnectionState() { 216
64 NOTIMPLEMENTED(); 217 // If this was the first message we received, and we're a server, there is a
65 } 218 // hello message (and possibly others) in the queue waiting to be sent.
66 219 if (first_message_for_connection && (mode_ & MODE_SERVER_FLAG))
67 Channel::ChannelImpl::ReadState 220 ProcessOutgoingMessages();
68 Channel::ChannelImpl::ReadData(char* buffer, 221 }
69 int buffer_len, 222
70 int* bytes_read) { 223 void Channel::ChannelImpl::ReadDidFail() {
71 return Channel::ChannelImpl::ReadState(); 224 Close();
225 }
226
227 bool Channel::ChannelImpl::CreatePipe(
228 const IPC::ChannelHandle& channel_handle) {
229 DCHECK(pipe_ == -1);
230
231 // There's one possible case in NaCl:
232 // 1) It's a channel wrapping a pipe that is given to us.
233 // We don't support these:
234 // 2) It's for a named channel.
235 // 3) It's for a client that we implement ourself.
236 // 4) It's the initial IPC channel.
237
238 if (channel_handle.socket.fd == -1) {
239 NOTIMPLEMENTED();
240 return false;
241 }
242 pipe_ = channel_handle.socket.fd;
243 return true;
244 }
245
246 bool Channel::ChannelImpl::ProcessOutgoingMessages() {
247 DCHECK(!waiting_connect_); // Why are we trying to send messages if there's
248 // no connection?
249 if (output_queue_.empty())
250 return true;
251
252 if (pipe_ == -1)
253 return false;
254
255 // Write out all the messages. The trusted implementation is guaranteed to not
256 // block. See the Chrome-side implementation of NaClCustomDesc.
257 // TODO(dmichael): Update this comment to be more specific.
258 while (!output_queue_.empty()) {
259 linked_ptr<Message> msg = output_queue_.front();
260 output_queue_.pop_front();
261
262 struct NaClImcMsgHdr msgh = {0};
263 struct NaClImcMsgIoVec iov = {const_cast<void*>(msg->data()), msg->size()};
264 msgh.iov = &iov;
265 msgh.iov_length = 1;
266 // imc_sendmsg is implemented by <TODO(dmichael), reference class here>,
267 // and is guaranteed to not block. It is also assumed that each message
268 // sent via imc_sendmsg represents a complete IPC::Message (no more no
269 // less), so if you update this code to do something different, you must
270 // also update <TODO(dmichael), reference class here>.
271 ssize_t bytes_written = imc_sendmsg(pipe_, &msgh, 0);
272
273 if (bytes_written < 0) {
274 // The trusted side should only ever give us an error of EPIPE. We
275 // should never be interrupted, nor should we get EAGAIN.
276 DCHECK(errno == EPIPE);
277 Close();
278 PLOG(ERROR) << "pipe_ error on "
279 << pipe_
280 << " Currently writing message of size: "
281 << msg->size();
282 return false;
283 }
284
285 // Message sent OK!
286 DVLOG(2) << "sent message @" << msg.get() << " with type " << msg->type()
287 << " on fd " << pipe_;
288 }
289 return true;
290 }
291
292 int Channel::ChannelImpl::GetHelloMessageProcId() {
293 // TODO(dmichael): Is there any sensible thing to return here?
294 return 0;
295 }
296
297 void Channel::ChannelImpl::QueueHelloMessage() {
298 // TODO(dmichael): Does it actually make sense to do the Hello message in the
299 // NaCl channel? The underlying channel that implements
300 // NaClCustomDesc should already take care of the
301 // client/server handshake.
302 // Create the Hello message.
303 scoped_ptr<Message> msg(new Message(MSG_ROUTING_NONE,
304 HELLO_MESSAGE_TYPE,
305 IPC::Message::PRIORITY_NORMAL));
306 if (!msg->WriteInt(GetHelloMessageProcId())) {
307 NOTREACHED() << "Unable to pickle hello message proc id";
308 return;
309 }
310 Send(msg.release());
311 }
312
313 Channel::ChannelImpl::ReadState Channel::ChannelImpl::ReadData(
314 char* buffer,
315 int buffer_len,
316 int* bytes_read) {
317 *bytes_read = 0;
318 if (pipe_ == -1)
319 return READ_FAILED;
320 if (read_queue_.empty())
321 return READ_PENDING;
322 while (!read_queue_.empty() && *bytes_read < buffer_len) {
323 linked_ptr<std::vector<char> > vec(read_queue_.front());
324 int bytes_to_read = buffer_len - *bytes_read;
325 if (vec->size() <= bytes_to_read) {
326 // We can read and discard the entire vector.
327 std::copy(vec->begin(), vec->end(), buffer + *bytes_read);
328 *bytes_read += vec->size();
329 read_queue_.pop_front();
330 } else {
331 // Read all the bytes we can and discard them from the front of the
332 // vector. (This can be slowish, since erase has to move the back of the
333 // vector to the front, but it's hopefully a temporary hack and it keeps
334 // the code simple).
335 std::copy(vec->begin(), vec->begin() + bytes_to_read,
336 buffer + *bytes_read);
337 vec->erase(vec->begin(), vec->begin() + bytes_to_read);
338 *bytes_read += bytes_to_read;
339 }
340 }
341 return READ_SUCCEEDED;
72 } 342 }
73 343
74 bool Channel::ChannelImpl::WillDispatchInputMessage(Message* msg) { 344 bool Channel::ChannelImpl::WillDispatchInputMessage(Message* msg) {
75 return false; 345 return true;
76 } 346 }
77 347
78 bool Channel::ChannelImpl::DidEmptyInputBuffers() { 348 bool Channel::ChannelImpl::DidEmptyInputBuffers() {
79 return false; 349 return true;
80 } 350 }
81 351
82 void Channel::ChannelImpl::HandleHelloMessage(const Message& msg) { 352 void Channel::ChannelImpl::HandleHelloMessage(const Message& msg) {
83 } 353 // The Hello message contains only the process id.
84 354 PickleIterator iter(msg);
85 // static 355 int pid;
86 bool Channel::ChannelImpl::IsNamedServerInitialized( 356 if (!msg.ReadInt(&iter, &pid))
87 const std::string& channel_id) { 357 NOTREACHED();
88 return false; //file_util::PathExists(FilePath(channel_id)); 358 peer_pid_ = pid;
359 QueueHelloMessage();
360 listener()->OnChannelConnected(pid);
89 } 361 }
90 362
91 //------------------------------------------------------------------------------ 363 //------------------------------------------------------------------------------
92 // Channel's methods simply call through to ChannelImpl. 364 // Channel's methods simply call through to ChannelImpl.
93 365
94 Channel::Channel(const IPC::ChannelHandle& channel_handle, 366 Channel::Channel(const IPC::ChannelHandle& channel_handle,
95 Mode mode, 367 Mode mode,
96 Listener* listener) 368 Listener* listener)
97 : channel_impl_(new ChannelImpl(channel_handle, mode, listener)) { 369 : channel_impl_(new ChannelImpl(channel_handle, mode, listener)) {
98 } 370 }
(...skipping 11 matching lines...) Expand all
110 } 382 }
111 383
112 void Channel::set_listener(Listener* listener) { 384 void Channel::set_listener(Listener* listener) {
113 channel_impl_->set_listener(listener); 385 channel_impl_->set_listener(listener);
114 } 386 }
115 387
116 bool Channel::Send(Message* message) { 388 bool Channel::Send(Message* message) {
117 return channel_impl_->Send(message); 389 return channel_impl_->Send(message);
118 } 390 }
119 391
120 int Channel::GetClientFileDescriptor() const {
121 return channel_impl_->GetClientFileDescriptor();
122 }
123
124 int Channel::TakeClientFileDescriptor() {
125 return channel_impl_->TakeClientFileDescriptor();
126 }
127
128 bool Channel::AcceptsConnections() const {
129 return channel_impl_->AcceptsConnections();
130 }
131
132 bool Channel::HasAcceptedConnection() const {
133 return channel_impl_->HasAcceptedConnection();
134 }
135
136 bool Channel::GetClientEuid(uid_t* client_euid) const {
137 return channel_impl_->GetClientEuid(client_euid);
138 }
139
140 void Channel::ResetToAcceptingConnectionState() {
141 channel_impl_->ResetToAcceptingConnectionState();
142 }
143
144 base::ProcessId Channel::peer_pid() const { return 0; }
145
146 // static
147 bool Channel::IsNamedServerInitialized(const std::string& channel_id) {
148 return ChannelImpl::IsNamedServerInitialized(channel_id);
149 }
150
151 // static 392 // static
152 std::string Channel::GenerateVerifiedChannelID(const std::string& prefix) { 393 std::string Channel::GenerateVerifiedChannelID(const std::string& prefix) {
153 // A random name is sufficient validation on posix systems, so we don't need 394 // A random name is sufficient validation on posix systems, so we don't need
154 // an additional shared secret. 395 // an additional shared secret.
155 std::string id = prefix; 396 std::string id = prefix;
156 if (!id.empty()) 397 if (!id.empty())
157 id.append("."); 398 id.append(".");
158 399
159 return id.append(GenerateUniqueRandomChannelID()); 400 return id.append(GenerateUniqueRandomChannelID());
160 } 401 }
161 402
162 } // namespace IPC 403 } // namespace IPC
OLDNEW
« no previous file with comments | « ipc/ipc_channel_nacl.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698