• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2013 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 "mojo/system/raw_channel.h"
6 
7 #include <windows.h>
8 
9 #include "base/auto_reset.h"
10 #include "base/bind.h"
11 #include "base/compiler_specific.h"
12 #include "base/lazy_instance.h"
13 #include "base/location.h"
14 #include "base/logging.h"
15 #include "base/macros.h"
16 #include "base/memory/scoped_ptr.h"
17 #include "base/message_loop/message_loop.h"
18 #include "base/synchronization/lock.h"
19 #include "base/win/windows_version.h"
20 #include "mojo/embedder/platform_handle.h"
21 
22 namespace mojo {
23 namespace system {
24 
25 namespace {
26 
27 class VistaOrHigherFunctions {
28  public:
29   VistaOrHigherFunctions();
30 
is_vista_or_higher() const31   bool is_vista_or_higher() const { return is_vista_or_higher_; }
32 
SetFileCompletionNotificationModes(HANDLE handle,UCHAR flags)33   BOOL SetFileCompletionNotificationModes(HANDLE handle, UCHAR flags) {
34     return set_file_completion_notification_modes_(handle, flags);
35   }
36 
CancelIoEx(HANDLE handle,LPOVERLAPPED overlapped)37   BOOL CancelIoEx(HANDLE handle, LPOVERLAPPED overlapped) {
38     return cancel_io_ex_(handle, overlapped);
39   }
40 
41  private:
42   typedef BOOL (WINAPI *SetFileCompletionNotificationModesFunc)(HANDLE, UCHAR);
43   typedef BOOL (WINAPI *CancelIoExFunc)(HANDLE, LPOVERLAPPED);
44 
45   bool is_vista_or_higher_;
46   SetFileCompletionNotificationModesFunc
47       set_file_completion_notification_modes_;
48   CancelIoExFunc cancel_io_ex_;
49 };
50 
VistaOrHigherFunctions()51 VistaOrHigherFunctions::VistaOrHigherFunctions()
52     : is_vista_or_higher_(base::win::GetVersion() >= base::win::VERSION_VISTA),
53       set_file_completion_notification_modes_(NULL),
54       cancel_io_ex_(NULL) {
55   if (!is_vista_or_higher_)
56     return;
57 
58   HMODULE module = GetModuleHandleW(L"kernel32.dll");
59   set_file_completion_notification_modes_ =
60       reinterpret_cast<SetFileCompletionNotificationModesFunc>(
61           GetProcAddress(module, "SetFileCompletionNotificationModes"));
62   DCHECK(set_file_completion_notification_modes_);
63 
64   cancel_io_ex_ = reinterpret_cast<CancelIoExFunc>(
65       GetProcAddress(module, "CancelIoEx"));
66   DCHECK(cancel_io_ex_);
67 }
68 
69 base::LazyInstance<VistaOrHigherFunctions> g_vista_or_higher_functions =
70     LAZY_INSTANCE_INITIALIZER;
71 
72 class RawChannelWin : public RawChannel {
73  public:
74   RawChannelWin(embedder::ScopedPlatformHandle handle);
75   virtual ~RawChannelWin();
76 
77   // |RawChannel| public methods:
78   virtual size_t GetSerializedPlatformHandleSize() const OVERRIDE;
79 
80  private:
81   // RawChannelIOHandler receives OS notifications for I/O completion. It must
82   // be created on the I/O thread.
83   //
84   // It manages its own destruction. Destruction happens on the I/O thread when
85   // all the following conditions are satisfied:
86   //   - |DetachFromOwnerNoLock()| has been called;
87   //   - there is no pending read;
88   //   - there is no pending write.
89   class RawChannelIOHandler : public base::MessageLoopForIO::IOHandler {
90    public:
91     RawChannelIOHandler(RawChannelWin* owner,
92                         embedder::ScopedPlatformHandle handle);
93 
handle() const94     HANDLE handle() const { return handle_.get().handle; }
95 
96     // The following methods are only called by the owner on the I/O thread.
97     bool pending_read() const;
98     base::MessageLoopForIO::IOContext* read_context();
99     // Instructs the object to wait for an |OnIOCompleted()| notification.
100     void OnPendingReadStarted();
101 
102     // The following methods are only called by the owner under
103     // |owner_->write_lock()|.
104     bool pending_write_no_lock() const;
105     base::MessageLoopForIO::IOContext* write_context_no_lock();
106     // Instructs the object to wait for an |OnIOCompleted()| notification.
107     void OnPendingWriteStartedNoLock();
108 
109     // |base::MessageLoopForIO::IOHandler| implementation:
110     // Must be called on the I/O thread. It could be called before or after
111     // detached from the owner.
112     virtual void OnIOCompleted(base::MessageLoopForIO::IOContext* context,
113                                DWORD bytes_transferred,
114                                DWORD error) OVERRIDE;
115 
116     // Must be called on the I/O thread under |owner_->write_lock()|.
117     // After this call, the owner must not make any further calls on this
118     // object, and therefore the object is used on the I/O thread exclusively
119     // (if it stays alive).
120     void DetachFromOwnerNoLock(scoped_ptr<ReadBuffer> read_buffer,
121                                scoped_ptr<WriteBuffer> write_buffer);
122 
123    private:
124     virtual ~RawChannelIOHandler();
125 
126     // Returns true if |owner_| has been reset and there is not pending read or
127     // write.
128     // Must be called on the I/O thread.
129     bool ShouldSelfDestruct() const;
130 
131     // Must be called on the I/O thread. It may be called before or after
132     // detaching from the owner.
133     void OnReadCompleted(DWORD bytes_read, DWORD error);
134     // Must be called on the I/O thread. It may be called before or after
135     // detaching from the owner.
136     void OnWriteCompleted(DWORD bytes_written, DWORD error);
137 
138     embedder::ScopedPlatformHandle handle_;
139 
140     // |owner_| is reset on the I/O thread under |owner_->write_lock()|.
141     // Therefore, it may be used on any thread under lock; or on the I/O thread
142     // without locking.
143     RawChannelWin* owner_;
144 
145     // The following members must be used on the I/O thread.
146     scoped_ptr<ReadBuffer> preserved_read_buffer_after_detach_;
147     scoped_ptr<WriteBuffer> preserved_write_buffer_after_detach_;
148     bool suppress_self_destruct_;
149 
150     bool pending_read_;
151     base::MessageLoopForIO::IOContext read_context_;
152 
153     // The following members must be used under |owner_->write_lock()| while the
154     // object is still attached to the owner, and only on the I/O thread
155     // afterwards.
156     bool pending_write_;
157     base::MessageLoopForIO::IOContext write_context_;
158 
159     DISALLOW_COPY_AND_ASSIGN(RawChannelIOHandler);
160   };
161 
162   // |RawChannel| private methods:
163   virtual IOResult Read(size_t* bytes_read) OVERRIDE;
164   virtual IOResult ScheduleRead() OVERRIDE;
165   virtual embedder::ScopedPlatformHandleVectorPtr GetReadPlatformHandles(
166       size_t num_platform_handles,
167       const void* platform_handle_table) OVERRIDE;
168   virtual IOResult WriteNoLock(size_t* platform_handles_written,
169                                size_t* bytes_written) OVERRIDE;
170   virtual IOResult ScheduleWriteNoLock() OVERRIDE;
171   virtual bool OnInit() OVERRIDE;
172   virtual void OnShutdownNoLock(
173       scoped_ptr<ReadBuffer> read_buffer,
174       scoped_ptr<WriteBuffer> write_buffer) OVERRIDE;
175 
176   // Passed to |io_handler_| during initialization.
177   embedder::ScopedPlatformHandle handle_;
178 
179   RawChannelIOHandler* io_handler_;
180 
181   const bool skip_completion_port_on_success_;
182 
183   DISALLOW_COPY_AND_ASSIGN(RawChannelWin);
184 };
185 
RawChannelIOHandler(RawChannelWin * owner,embedder::ScopedPlatformHandle handle)186 RawChannelWin::RawChannelIOHandler::RawChannelIOHandler(
187     RawChannelWin* owner,
188     embedder::ScopedPlatformHandle handle) : handle_(handle.Pass()),
189                                              owner_(owner),
190                                              suppress_self_destruct_(false),
191                                              pending_read_(false),
192                                              pending_write_(false) {
193   memset(&read_context_.overlapped, 0, sizeof(read_context_.overlapped));
194   read_context_.handler = this;
195   memset(&write_context_.overlapped, 0, sizeof(write_context_.overlapped));
196   write_context_.handler = this;
197 
198   owner_->message_loop_for_io()->RegisterIOHandler(handle_.get().handle, this);
199 }
200 
~RawChannelIOHandler()201 RawChannelWin::RawChannelIOHandler::~RawChannelIOHandler() {
202   DCHECK(ShouldSelfDestruct());
203 }
204 
pending_read() const205 bool RawChannelWin::RawChannelIOHandler::pending_read() const {
206   DCHECK(owner_);
207   DCHECK_EQ(base::MessageLoop::current(), owner_->message_loop_for_io());
208   return pending_read_;
209 }
210 
211 base::MessageLoopForIO::IOContext*
read_context()212     RawChannelWin::RawChannelIOHandler::read_context() {
213   DCHECK(owner_);
214   DCHECK_EQ(base::MessageLoop::current(), owner_->message_loop_for_io());
215   return &read_context_;
216 }
217 
OnPendingReadStarted()218 void RawChannelWin::RawChannelIOHandler::OnPendingReadStarted() {
219   DCHECK(owner_);
220   DCHECK_EQ(base::MessageLoop::current(), owner_->message_loop_for_io());
221   DCHECK(!pending_read_);
222   pending_read_ = true;
223 }
224 
pending_write_no_lock() const225 bool RawChannelWin::RawChannelIOHandler::pending_write_no_lock() const {
226   DCHECK(owner_);
227   owner_->write_lock().AssertAcquired();
228   return pending_write_;
229 }
230 
231 base::MessageLoopForIO::IOContext*
write_context_no_lock()232     RawChannelWin::RawChannelIOHandler::write_context_no_lock() {
233   DCHECK(owner_);
234   owner_->write_lock().AssertAcquired();
235   return &write_context_;
236 }
237 
OnPendingWriteStartedNoLock()238 void RawChannelWin::RawChannelIOHandler::OnPendingWriteStartedNoLock() {
239   DCHECK(owner_);
240   owner_->write_lock().AssertAcquired();
241   DCHECK(!pending_write_);
242   pending_write_ = true;
243 }
244 
OnIOCompleted(base::MessageLoopForIO::IOContext * context,DWORD bytes_transferred,DWORD error)245 void RawChannelWin::RawChannelIOHandler::OnIOCompleted(
246     base::MessageLoopForIO::IOContext* context,
247     DWORD bytes_transferred,
248     DWORD error) {
249   DCHECK(!owner_ ||
250          base::MessageLoop::current() == owner_->message_loop_for_io());
251 
252   {
253     // Suppress self-destruction inside |OnReadCompleted()|, etc. (in case they
254     // result in a call to |Shutdown()|).
255     base::AutoReset<bool> resetter(&suppress_self_destruct_, true);
256 
257     if (context == &read_context_)
258       OnReadCompleted(bytes_transferred, error);
259     else if (context == &write_context_)
260       OnWriteCompleted(bytes_transferred, error);
261     else
262       NOTREACHED();
263   }
264 
265   if (ShouldSelfDestruct())
266     delete this;
267 }
268 
DetachFromOwnerNoLock(scoped_ptr<ReadBuffer> read_buffer,scoped_ptr<WriteBuffer> write_buffer)269 void RawChannelWin::RawChannelIOHandler::DetachFromOwnerNoLock(
270     scoped_ptr<ReadBuffer> read_buffer,
271     scoped_ptr<WriteBuffer> write_buffer) {
272   DCHECK(owner_);
273   DCHECK_EQ(base::MessageLoop::current(), owner_->message_loop_for_io());
274   owner_->write_lock().AssertAcquired();
275 
276   // If read/write is pending, we have to retain the corresponding buffer.
277   if (pending_read_)
278     preserved_read_buffer_after_detach_ = read_buffer.Pass();
279   if (pending_write_)
280     preserved_write_buffer_after_detach_ = write_buffer.Pass();
281 
282   owner_ = NULL;
283   if (ShouldSelfDestruct())
284     delete this;
285 }
286 
ShouldSelfDestruct() const287 bool RawChannelWin::RawChannelIOHandler::ShouldSelfDestruct() const {
288   if (owner_ || suppress_self_destruct_)
289     return false;
290 
291   // Note: Detached, hence no lock needed for |pending_write_|.
292   return !pending_read_ && !pending_write_;
293 }
294 
OnReadCompleted(DWORD bytes_read,DWORD error)295 void RawChannelWin::RawChannelIOHandler::OnReadCompleted(DWORD bytes_read,
296                                                          DWORD error) {
297   DCHECK(!owner_ ||
298          base::MessageLoop::current() == owner_->message_loop_for_io());
299   DCHECK(suppress_self_destruct_);
300 
301   CHECK(pending_read_);
302   pending_read_ = false;
303   if (!owner_)
304     return;
305 
306   if (error != ERROR_SUCCESS) {
307     DCHECK_EQ(bytes_read, 0u);
308     LOG_IF(ERROR, error != ERROR_BROKEN_PIPE)
309         << "ReadFile: " << logging::SystemErrorCodeToString(error);
310     owner_->OnReadCompleted(false, 0);
311   } else {
312     DCHECK_GT(bytes_read, 0u);
313     owner_->OnReadCompleted(true, bytes_read);
314   }
315 }
316 
OnWriteCompleted(DWORD bytes_written,DWORD error)317 void RawChannelWin::RawChannelIOHandler::OnWriteCompleted(DWORD bytes_written,
318                                                           DWORD error) {
319   DCHECK(!owner_ ||
320          base::MessageLoop::current() == owner_->message_loop_for_io());
321   DCHECK(suppress_self_destruct_);
322 
323   if (!owner_) {
324     // No lock needed.
325     CHECK(pending_write_);
326     pending_write_ = false;
327     return;
328   }
329 
330   {
331     base::AutoLock locker(owner_->write_lock());
332     CHECK(pending_write_);
333     pending_write_ = false;
334   }
335 
336   if (error != ERROR_SUCCESS) {
337     LOG(ERROR) << "WriteFile: " << logging::SystemErrorCodeToString(error);
338     owner_->OnWriteCompleted(false, 0, 0);
339   } else {
340     owner_->OnWriteCompleted(true, 0, bytes_written);
341   }
342 }
343 
RawChannelWin(embedder::ScopedPlatformHandle handle)344 RawChannelWin::RawChannelWin(embedder::ScopedPlatformHandle handle)
345     : handle_(handle.Pass()),
346       io_handler_(NULL),
347       skip_completion_port_on_success_(
348           g_vista_or_higher_functions.Get().is_vista_or_higher()) {
349   DCHECK(handle_.is_valid());
350 }
351 
~RawChannelWin()352 RawChannelWin::~RawChannelWin() {
353   DCHECK(!io_handler_);
354 }
355 
GetSerializedPlatformHandleSize() const356 size_t RawChannelWin::GetSerializedPlatformHandleSize() const {
357   // TODO(vtl): Implement.
358   return 0;
359 }
360 
Read(size_t * bytes_read)361 RawChannel::IOResult RawChannelWin::Read(size_t* bytes_read) {
362   DCHECK_EQ(base::MessageLoop::current(), message_loop_for_io());
363   DCHECK(io_handler_);
364   DCHECK(!io_handler_->pending_read());
365 
366   char* buffer = NULL;
367   size_t bytes_to_read = 0;
368   read_buffer()->GetBuffer(&buffer, &bytes_to_read);
369 
370   DWORD bytes_read_dword = 0;
371   BOOL result = ReadFile(io_handler_->handle(),
372                          buffer,
373                          static_cast<DWORD>(bytes_to_read),
374                          &bytes_read_dword,
375                          &io_handler_->read_context()->overlapped);
376   if (!result) {
377     DCHECK_EQ(bytes_read_dword, 0u);
378     DWORD error = GetLastError();
379     if (error != ERROR_IO_PENDING) {
380       LOG_IF(ERROR, error != ERROR_BROKEN_PIPE)
381           << "ReadFile: " << logging::SystemErrorCodeToString(error);
382       return IO_FAILED;
383     }
384   }
385 
386   if (result && skip_completion_port_on_success_) {
387     *bytes_read = bytes_read_dword;
388     return IO_SUCCEEDED;
389   }
390 
391   // If the read is pending or the read has succeeded but we don't skip
392   // completion port on success, instruct |io_handler_| to wait for the
393   // completion packet.
394   //
395   // TODO(yzshen): It seems there isn't document saying that all error cases
396   // (other than ERROR_IO_PENDING) are guaranteed to *not* queue a completion
397   // packet. If we do get one for errors, |RawChannelIOHandler::OnIOCompleted()|
398   // will crash so we will learn about it.
399 
400   io_handler_->OnPendingReadStarted();
401   return IO_PENDING;
402 }
403 
ScheduleRead()404 RawChannel::IOResult RawChannelWin::ScheduleRead() {
405   DCHECK_EQ(base::MessageLoop::current(), message_loop_for_io());
406   DCHECK(io_handler_);
407   DCHECK(!io_handler_->pending_read());
408 
409   size_t bytes_read = 0;
410   IOResult io_result = Read(&bytes_read);
411   if (io_result == IO_SUCCEEDED) {
412     DCHECK(skip_completion_port_on_success_);
413 
414     // We have finished reading successfully. Queue a notification manually.
415     io_handler_->OnPendingReadStarted();
416     // |io_handler_| won't go away before the task is run, so it is safe to use
417     // |base::Unretained()|.
418     message_loop_for_io()->PostTask(
419         FROM_HERE,
420         base::Bind(&RawChannelIOHandler::OnIOCompleted,
421                    base::Unretained(io_handler_),
422                    base::Unretained(io_handler_->read_context()),
423                    static_cast<DWORD>(bytes_read),
424                    ERROR_SUCCESS));
425     return IO_PENDING;
426   }
427 
428   return io_result;
429 }
430 
GetReadPlatformHandles(size_t num_platform_handles,const void * platform_handle_table)431 embedder::ScopedPlatformHandleVectorPtr RawChannelWin::GetReadPlatformHandles(
432     size_t num_platform_handles,
433     const void* platform_handle_table) {
434   // TODO(vtl): Implement.
435   NOTIMPLEMENTED();
436   return embedder::ScopedPlatformHandleVectorPtr();
437 }
438 
WriteNoLock(size_t * platform_handles_written,size_t * bytes_written)439 RawChannel::IOResult RawChannelWin::WriteNoLock(
440     size_t* platform_handles_written,
441     size_t* bytes_written) {
442   write_lock().AssertAcquired();
443 
444   DCHECK(io_handler_);
445   DCHECK(!io_handler_->pending_write_no_lock());
446 
447   if (write_buffer_no_lock()->HavePlatformHandlesToSend()) {
448     // TODO(vtl): Implement.
449     NOTIMPLEMENTED();
450   }
451 
452   std::vector<WriteBuffer::Buffer> buffers;
453   write_buffer_no_lock()->GetBuffers(&buffers);
454   DCHECK(!buffers.empty());
455 
456   // TODO(yzshen): Handle multi-segment writes more efficiently.
457   DWORD bytes_written_dword = 0;
458   BOOL result = WriteFile(io_handler_->handle(),
459                           buffers[0].addr,
460                           static_cast<DWORD>(buffers[0].size),
461                           &bytes_written_dword,
462                           &io_handler_->write_context_no_lock()->overlapped);
463   if (!result && GetLastError() != ERROR_IO_PENDING) {
464     PLOG(ERROR) << "WriteFile";
465     return IO_FAILED;
466   }
467 
468   if (result && skip_completion_port_on_success_) {
469     *platform_handles_written = 0;
470     *bytes_written = bytes_written_dword;
471     return IO_SUCCEEDED;
472   }
473 
474   // If the write is pending or the write has succeeded but we don't skip
475   // completion port on success, instruct |io_handler_| to wait for the
476   // completion packet.
477   //
478   // TODO(yzshen): it seems there isn't document saying that all error cases
479   // (other than ERROR_IO_PENDING) are guaranteed to *not* queue a completion
480   // packet. If we do get one for errors, |RawChannelIOHandler::OnIOCompleted()|
481   // will crash so we will learn about it.
482 
483   io_handler_->OnPendingWriteStartedNoLock();
484   return IO_PENDING;
485 }
486 
ScheduleWriteNoLock()487 RawChannel::IOResult RawChannelWin::ScheduleWriteNoLock() {
488   write_lock().AssertAcquired();
489 
490   DCHECK(io_handler_);
491   DCHECK(!io_handler_->pending_write_no_lock());
492 
493   // TODO(vtl): Do something with |platform_handles_written|.
494   size_t platform_handles_written = 0;
495   size_t bytes_written = 0;
496   IOResult io_result = WriteNoLock(&platform_handles_written, &bytes_written);
497   if (io_result == IO_SUCCEEDED) {
498     DCHECK(skip_completion_port_on_success_);
499 
500     // We have finished writing successfully. Queue a notification manually.
501     io_handler_->OnPendingWriteStartedNoLock();
502     // |io_handler_| won't go away before that task is run, so it is safe to use
503     // |base::Unretained()|.
504     message_loop_for_io()->PostTask(
505         FROM_HERE,
506         base::Bind(&RawChannelIOHandler::OnIOCompleted,
507                    base::Unretained(io_handler_),
508                    base::Unretained(io_handler_->write_context_no_lock()),
509                    static_cast<DWORD>(bytes_written),
510                    ERROR_SUCCESS));
511     return IO_PENDING;
512   }
513 
514   return io_result;
515 }
516 
OnInit()517 bool RawChannelWin::OnInit() {
518   DCHECK_EQ(base::MessageLoop::current(), message_loop_for_io());
519 
520   DCHECK(handle_.is_valid());
521   if (skip_completion_port_on_success_ &&
522       !g_vista_or_higher_functions.Get().SetFileCompletionNotificationModes(
523           handle_.get().handle, FILE_SKIP_COMPLETION_PORT_ON_SUCCESS)) {
524     return false;
525   }
526 
527   DCHECK(!io_handler_);
528   io_handler_ = new RawChannelIOHandler(this, handle_.Pass());
529 
530   return true;
531 }
532 
OnShutdownNoLock(scoped_ptr<ReadBuffer> read_buffer,scoped_ptr<WriteBuffer> write_buffer)533 void RawChannelWin::OnShutdownNoLock(scoped_ptr<ReadBuffer> read_buffer,
534                                      scoped_ptr<WriteBuffer> write_buffer) {
535   DCHECK_EQ(base::MessageLoop::current(), message_loop_for_io());
536   DCHECK(io_handler_);
537 
538   write_lock().AssertAcquired();
539 
540   if (io_handler_->pending_read() || io_handler_->pending_write_no_lock()) {
541     // |io_handler_| will be alive until pending read/write (if any) completes.
542     // Call |CancelIoEx()| or |CancelIo()| so that resources can be freed up as
543     // soon as possible.
544     // Note: |CancelIo()| only cancels read/write requests started from this
545     // thread.
546     if (g_vista_or_higher_functions.Get().is_vista_or_higher())
547       g_vista_or_higher_functions.Get().CancelIoEx(io_handler_->handle(), NULL);
548     else
549       CancelIo(io_handler_->handle());
550   }
551 
552   io_handler_->DetachFromOwnerNoLock(read_buffer.Pass(), write_buffer.Pass());
553   io_handler_ = NULL;
554 }
555 
556 }  // namespace
557 
558 // -----------------------------------------------------------------------------
559 
560 // Static factory method declared in raw_channel.h.
561 // static
Create(embedder::ScopedPlatformHandle handle)562 scoped_ptr<RawChannel> RawChannel::Create(
563     embedder::ScopedPlatformHandle handle) {
564   return scoped_ptr<RawChannel>(new RawChannelWin(handle.Pass()));
565 }
566 
567 }  // namespace system
568 }  // namespace mojo
569