| OLD | NEW | 
|---|
| 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 "base/message_loop/message_pump_win.h" | 5 #include "base/message_loop/message_pump_win.h" | 
| 6 | 6 | 
| 7 #include <math.h> | 7 #include <math.h> | 
| 8 #include <stdint.h> | 8 #include <stdint.h> | 
| 9 | 9 | 
| 10 #include <limits> | 10 #include <limits> | 
| (...skipping 402 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 413   // windows code.   This ScheduleWork() may hurt performance a tiny bit when | 413   // windows code.   This ScheduleWork() may hurt performance a tiny bit when | 
| 414   // tasks appear very infrequently, but when the event queue is busy, the | 414   // tasks appear very infrequently, but when the event queue is busy, the | 
| 415   // kMsgHaveWork events get (percentage wise) rarer and rarer. | 415   // kMsgHaveWork events get (percentage wise) rarer and rarer. | 
| 416   ScheduleWork(); | 416   ScheduleWork(); | 
| 417   return ProcessMessageHelper(msg); | 417   return ProcessMessageHelper(msg); | 
| 418 } | 418 } | 
| 419 | 419 | 
| 420 //----------------------------------------------------------------------------- | 420 //----------------------------------------------------------------------------- | 
| 421 // MessagePumpForIO public: | 421 // MessagePumpForIO public: | 
| 422 | 422 | 
|  | 423 MessagePumpForIO::IOContext::IOContext() { | 
|  | 424   memset(&overlapped, 0, sizeof(overlapped)); | 
|  | 425 } | 
|  | 426 | 
| 423 MessagePumpForIO::MessagePumpForIO() { | 427 MessagePumpForIO::MessagePumpForIO() { | 
| 424   port_.Set(CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, NULL, 1)); | 428   port_.Set(CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, NULL, 1)); | 
| 425   DCHECK(port_.IsValid()); | 429   DCHECK(port_.IsValid()); | 
| 426 } | 430 } | 
| 427 | 431 | 
| 428 MessagePumpForIO::~MessagePumpForIO() { | 432 MessagePumpForIO::~MessagePumpForIO() = default; | 
| 429 } |  | 
| 430 | 433 | 
| 431 void MessagePumpForIO::ScheduleWork() { | 434 void MessagePumpForIO::ScheduleWork() { | 
| 432   if (InterlockedExchange(&have_work_, 1)) | 435   if (InterlockedExchange(&have_work_, 1)) | 
| 433     return;  // Someone else continued the pumping. | 436     return;  // Someone else continued the pumping. | 
| 434 | 437 | 
| 435   // Make sure the MessagePump does some work for us. | 438   // Make sure the MessagePump does some work for us. | 
| 436   BOOL ret = PostQueuedCompletionStatus(port_.Get(), 0, | 439   BOOL ret = PostQueuedCompletionStatus(port_.Get(), 0, | 
| 437                                         reinterpret_cast<ULONG_PTR>(this), | 440                                         reinterpret_cast<ULONG_PTR>(this), | 
| 438                                         reinterpret_cast<OVERLAPPED*>(this)); | 441                                         reinterpret_cast<OVERLAPPED*>(this)); | 
| 439   if (ret) | 442   if (ret) | 
| 440     return;  // Post worked perfectly. | 443     return;  // Post worked perfectly. | 
| 441 | 444 | 
| 442   // See comment in MessagePumpForUI::ScheduleWork() for this error recovery. | 445   // See comment in MessagePumpForUI::ScheduleWork() for this error recovery. | 
| 443   InterlockedExchange(&have_work_, 0);  // Clarify that we didn't succeed. | 446   InterlockedExchange(&have_work_, 0);  // Clarify that we didn't succeed. | 
| 444   UMA_HISTOGRAM_ENUMERATION("Chrome.MessageLoopProblem", COMPLETION_POST_ERROR, | 447   UMA_HISTOGRAM_ENUMERATION("Chrome.MessageLoopProblem", COMPLETION_POST_ERROR, | 
| 445                             MESSAGE_LOOP_PROBLEM_MAX); | 448                             MESSAGE_LOOP_PROBLEM_MAX); | 
| 446   state_->schedule_work_error_count++; | 449   state_->schedule_work_error_count++; | 
| 447   state_->last_schedule_work_error_time = Time::Now(); | 450   state_->last_schedule_work_error_time = Time::Now(); | 
| 448 } | 451 } | 
| 449 | 452 | 
| 450 void MessagePumpForIO::ScheduleDelayedWork(const TimeTicks& delayed_work_time) { | 453 void MessagePumpForIO::ScheduleDelayedWork(const TimeTicks& delayed_work_time) { | 
| 451   // We know that we can't be blocked right now since this method can only be | 454   // We know that we can't be blocked right now since this method can only be | 
| 452   // called on the same thread as Run, so we only need to update our record of | 455   // called on the same thread as Run, so we only need to update our record of | 
| 453   // how long to sleep when we do sleep. | 456   // how long to sleep when we do sleep. | 
| 454   delayed_work_time_ = delayed_work_time; | 457   delayed_work_time_ = delayed_work_time; | 
| 455 } | 458 } | 
| 456 | 459 | 
| 457 void MessagePumpForIO::RegisterIOHandler(HANDLE file_handle, | 460 void MessagePumpForIO::RegisterIOHandler(HANDLE file_handle, | 
| 458                                          IOHandler* handler) { | 461                                          IOHandler* handler) { | 
| 459   ULONG_PTR key = HandlerToKey(handler, true); | 462   HANDLE port = CreateIoCompletionPort(file_handle, port_.Get(), | 
| 460   HANDLE port = CreateIoCompletionPort(file_handle, port_.Get(), key, 1); | 463                                        reinterpret_cast<ULONG_PTR>(handler), 1); | 
| 461   DPCHECK(port); | 464   DPCHECK(port); | 
| 462 } | 465 } | 
| 463 | 466 | 
| 464 bool MessagePumpForIO::RegisterJobObject(HANDLE job_handle, | 467 bool MessagePumpForIO::RegisterJobObject(HANDLE job_handle, | 
| 465                                          IOHandler* handler) { | 468                                          IOHandler* handler) { | 
| 466   // Job object notifications use the OVERLAPPED pointer to carry the message |  | 
| 467   // data. Mark the completion key correspondingly, so we will not try to |  | 
| 468   // convert OVERLAPPED* to IOContext*. |  | 
| 469   ULONG_PTR key = HandlerToKey(handler, false); |  | 
| 470   JOBOBJECT_ASSOCIATE_COMPLETION_PORT info; | 469   JOBOBJECT_ASSOCIATE_COMPLETION_PORT info; | 
| 471   info.CompletionKey = reinterpret_cast<void*>(key); | 470   info.CompletionKey = handler; | 
| 472   info.CompletionPort = port_.Get(); | 471   info.CompletionPort = port_.Get(); | 
| 473   return SetInformationJobObject(job_handle, | 472   return SetInformationJobObject(job_handle, | 
| 474                                  JobObjectAssociateCompletionPortInformation, | 473                                  JobObjectAssociateCompletionPortInformation, | 
| 475                                  &info, | 474                                  &info, | 
| 476                                  sizeof(info)) != FALSE; | 475                                  sizeof(info)) != FALSE; | 
| 477 } | 476 } | 
| 478 | 477 | 
| 479 //----------------------------------------------------------------------------- | 478 //----------------------------------------------------------------------------- | 
| 480 // MessagePumpForIO private: | 479 // MessagePumpForIO private: | 
| 481 | 480 | 
| (...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 535   IOItem item; | 534   IOItem item; | 
| 536   if (completed_io_.empty() || !MatchCompletedIOItem(filter, &item)) { | 535   if (completed_io_.empty() || !MatchCompletedIOItem(filter, &item)) { | 
| 537     // We have to ask the system for another IO completion. | 536     // We have to ask the system for another IO completion. | 
| 538     if (!GetIOItem(timeout, &item)) | 537     if (!GetIOItem(timeout, &item)) | 
| 539       return false; | 538       return false; | 
| 540 | 539 | 
| 541     if (ProcessInternalIOItem(item)) | 540     if (ProcessInternalIOItem(item)) | 
| 542       return true; | 541       return true; | 
| 543   } | 542   } | 
| 544 | 543 | 
| 545   // If |item.has_valid_io_context| is false then |item.context| does not point | 544   if (filter && item.handler != filter) { | 
| 546   // to a context structure, and so should not be dereferenced, although it may | 545     // Save this item for later | 
| 547   // still hold valid non-pointer data. | 546     completed_io_.push_back(item); | 
| 548   if (!item.has_valid_io_context || item.context->handler) { |  | 
| 549     if (filter && item.handler != filter) { |  | 
| 550       // Save this item for later |  | 
| 551       completed_io_.push_back(item); |  | 
| 552     } else { |  | 
| 553       DCHECK(!item.has_valid_io_context || |  | 
| 554              (item.context->handler == item.handler)); |  | 
| 555       item.handler->OnIOCompleted(item.context, item.bytes_transfered, |  | 
| 556                                   item.error); |  | 
| 557     } |  | 
| 558   } else { | 547   } else { | 
| 559     // The handler must be gone by now, just cleanup the mess. | 548     item.handler->OnIOCompleted(item.context, item.bytes_transfered, | 
| 560     delete item.context; | 549                                 item.error); | 
| 561   } | 550   } | 
| 562   return true; | 551   return true; | 
| 563 } | 552 } | 
| 564 | 553 | 
| 565 // Asks the OS for another IO completion result. | 554 // Asks the OS for another IO completion result. | 
| 566 bool MessagePumpForIO::GetIOItem(DWORD timeout, IOItem* item) { | 555 bool MessagePumpForIO::GetIOItem(DWORD timeout, IOItem* item) { | 
| 567   memset(item, 0, sizeof(*item)); | 556   memset(item, 0, sizeof(*item)); | 
| 568   ULONG_PTR key = NULL; | 557   ULONG_PTR key = NULL; | 
| 569   OVERLAPPED* overlapped = NULL; | 558   OVERLAPPED* overlapped = NULL; | 
| 570   if (!GetQueuedCompletionStatus(port_.Get(), &item->bytes_transfered, &key, | 559   if (!GetQueuedCompletionStatus(port_.Get(), &item->bytes_transfered, &key, | 
| 571                                  &overlapped, timeout)) { | 560                                  &overlapped, timeout)) { | 
| 572     if (!overlapped) | 561     if (!overlapped) | 
| 573       return false;  // Nothing in the queue. | 562       return false;  // Nothing in the queue. | 
| 574     item->error = GetLastError(); | 563     item->error = GetLastError(); | 
| 575     item->bytes_transfered = 0; | 564     item->bytes_transfered = 0; | 
| 576   } | 565   } | 
| 577 | 566 | 
| 578   item->handler = KeyToHandler(key, &item->has_valid_io_context); | 567   item->handler = reinterpret_cast<IOHandler*>(key); | 
| 579   item->context = reinterpret_cast<IOContext*>(overlapped); | 568   item->context = reinterpret_cast<IOContext*>(overlapped); | 
| 580   return true; | 569   return true; | 
| 581 } | 570 } | 
| 582 | 571 | 
| 583 bool MessagePumpForIO::ProcessInternalIOItem(const IOItem& item) { | 572 bool MessagePumpForIO::ProcessInternalIOItem(const IOItem& item) { | 
| 584   if (reinterpret_cast<void*>(this) == reinterpret_cast<void*>(item.context) && | 573   if (reinterpret_cast<void*>(this) == reinterpret_cast<void*>(item.context) && | 
| 585       reinterpret_cast<void*>(this) == reinterpret_cast<void*>(item.handler)) { | 574       reinterpret_cast<void*>(this) == reinterpret_cast<void*>(item.handler)) { | 
| 586     // This is our internal completion. | 575     // This is our internal completion. | 
| 587     DCHECK(!item.bytes_transfered); | 576     DCHECK(!item.bytes_transfered); | 
| 588     InterlockedExchange(&have_work_, 0); | 577     InterlockedExchange(&have_work_, 0); | 
| 589     return true; | 578     return true; | 
| 590   } | 579   } | 
| 591   return false; | 580   return false; | 
| 592 } | 581 } | 
| 593 | 582 | 
| 594 // Returns a completion item that was previously received. | 583 // Returns a completion item that was previously received. | 
| 595 bool MessagePumpForIO::MatchCompletedIOItem(IOHandler* filter, IOItem* item) { | 584 bool MessagePumpForIO::MatchCompletedIOItem(IOHandler* filter, IOItem* item) { | 
| 596   DCHECK(!completed_io_.empty()); | 585   DCHECK(!completed_io_.empty()); | 
| 597   for (std::list<IOItem>::iterator it = completed_io_.begin(); | 586   for (std::list<IOItem>::iterator it = completed_io_.begin(); | 
| 598        it != completed_io_.end(); ++it) { | 587        it != completed_io_.end(); ++it) { | 
| 599     if (!filter || it->handler == filter) { | 588     if (!filter || it->handler == filter) { | 
| 600       *item = *it; | 589       *item = *it; | 
| 601       completed_io_.erase(it); | 590       completed_io_.erase(it); | 
| 602       return true; | 591       return true; | 
| 603     } | 592     } | 
| 604   } | 593   } | 
| 605   return false; | 594   return false; | 
| 606 } | 595 } | 
| 607 | 596 | 
| 608 // static |  | 
| 609 ULONG_PTR MessagePumpForIO::HandlerToKey(IOHandler* handler, |  | 
| 610                                          bool has_valid_io_context) { |  | 
| 611   ULONG_PTR key = reinterpret_cast<ULONG_PTR>(handler); |  | 
| 612 |  | 
| 613   // |IOHandler| is at least pointer-size aligned, so the lowest two bits are |  | 
| 614   // always cleared. We use the lowest bit to distinguish completion keys with |  | 
| 615   // and without the associated |IOContext|. |  | 
| 616   DCHECK_EQ(key & 1, 0u); |  | 
| 617 |  | 
| 618   // Mark the completion key as context-less. |  | 
| 619   if (!has_valid_io_context) |  | 
| 620     key = key | 1; |  | 
| 621   return key; |  | 
| 622 } |  | 
| 623 |  | 
| 624 // static |  | 
| 625 MessagePumpForIO::IOHandler* MessagePumpForIO::KeyToHandler( |  | 
| 626     ULONG_PTR key, |  | 
| 627     bool* has_valid_io_context) { |  | 
| 628   *has_valid_io_context = ((key & 1) == 0); |  | 
| 629   return reinterpret_cast<IOHandler*>(key & ~static_cast<ULONG_PTR>(1)); |  | 
| 630 } |  | 
| 631 |  | 
| 632 }  // namespace base | 597 }  // namespace base | 
| OLD | NEW | 
|---|