Index: util/mach/mach_message_server.cc |
diff --git a/util/mach/mach_message_server.cc b/util/mach/mach_message_server.cc |
index c0f848daa9a627ee04d9fef9ea763c3052f7be2c..e14c963eada8ead451a7f3aa479f2f0371556fff 100644 |
--- a/util/mach/mach_message_server.cc |
+++ b/util/mach/mach_message_server.cc |
@@ -30,9 +30,9 @@ const int kNanosecondsPerMillisecond = 1E6; |
// the future, |*remaining_ms| is set to the number of milliseconds remaining, |
// which will always be a positive value, and this function returns true. If |
// |deadline| is zero (indicating that no timer is in effect), |*remaining_ms| |
-// is set to zero and this function returns true. Otherwise, this function |
-// returns false. |deadline| is specified on the same time base as is returned |
-// by ClockMonotonicNanoseconds(). |
+// is set to zero and this function returns true. Otherwise, this function sets |
+// |*remaining_ms| to zero and returns false. |deadline| is specified on the |
+// same time base as is returned by ClockMonotonicNanoseconds(). |
bool TimerRunning(uint64_t deadline, mach_msg_timeout_t* remaining_ms) { |
if (!deadline) { |
*remaining_ms = MACH_MSG_TIMEOUT_NONE; |
@@ -42,6 +42,7 @@ bool TimerRunning(uint64_t deadline, mach_msg_timeout_t* remaining_ms) { |
uint64_t now = ClockMonotonicNanoseconds(); |
if (now >= deadline) { |
+ *remaining_ms = 0; |
return false; |
} |
@@ -59,6 +60,7 @@ bool TimerRunning(uint64_t deadline, mach_msg_timeout_t* remaining_ms) { |
} |
if (remaining_mach == MACH_MSG_TIMEOUT_NONE) { |
+ *remaining_ms = 0; |
return false; |
} |
@@ -131,37 +133,50 @@ mach_msg_return_t MachMessageServer::Run(Interface* interface, |
// computation if it does, but that option is ineffective on OS X. |
mach_msg_size_t reply_alloc = round_page(max_reply_size); |
+ base::mac::ScopedMachVM request_scoper; |
+ base::mac::ScopedMachVM reply_scoper; |
+ bool received_any_request = false; |
+ |
kern_return_t kr; |
do { |
mach_msg_size_t this_request_alloc = request_alloc; |
mach_msg_size_t this_request_size = request_size; |
- base::mac::ScopedMachVM request_scoper; |
mach_msg_header_t* request_header = nullptr; |
- while (!request_scoper.address()) { |
- vm_address_t request_addr; |
- kr = vm_allocate(mach_task_self(), |
- &request_addr, |
- this_request_alloc, |
- VM_FLAGS_ANYWHERE | VM_MAKE_TAG(VM_MEMORY_MACH_MSG)); |
- if (kr != KERN_SUCCESS) { |
- return kr; |
+ do { |
+ // This test uses != instead of < so that a large reallocation to receive |
+ // a large message doesn’t cause permanent memory bloat. |
+ if (request_scoper.size() != this_request_alloc) { |
+ // reset() first, so that two allocations don’t exist simultaneously. |
+ request_scoper.reset(); |
+ |
+ vm_address_t request_addr; |
+ kr = vm_allocate(mach_task_self(), |
+ &request_addr, |
+ this_request_alloc, |
+ VM_FLAGS_ANYWHERE | VM_MAKE_TAG(VM_MEMORY_MACH_MSG)); |
+ if (kr != KERN_SUCCESS) { |
+ return kr; |
+ } |
+ |
+ request_scoper.reset(request_addr, this_request_alloc); |
} |
- base::mac::ScopedMachVM trial_request_scoper(request_addr, |
- this_request_alloc); |
- request_header = reinterpret_cast<mach_msg_header_t*>(request_addr); |
- bool run_mach_msg_receive = false; |
+ request_header = |
+ reinterpret_cast<mach_msg_header_t*>(request_scoper.address()); |
+ |
do { |
// If |options| contains MACH_RCV_INTERRUPT, retry mach_msg() in a loop |
// when it returns MACH_RCV_INTERRUPTED to recompute |remaining_ms| |
// rather than allowing mach_msg() to retry using the original timeout |
// value. See 10.9.4 xnu-2422.110.17/libsyscall/mach/mach_msg.c |
- // mach_msg(). |
+ // mach_msg(). Don’t return early here if nothing has ever been |
+ // received: this method should always attempt to dequeue at least one |
+ // message. |
mach_msg_timeout_t remaining_ms; |
- if (!TimerRunning(deadline, &remaining_ms)) { |
+ if (!TimerRunning(deadline, &remaining_ms) && received_any_request) { |
return MACH_RCV_TIMED_OUT; |
} |
@@ -175,37 +190,37 @@ mach_msg_return_t MachMessageServer::Run(Interface* interface, |
if (kr == MACH_RCV_TOO_LARGE && receive_large == kReceiveLargeIgnore) { |
MACH_LOG(WARNING, kr) << "mach_msg: ignoring large message"; |
- run_mach_msg_receive = true; |
- } else if (kr == MACH_RCV_INTERRUPTED) { |
- run_mach_msg_receive = true; |
} |
- } while (run_mach_msg_receive); |
+ } while ( |
+ (kr == MACH_RCV_TOO_LARGE && receive_large == kReceiveLargeIgnore) || |
+ kr == MACH_RCV_INTERRUPTED); |
- if (kr == MACH_MSG_SUCCESS) { |
- request_scoper.swap(trial_request_scoper); |
- } else if (kr == MACH_RCV_TOO_LARGE && |
- receive_large == kReceiveLargeResize) { |
+ if (kr == MACH_RCV_TOO_LARGE && receive_large == kReceiveLargeResize) { |
this_request_size = |
round_page(request_header->msgh_size + trailer_alloc); |
this_request_alloc = this_request_size; |
- } else { |
+ } else if (kr != MACH_MSG_SUCCESS) { |
return kr; |
} |
- } |
+ } while (kr != MACH_MSG_SUCCESS); |
- vm_address_t reply_addr; |
- kr = vm_allocate(mach_task_self(), |
- &reply_addr, |
- reply_alloc, |
- VM_FLAGS_ANYWHERE | VM_MAKE_TAG(VM_MEMORY_MACH_MSG)); |
- if (kr != KERN_SUCCESS) { |
- return kr; |
- } |
+ received_any_request = true; |
+ |
+ if (reply_scoper.size() != reply_alloc) { |
+ vm_address_t reply_addr; |
+ kr = vm_allocate(mach_task_self(), |
+ &reply_addr, |
+ reply_alloc, |
+ VM_FLAGS_ANYWHERE | VM_MAKE_TAG(VM_MEMORY_MACH_MSG)); |
+ if (kr != KERN_SUCCESS) { |
+ return kr; |
+ } |
- base::mac::ScopedMachVM reply_scoper(reply_addr, reply_alloc); |
+ reply_scoper.reset(reply_addr, reply_alloc); |
+ } |
mach_msg_header_t* reply_header = |
- reinterpret_cast<mach_msg_header_t*>(reply_addr); |
+ reinterpret_cast<mach_msg_header_t*>(reply_scoper.address()); |
bool destroy_complex_request = false; |
interface->MachMessageServerFunction( |
request_header, reply_header, &destroy_complex_request); |
@@ -256,11 +271,10 @@ mach_msg_return_t MachMessageServer::Run(Interface* interface, |
// mach_msg(). |
mach_msg_timeout_t remaining_ms; |
running = TimerRunning(deadline, &remaining_ms); |
- if (!running) { |
- // Don’t return just yet. If the timer ran out in between the time the |
- // request was received and now, at least try to send the response. |
- remaining_ms = 0; |
- } |
+ |
+ // Don’t return just yet even if |running| is false. If the timer ran |
+ // out in between the time the request was received and now, at least |
+ // try to send the response. |
kr = mach_msg(reply_header, |
send_options, |