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.
5 #include "mojo/edk/system/raw_channel.h"
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/edk/embedder/platform_handle.h"
27 class VistaOrHigherFunctions
{
29 VistaOrHigherFunctions();
31 bool is_vista_or_higher() const { return is_vista_or_higher_
; }
33 BOOL
SetFileCompletionNotificationModes(HANDLE handle
, UCHAR flags
) {
34 return set_file_completion_notification_modes_(handle
, flags
);
37 BOOL
CancelIoEx(HANDLE handle
, LPOVERLAPPED overlapped
) {
38 return cancel_io_ex_(handle
, overlapped
);
42 typedef BOOL(WINAPI
* SetFileCompletionNotificationModesFunc
)(HANDLE
, UCHAR
);
43 typedef BOOL(WINAPI
* CancelIoExFunc
)(HANDLE
, LPOVERLAPPED
);
45 bool is_vista_or_higher_
;
46 SetFileCompletionNotificationModesFunc
47 set_file_completion_notification_modes_
;
48 CancelIoExFunc cancel_io_ex_
;
51 VistaOrHigherFunctions::VistaOrHigherFunctions()
52 : is_vista_or_higher_(base::win::GetVersion() >= base::win::VERSION_VISTA
),
53 set_file_completion_notification_modes_(nullptr),
54 cancel_io_ex_(nullptr) {
55 if (!is_vista_or_higher_
)
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_
);
65 reinterpret_cast<CancelIoExFunc
>(GetProcAddress(module
, "CancelIoEx"));
66 DCHECK(cancel_io_ex_
);
69 base::LazyInstance
<VistaOrHigherFunctions
> g_vista_or_higher_functions
=
70 LAZY_INSTANCE_INITIALIZER
;
72 class RawChannelWin
: public RawChannel
{
74 RawChannelWin(embedder::ScopedPlatformHandle handle
);
75 ~RawChannelWin() override
;
77 // |RawChannel| public methods:
78 size_t GetSerializedPlatformHandleSize() const override
;
81 // RawChannelIOHandler receives OS notifications for I/O completion. It must
82 // be created on the I/O thread.
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
{
91 RawChannelIOHandler(RawChannelWin
* owner
,
92 embedder::ScopedPlatformHandle handle
);
94 HANDLE
handle() const { return handle_
.get().handle
; }
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();
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();
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 void OnIOCompleted(base::MessageLoopForIO::IOContext
* context
,
113 DWORD bytes_transferred
,
114 DWORD error
) override
;
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
);
124 ~RawChannelIOHandler() override
;
126 // Returns true if |owner_| has been reset and there is not pending read or
128 // Must be called on the I/O thread.
129 bool ShouldSelfDestruct() const;
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
);
138 embedder::ScopedPlatformHandle handle_
;
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
143 RawChannelWin
* owner_
;
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_
;
151 base::MessageLoopForIO::IOContext read_context_
;
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
157 base::MessageLoopForIO::IOContext write_context_
;
159 DISALLOW_COPY_AND_ASSIGN(RawChannelIOHandler
);
162 // |RawChannel| private methods:
163 IOResult
Read(size_t* bytes_read
) override
;
164 IOResult
ScheduleRead() override
;
165 embedder::ScopedPlatformHandleVectorPtr
GetReadPlatformHandles(
166 size_t num_platform_handles
,
167 const void* platform_handle_table
) override
;
168 IOResult
WriteNoLock(size_t* platform_handles_written
,
169 size_t* bytes_written
) override
;
170 IOResult
ScheduleWriteNoLock() override
;
171 bool OnInit() override
;
172 void OnShutdownNoLock(scoped_ptr
<ReadBuffer
> read_buffer
,
173 scoped_ptr
<WriteBuffer
> write_buffer
) override
;
175 // Passed to |io_handler_| during initialization.
176 embedder::ScopedPlatformHandle handle_
;
178 RawChannelIOHandler
* io_handler_
;
180 const bool skip_completion_port_on_success_
;
182 DISALLOW_COPY_AND_ASSIGN(RawChannelWin
);
185 RawChannelWin::RawChannelIOHandler::RawChannelIOHandler(
186 RawChannelWin
* owner
,
187 embedder::ScopedPlatformHandle handle
)
188 : handle_(handle
.Pass()),
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;
198 owner_
->message_loop_for_io()->RegisterIOHandler(handle_
.get().handle
, this);
201 RawChannelWin::RawChannelIOHandler::~RawChannelIOHandler() {
202 DCHECK(ShouldSelfDestruct());
205 bool RawChannelWin::RawChannelIOHandler::pending_read() const {
207 DCHECK_EQ(base::MessageLoop::current(), owner_
->message_loop_for_io());
208 return pending_read_
;
211 base::MessageLoopForIO::IOContext
*
212 RawChannelWin::RawChannelIOHandler::read_context() {
214 DCHECK_EQ(base::MessageLoop::current(), owner_
->message_loop_for_io());
215 return &read_context_
;
218 void RawChannelWin::RawChannelIOHandler::OnPendingReadStarted() {
220 DCHECK_EQ(base::MessageLoop::current(), owner_
->message_loop_for_io());
221 DCHECK(!pending_read_
);
222 pending_read_
= true;
225 bool RawChannelWin::RawChannelIOHandler::pending_write_no_lock() const {
227 owner_
->write_lock().AssertAcquired();
228 return pending_write_
;
231 base::MessageLoopForIO::IOContext
*
232 RawChannelWin::RawChannelIOHandler::write_context_no_lock() {
234 owner_
->write_lock().AssertAcquired();
235 return &write_context_
;
238 void RawChannelWin::RawChannelIOHandler::OnPendingWriteStartedNoLock() {
240 owner_
->write_lock().AssertAcquired();
241 DCHECK(!pending_write_
);
242 pending_write_
= true;
245 void RawChannelWin::RawChannelIOHandler::OnIOCompleted(
246 base::MessageLoopForIO::IOContext
* context
,
247 DWORD bytes_transferred
,
250 base::MessageLoop::current() == owner_
->message_loop_for_io());
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);
257 if (context
== &read_context_
)
258 OnReadCompleted(bytes_transferred
, error
);
259 else if (context
== &write_context_
)
260 OnWriteCompleted(bytes_transferred
, error
);
265 if (ShouldSelfDestruct())
269 void RawChannelWin::RawChannelIOHandler::DetachFromOwnerNoLock(
270 scoped_ptr
<ReadBuffer
> read_buffer
,
271 scoped_ptr
<WriteBuffer
> write_buffer
) {
273 DCHECK_EQ(base::MessageLoop::current(), owner_
->message_loop_for_io());
274 owner_
->write_lock().AssertAcquired();
276 // If read/write is pending, we have to retain the corresponding buffer.
278 preserved_read_buffer_after_detach_
= read_buffer
.Pass();
280 preserved_write_buffer_after_detach_
= write_buffer
.Pass();
283 if (ShouldSelfDestruct())
287 bool RawChannelWin::RawChannelIOHandler::ShouldSelfDestruct() const {
288 if (owner_
|| suppress_self_destruct_
)
291 // Note: Detached, hence no lock needed for |pending_write_|.
292 return !pending_read_
&& !pending_write_
;
295 void RawChannelWin::RawChannelIOHandler::OnReadCompleted(DWORD bytes_read
,
298 base::MessageLoop::current() == owner_
->message_loop_for_io());
299 DCHECK(suppress_self_destruct_
);
301 CHECK(pending_read_
);
302 pending_read_
= false;
306 if (error
== ERROR_SUCCESS
) {
307 DCHECK_GT(bytes_read
, 0u);
308 owner_
->OnReadCompleted(IO_SUCCEEDED
, bytes_read
);
309 } else if (error
== ERROR_BROKEN_PIPE
) {
310 DCHECK_EQ(bytes_read
, 0u);
311 owner_
->OnReadCompleted(IO_FAILED_SHUTDOWN
, 0);
313 DCHECK_EQ(bytes_read
, 0u);
314 LOG(WARNING
) << "ReadFile: " << logging::SystemErrorCodeToString(error
);
315 owner_
->OnReadCompleted(IO_FAILED_UNKNOWN
, 0);
319 void RawChannelWin::RawChannelIOHandler::OnWriteCompleted(DWORD bytes_written
,
322 base::MessageLoop::current() == owner_
->message_loop_for_io());
323 DCHECK(suppress_self_destruct_
);
327 CHECK(pending_write_
);
328 pending_write_
= false;
333 base::AutoLock
locker(owner_
->write_lock());
334 CHECK(pending_write_
);
335 pending_write_
= false;
338 if (error
== ERROR_SUCCESS
) {
339 owner_
->OnWriteCompleted(IO_SUCCEEDED
, 0, bytes_written
);
340 } else if (error
== ERROR_BROKEN_PIPE
) {
341 owner_
->OnWriteCompleted(IO_FAILED_SHUTDOWN
, 0, 0);
343 LOG(WARNING
) << "WriteFile: " << logging::SystemErrorCodeToString(error
);
344 owner_
->OnWriteCompleted(IO_FAILED_UNKNOWN
, 0, 0);
348 RawChannelWin::RawChannelWin(embedder::ScopedPlatformHandle handle
)
349 : handle_(handle
.Pass()),
350 io_handler_(nullptr),
351 skip_completion_port_on_success_(
352 g_vista_or_higher_functions
.Get().is_vista_or_higher()) {
353 DCHECK(handle_
.is_valid());
356 RawChannelWin::~RawChannelWin() {
357 DCHECK(!io_handler_
);
360 size_t RawChannelWin::GetSerializedPlatformHandleSize() const {
361 // TODO(vtl): Implement.
365 RawChannel::IOResult
RawChannelWin::Read(size_t* bytes_read
) {
366 DCHECK_EQ(base::MessageLoop::current(), message_loop_for_io());
368 DCHECK(!io_handler_
->pending_read());
370 char* buffer
= nullptr;
371 size_t bytes_to_read
= 0;
372 read_buffer()->GetBuffer(&buffer
, &bytes_to_read
);
375 ReadFile(io_handler_
->handle(), buffer
, static_cast<DWORD
>(bytes_to_read
),
376 nullptr, &io_handler_
->read_context()->overlapped
);
378 DWORD error
= GetLastError();
379 if (error
== ERROR_BROKEN_PIPE
)
380 return IO_FAILED_SHUTDOWN
;
381 if (error
!= ERROR_IO_PENDING
) {
382 LOG(WARNING
) << "ReadFile: " << logging::SystemErrorCodeToString(error
);
383 return IO_FAILED_UNKNOWN
;
387 if (result
&& skip_completion_port_on_success_
) {
388 DWORD bytes_read_dword
= 0;
389 BOOL get_size_result
= GetOverlappedResult(
390 io_handler_
->handle(), &io_handler_
->read_context()->overlapped
,
391 &bytes_read_dword
, FALSE
);
392 DPCHECK(get_size_result
);
393 *bytes_read
= bytes_read_dword
;
397 // If the read is pending or the read has succeeded but we don't skip
398 // completion port on success, instruct |io_handler_| to wait for the
399 // completion packet.
401 // TODO(yzshen): It seems there isn't document saying that all error cases
402 // (other than ERROR_IO_PENDING) are guaranteed to *not* queue a completion
403 // packet. If we do get one for errors, |RawChannelIOHandler::OnIOCompleted()|
404 // will crash so we will learn about it.
406 io_handler_
->OnPendingReadStarted();
410 RawChannel::IOResult
RawChannelWin::ScheduleRead() {
411 DCHECK_EQ(base::MessageLoop::current(), message_loop_for_io());
413 DCHECK(!io_handler_
->pending_read());
415 size_t bytes_read
= 0;
416 IOResult io_result
= Read(&bytes_read
);
417 if (io_result
== IO_SUCCEEDED
) {
418 DCHECK(skip_completion_port_on_success_
);
420 // We have finished reading successfully. Queue a notification manually.
421 io_handler_
->OnPendingReadStarted();
422 // |io_handler_| won't go away before the task is run, so it is safe to use
423 // |base::Unretained()|.
424 message_loop_for_io()->PostTask(
425 FROM_HERE
, base::Bind(&RawChannelIOHandler::OnIOCompleted
,
426 base::Unretained(io_handler_
),
427 base::Unretained(io_handler_
->read_context()),
428 static_cast<DWORD
>(bytes_read
), ERROR_SUCCESS
));
435 embedder::ScopedPlatformHandleVectorPtr
RawChannelWin::GetReadPlatformHandles(
436 size_t num_platform_handles
,
437 const void* platform_handle_table
) {
438 // TODO(vtl): Implement.
440 return embedder::ScopedPlatformHandleVectorPtr();
443 RawChannel::IOResult
RawChannelWin::WriteNoLock(
444 size_t* platform_handles_written
,
445 size_t* bytes_written
) {
446 write_lock().AssertAcquired();
449 DCHECK(!io_handler_
->pending_write_no_lock());
451 if (write_buffer_no_lock()->HavePlatformHandlesToSend()) {
452 // TODO(vtl): Implement.
456 std::vector
<WriteBuffer::Buffer
> buffers
;
457 write_buffer_no_lock()->GetBuffers(&buffers
);
458 DCHECK(!buffers
.empty());
460 // TODO(yzshen): Handle multi-segment writes more efficiently.
461 DWORD bytes_written_dword
= 0;
463 WriteFile(io_handler_
->handle(), buffers
[0].addr
,
464 static_cast<DWORD
>(buffers
[0].size
), &bytes_written_dword
,
465 &io_handler_
->write_context_no_lock()->overlapped
);
467 DWORD error
= GetLastError();
468 if (error
== ERROR_BROKEN_PIPE
)
469 return IO_FAILED_SHUTDOWN
;
470 if (error
!= ERROR_IO_PENDING
) {
471 LOG(WARNING
) << "WriteFile: " << logging::SystemErrorCodeToString(error
);
472 return IO_FAILED_UNKNOWN
;
476 if (result
&& skip_completion_port_on_success_
) {
477 *platform_handles_written
= 0;
478 *bytes_written
= bytes_written_dword
;
482 // If the write is pending or the write has succeeded but we don't skip
483 // completion port on success, instruct |io_handler_| to wait for the
484 // completion packet.
486 // TODO(yzshen): it seems there isn't document saying that all error cases
487 // (other than ERROR_IO_PENDING) are guaranteed to *not* queue a completion
488 // packet. If we do get one for errors, |RawChannelIOHandler::OnIOCompleted()|
489 // will crash so we will learn about it.
491 io_handler_
->OnPendingWriteStartedNoLock();
495 RawChannel::IOResult
RawChannelWin::ScheduleWriteNoLock() {
496 write_lock().AssertAcquired();
499 DCHECK(!io_handler_
->pending_write_no_lock());
501 // TODO(vtl): Do something with |platform_handles_written|.
502 size_t platform_handles_written
= 0;
503 size_t bytes_written
= 0;
504 IOResult io_result
= WriteNoLock(&platform_handles_written
, &bytes_written
);
505 if (io_result
== IO_SUCCEEDED
) {
506 DCHECK(skip_completion_port_on_success_
);
508 // We have finished writing successfully. Queue a notification manually.
509 io_handler_
->OnPendingWriteStartedNoLock();
510 // |io_handler_| won't go away before that task is run, so it is safe to use
511 // |base::Unretained()|.
512 message_loop_for_io()->PostTask(
514 base::Bind(&RawChannelIOHandler::OnIOCompleted
,
515 base::Unretained(io_handler_
),
516 base::Unretained(io_handler_
->write_context_no_lock()),
517 static_cast<DWORD
>(bytes_written
), ERROR_SUCCESS
));
524 bool RawChannelWin::OnInit() {
525 DCHECK_EQ(base::MessageLoop::current(), message_loop_for_io());
527 DCHECK(handle_
.is_valid());
528 if (skip_completion_port_on_success_
&&
529 !g_vista_or_higher_functions
.Get().SetFileCompletionNotificationModes(
530 handle_
.get().handle
, FILE_SKIP_COMPLETION_PORT_ON_SUCCESS
)) {
534 DCHECK(!io_handler_
);
535 io_handler_
= new RawChannelIOHandler(this, handle_
.Pass());
540 void RawChannelWin::OnShutdownNoLock(scoped_ptr
<ReadBuffer
> read_buffer
,
541 scoped_ptr
<WriteBuffer
> write_buffer
) {
542 DCHECK_EQ(base::MessageLoop::current(), message_loop_for_io());
545 write_lock().AssertAcquired();
547 if (io_handler_
->pending_read() || io_handler_
->pending_write_no_lock()) {
548 // |io_handler_| will be alive until pending read/write (if any) completes.
549 // Call |CancelIoEx()| or |CancelIo()| so that resources can be freed up as
551 // Note: |CancelIo()| only cancels read/write requests started from this
553 if (g_vista_or_higher_functions
.Get().is_vista_or_higher()) {
554 g_vista_or_higher_functions
.Get().CancelIoEx(io_handler_
->handle(),
557 CancelIo(io_handler_
->handle());
561 io_handler_
->DetachFromOwnerNoLock(read_buffer
.Pass(), write_buffer
.Pass());
562 io_handler_
= nullptr;
567 // -----------------------------------------------------------------------------
569 // Static factory method declared in raw_channel.h.
571 scoped_ptr
<RawChannel
> RawChannel::Create(
572 embedder::ScopedPlatformHandle handle
) {
573 return make_scoped_ptr(new RawChannelWin(handle
.Pass()));
576 } // namespace system