| OLD | NEW |
| 1 // Copyright 2014 The Crashpad Authors. All rights reserved. | 1 // Copyright 2014 The Crashpad Authors. All rights reserved. |
| 2 // | 2 // |
| 3 // Licensed under the Apache License, Version 2.0 (the "License"); | 3 // Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 // you may not use this file except in compliance with the License. | 4 // you may not use this file except in compliance with the License. |
| 5 // You may obtain a copy of the License at | 5 // You may obtain a copy of the License at |
| 6 // | 6 // |
| 7 // http://www.apache.org/licenses/LICENSE-2.0 | 7 // http://www.apache.org/licenses/LICENSE-2.0 |
| 8 // | 8 // |
| 9 // Unless required by applicable law or agreed to in writing, software | 9 // Unless required by applicable law or agreed to in writing, software |
| 10 // distributed under the License is distributed on an "AS IS" BASIS, | 10 // distributed under the License is distributed on an "AS IS" BASIS, |
| 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 // See the License for the specific language governing permissions and | 12 // See the License for the specific language governing permissions and |
| 13 // limitations under the License. | 13 // limitations under the License. |
| 14 | 14 |
| 15 #include "util/mach/mach_message_server.h" | 15 #include "util/mach/mach_message_server.h" |
| 16 | 16 |
| 17 #include <limits> | 17 #include <limits> |
| 18 | 18 |
| 19 #include "base/logging.h" |
| 19 #include "base/mac/mach_logging.h" | 20 #include "base/mac/mach_logging.h" |
| 20 #include "base/mac/scoped_mach_vm.h" | 21 #include "base/mac/scoped_mach_vm.h" |
| 21 #include "util/misc/clock.h" | 22 #include "util/mach/mach_message.h" |
| 22 | 23 |
| 23 namespace crashpad { | 24 namespace crashpad { |
| 24 | 25 |
| 25 namespace { | 26 namespace { |
| 26 | 27 |
| 27 const int kNanosecondsPerMillisecond = 1E6; | 28 //! \brief Manages a dynamically-allocated buffer to be used for Mach messaging. |
| 29 class MachMessageBuffer { |
| 30 public: |
| 31 MachMessageBuffer() : vm_() {} |
| 28 | 32 |
| 29 // TimerRunning determines whether |deadline| has passed. If |deadline| is in | 33 ~MachMessageBuffer() {} |
| 30 // the future, |*remaining_ms| is set to the number of milliseconds remaining, | 34 |
| 31 // which will always be a positive value, and this function returns true. If | 35 //! \return A pointer to the buffer. |
| 32 // |deadline| is zero (indicating that no timer is in effect), |*remaining_ms| | 36 mach_msg_header_t* Header() const { |
| 33 // is set to zero and this function returns true. Otherwise, this function sets | 37 return reinterpret_cast<mach_msg_header_t*>(vm_.address()); |
| 34 // |*remaining_ms| to zero and returns false. |deadline| is specified on the | |
| 35 // same time base as is returned by ClockMonotonicNanoseconds(). | |
| 36 bool TimerRunning(uint64_t deadline, mach_msg_timeout_t* remaining_ms) { | |
| 37 if (!deadline) { | |
| 38 *remaining_ms = MACH_MSG_TIMEOUT_NONE; | |
| 39 return true; | |
| 40 } | 38 } |
| 41 | 39 |
| 42 uint64_t now = ClockMonotonicNanoseconds(); | 40 //! \brief Ensures that this object has a buffer of exactly \a size bytes |
| 41 //! available. |
| 42 //! |
| 43 //! If the existing buffer is a different size, it will be reallocated without |
| 44 //! copying any of the old buffer’s contents to the new buffer. The contents |
| 45 //! of the buffer are unspecified after this call, even if no reallocation is |
| 46 //! made. |
| 47 kern_return_t Reallocate(vm_size_t size) { |
| 48 // This test uses == instead of > so that a large reallocation to receive a |
| 49 // large message doesn’t cause permanent memory bloat for the duration of |
| 50 // a MachMessageServer::Run() loop. |
| 51 if (size == vm_.size()) { |
| 52 return KERN_SUCCESS; |
| 53 } |
| 43 | 54 |
| 44 if (now >= deadline) { | 55 // reset() first, so that two allocations don’t exist simultaneously. |
| 45 *remaining_ms = 0; | 56 vm_.reset(); |
| 46 return false; | 57 |
| 58 vm_address_t address; |
| 59 kern_return_t kr = |
| 60 vm_allocate(mach_task_self(), |
| 61 &address, |
| 62 size, |
| 63 VM_FLAGS_ANYWHERE | VM_MAKE_TAG(VM_MEMORY_MACH_MSG)); |
| 64 if (kr != KERN_SUCCESS) { |
| 65 return kr; |
| 66 } |
| 67 |
| 68 vm_.reset(address, size); |
| 69 return KERN_SUCCESS; |
| 47 } | 70 } |
| 48 | 71 |
| 49 uint64_t remaining = deadline - now; | 72 private: |
| 73 base::mac::ScopedMachVM vm_; |
| 50 | 74 |
| 51 // Round to the nearest millisecond, taking care not to overflow. | 75 DISALLOW_COPY_AND_ASSIGN(MachMessageBuffer); |
| 52 const int kHalfMillisecondInNanoseconds = kNanosecondsPerMillisecond / 2; | 76 }; |
| 53 mach_msg_timeout_t remaining_mach; | 77 |
| 54 if (remaining <= | 78 // Wraps MachMessageWithDeadline(), using a MachMessageBuffer argument which |
| 55 std::numeric_limits<uint64_t>::max() - kHalfMillisecondInNanoseconds) { | 79 // will be resized to |receive_size| (after being page-rounded). MACH_RCV_MSG |
| 56 remaining_mach = (remaining + kHalfMillisecondInNanoseconds) / | 80 // is always combined into |options|. |
| 57 kNanosecondsPerMillisecond; | 81 mach_msg_return_t MachMessageAllocateReceive(MachMessageBuffer* request, |
| 58 } else { | 82 mach_msg_option_t options, |
| 59 remaining_mach = remaining / kNanosecondsPerMillisecond; | 83 mach_msg_size_t receive_size, |
| 84 mach_port_name_t receive_port, |
| 85 MachMessageDeadline deadline, |
| 86 mach_port_name_t notify_port, |
| 87 bool run_even_if_expired) { |
| 88 mach_msg_size_t request_alloc = round_page(receive_size); |
| 89 kern_return_t kr = request->Reallocate(request_alloc); |
| 90 if (kr != KERN_SUCCESS) { |
| 91 return kr; |
| 60 } | 92 } |
| 61 | 93 |
| 62 if (remaining_mach == MACH_MSG_TIMEOUT_NONE) { | 94 return MachMessageWithDeadline(request->Header(), |
| 63 *remaining_ms = 0; | 95 options | MACH_RCV_MSG, |
| 64 return false; | 96 receive_size, |
| 65 } | 97 receive_port, |
| 66 | 98 deadline, |
| 67 *remaining_ms = remaining_mach; | 99 notify_port, |
| 68 return true; | 100 run_even_if_expired); |
| 69 } | 101 } |
| 70 | 102 |
| 71 } // namespace | 103 } // namespace |
| 72 | 104 |
| 73 // This implementation is based on 10.9.4 | 105 // This implementation is based on 10.9.4 |
| 74 // xnu-2422.110.17/libsyscall/mach/mach_msg.c mach_msg_server_once(), but | 106 // xnu-2422.110.17/libsyscall/mach/mach_msg.c mach_msg_server_once(), but |
| 75 // adapted to local style using scopers, replacing the server callback function | 107 // adapted to local style using scopers, replacing the server callback function |
| 76 // and |max_size| parameter with a C++ interface, and with the addition of the | 108 // and |max_size| parameter with a C++ interface, and with the addition of the |
| 77 // the |persistent| parameter allowing this function to serve as a stand-in for | 109 // the |persistent| parameter allowing this function to serve as a stand-in for |
| 78 // mach_msg_server(), the |nonblocking| parameter to control blocking directly, | 110 // mach_msg_server(), the |nonblocking| parameter to control blocking directly, |
| 79 // and the |timeout_ms| parameter allowing this function to not block | 111 // and the |timeout_ms| parameter allowing this function to not block |
| 80 // indefinitely. | 112 // indefinitely. |
| 81 // | 113 // |
| 82 // static | 114 // static |
| 83 mach_msg_return_t MachMessageServer::Run(Interface* interface, | 115 mach_msg_return_t MachMessageServer::Run(Interface* interface, |
| 84 mach_port_t receive_port, | 116 mach_port_t receive_port, |
| 85 mach_msg_options_t options, | 117 mach_msg_options_t options, |
| 86 Persistent persistent, | 118 Persistent persistent, |
| 87 Nonblocking nonblocking, | 119 Nonblocking nonblocking, |
| 88 ReceiveLarge receive_large, | 120 ReceiveLarge receive_large, |
| 89 mach_msg_timeout_t timeout_ms) { | 121 mach_msg_timeout_t timeout_ms) { |
| 90 options &= ~(MACH_RCV_MSG | MACH_SEND_MSG); | 122 options &= ~(MACH_RCV_MSG | MACH_SEND_MSG); |
| 91 | 123 |
| 92 mach_msg_options_t timeout_options = MACH_RCV_TIMEOUT | MACH_SEND_TIMEOUT | | 124 MachMessageDeadline deadline; |
| 93 MACH_RCV_INTERRUPT | MACH_SEND_INTERRUPT; | |
| 94 | |
| 95 uint64_t deadline; | |
| 96 if (nonblocking == kNonblocking) { | 125 if (nonblocking == kNonblocking) { |
| 97 options |= timeout_options; | 126 deadline = kMachMessageNonblocking; |
| 98 deadline = 0; | 127 } else if (timeout_ms == MACH_MSG_TIMEOUT_NONE) { |
| 99 } else if (timeout_ms != MACH_MSG_TIMEOUT_NONE) { | 128 deadline = kMachMessageWaitIndefinitely; |
| 100 options |= timeout_options; | |
| 101 deadline = ClockMonotonicNanoseconds() + | |
| 102 implicit_cast<uint64_t>(timeout_ms) * kNanosecondsPerMillisecond; | |
| 103 } else { | 129 } else { |
| 104 options &= ~timeout_options; | 130 deadline = MachMessageDeadlineFromTimeout(timeout_ms); |
| 105 deadline = 0; | |
| 106 } | 131 } |
| 107 | 132 |
| 108 if (receive_large == kReceiveLargeResize) { | 133 if (receive_large == kReceiveLargeResize) { |
| 109 options |= MACH_RCV_LARGE; | 134 options |= MACH_RCV_LARGE; |
| 110 } else { | 135 } else { |
| 111 options &= ~MACH_RCV_LARGE; | 136 options &= ~MACH_RCV_LARGE; |
| 112 } | 137 } |
| 113 | 138 |
| 114 mach_msg_size_t trailer_alloc = REQUESTED_TRAILER_SIZE(options); | 139 const mach_msg_size_t trailer_alloc = REQUESTED_TRAILER_SIZE(options); |
| 115 mach_msg_size_t expected_request_size = | 140 const mach_msg_size_t expected_receive_size = |
| 116 interface->MachMessageServerRequestSize(); | 141 round_msg(interface->MachMessageServerRequestSize()) + trailer_alloc; |
| 117 mach_msg_size_t request_alloc = | 142 const mach_msg_size_t request_size = (receive_large == kReceiveLargeResize) |
| 118 round_page(round_msg(expected_request_size) + trailer_alloc); | 143 ? round_page(expected_receive_size) |
| 119 mach_msg_size_t request_size = | 144 : expected_receive_size; |
| 120 (receive_large == kReceiveLargeResize) | |
| 121 ? request_alloc | |
| 122 : round_msg(expected_request_size) + trailer_alloc; | |
| 123 | |
| 124 mach_msg_size_t max_reply_size = interface->MachMessageServerReplySize(); | |
| 125 | 145 |
| 126 // mach_msg_server() and mach_msg_server_once() would consider whether | 146 // mach_msg_server() and mach_msg_server_once() would consider whether |
| 127 // |options| contains MACH_SEND_TRAILER and include MAX_TRAILER_SIZE in this | 147 // |options| contains MACH_SEND_TRAILER and include MAX_TRAILER_SIZE in this |
| 128 // computation if it does, but that option is ineffective on OS X. | 148 // computation if it does, but that option is ineffective on OS X. |
| 129 mach_msg_size_t reply_alloc = round_page(max_reply_size); | 149 const mach_msg_size_t reply_alloc = |
| 150 round_page(interface->MachMessageServerReplySize()); |
| 130 | 151 |
| 131 base::mac::ScopedMachVM request_scoper; | 152 MachMessageBuffer request; |
| 132 base::mac::ScopedMachVM reply_scoper; | 153 MachMessageBuffer reply; |
| 133 bool received_any_request = false; | 154 bool received_any_request = false; |
| 155 bool retry; |
| 134 | 156 |
| 135 kern_return_t kr; | 157 kern_return_t kr; |
| 136 | 158 |
| 137 do { | 159 do { |
| 138 mach_msg_size_t this_request_alloc = request_alloc; | 160 retry = false; |
| 139 mach_msg_size_t this_request_size = request_size; | |
| 140 | 161 |
| 141 mach_msg_header_t* request_header = nullptr; | 162 kr = MachMessageAllocateReceive(&request, |
| 163 options, |
| 164 request_size, |
| 165 receive_port, |
| 166 deadline, |
| 167 MACH_PORT_NULL, |
| 168 !received_any_request); |
| 169 if (kr == MACH_RCV_TOO_LARGE) { |
| 170 switch (receive_large) { |
| 171 case kReceiveLargeError: |
| 172 break; |
| 142 | 173 |
| 143 do { | 174 case kReceiveLargeIgnore: |
| 144 // This test uses != instead of < so that a large reallocation to receive | 175 // Try again, even in one-shot mode. The caller is expecting this |
| 145 // a large message doesn’t cause permanent memory bloat. | 176 // method to take action on the first message in the queue, and has |
| 146 if (request_scoper.size() != this_request_alloc) { | 177 // indicated that they want large messages to be ignored. The |
| 147 // reset() first, so that two allocations don’t exist simultaneously. | 178 // alternatives, which might involve returning MACH_MSG_SUCCESS, |
| 148 request_scoper.reset(); | 179 // MACH_RCV_TIMED_OUT, or MACH_RCV_TOO_LARGE to a caller that |
| 180 // specified one-shot behavior, all seem less correct than retrying. |
| 181 MACH_LOG(WARNING, kr) << "mach_msg: ignoring large message"; |
| 182 retry = true; |
| 183 continue; |
| 149 | 184 |
| 150 vm_address_t request_addr; | 185 case kReceiveLargeResize: { |
| 151 kr = vm_allocate(mach_task_self(), | 186 mach_msg_size_t this_request_size = round_page( |
| 152 &request_addr, | 187 round_msg(request.Header()->msgh_size) + trailer_alloc); |
| 153 this_request_alloc, | 188 |
| 154 VM_FLAGS_ANYWHERE | VM_MAKE_TAG(VM_MEMORY_MACH_MSG)); | 189 kr = MachMessageAllocateReceive(&request, |
| 155 if (kr != KERN_SUCCESS) { | 190 options & ~MACH_RCV_LARGE, |
| 156 return kr; | 191 this_request_size, |
| 192 receive_port, |
| 193 deadline, |
| 194 MACH_PORT_NULL, |
| 195 !received_any_request); |
| 196 |
| 197 break; |
| 157 } | 198 } |
| 199 } |
| 200 } |
| 158 | 201 |
| 159 request_scoper.reset(request_addr, this_request_alloc); | 202 if (kr != MACH_MSG_SUCCESS) { |
| 160 } | 203 return kr; |
| 161 | 204 } |
| 162 request_header = | |
| 163 reinterpret_cast<mach_msg_header_t*>(request_scoper.address()); | |
| 164 | |
| 165 do { | |
| 166 // If |options| contains MACH_RCV_INTERRUPT, retry mach_msg() in a loop | |
| 167 // when it returns MACH_RCV_INTERRUPTED to recompute |remaining_ms| | |
| 168 // rather than allowing mach_msg() to retry using the original timeout | |
| 169 // value. See 10.9.4 xnu-2422.110.17/libsyscall/mach/mach_msg.c | |
| 170 // mach_msg(). Don’t return early here if nothing has ever been | |
| 171 // received: this method should always attempt to dequeue at least one | |
| 172 // message. | |
| 173 mach_msg_timeout_t remaining_ms; | |
| 174 if (!TimerRunning(deadline, &remaining_ms) && received_any_request) { | |
| 175 return MACH_RCV_TIMED_OUT; | |
| 176 } | |
| 177 | |
| 178 kr = mach_msg(request_header, | |
| 179 options | MACH_RCV_MSG, | |
| 180 0, | |
| 181 this_request_size, | |
| 182 receive_port, | |
| 183 remaining_ms, | |
| 184 MACH_PORT_NULL); | |
| 185 | |
| 186 if (kr == MACH_RCV_TOO_LARGE && receive_large == kReceiveLargeIgnore) { | |
| 187 MACH_LOG(WARNING, kr) << "mach_msg: ignoring large message"; | |
| 188 } | |
| 189 } while ( | |
| 190 (kr == MACH_RCV_TOO_LARGE && receive_large == kReceiveLargeIgnore) || | |
| 191 kr == MACH_RCV_INTERRUPTED); | |
| 192 | |
| 193 if (kr == MACH_RCV_TOO_LARGE && receive_large == kReceiveLargeResize) { | |
| 194 this_request_size = | |
| 195 round_page(round_msg(request_header->msgh_size) + trailer_alloc); | |
| 196 this_request_alloc = this_request_size; | |
| 197 } else if (kr != MACH_MSG_SUCCESS) { | |
| 198 return kr; | |
| 199 } | |
| 200 } while (kr != MACH_MSG_SUCCESS); | |
| 201 | 205 |
| 202 received_any_request = true; | 206 received_any_request = true; |
| 203 | 207 |
| 204 if (reply_scoper.size() != reply_alloc) { | 208 kr = reply.Reallocate(reply_alloc); |
| 205 vm_address_t reply_addr; | 209 if (kr != KERN_SUCCESS) { |
| 206 kr = vm_allocate(mach_task_self(), | 210 return kr; |
| 207 &reply_addr, | |
| 208 reply_alloc, | |
| 209 VM_FLAGS_ANYWHERE | VM_MAKE_TAG(VM_MEMORY_MACH_MSG)); | |
| 210 if (kr != KERN_SUCCESS) { | |
| 211 return kr; | |
| 212 } | |
| 213 | |
| 214 reply_scoper.reset(reply_addr, reply_alloc); | |
| 215 } | 211 } |
| 216 | 212 |
| 217 mach_msg_header_t* reply_header = | 213 mach_msg_header_t* request_header = request.Header(); |
| 218 reinterpret_cast<mach_msg_header_t*>(reply_scoper.address()); | 214 mach_msg_header_t* reply_header = reply.Header(); |
| 219 bool destroy_complex_request = false; | 215 bool destroy_complex_request = false; |
| 220 interface->MachMessageServerFunction( | 216 interface->MachMessageServerFunction( |
| 221 request_header, reply_header, &destroy_complex_request); | 217 request_header, reply_header, &destroy_complex_request); |
| 222 | 218 |
| 223 if (!(reply_header->msgh_bits & MACH_MSGH_BITS_COMPLEX)) { | 219 if (!(reply_header->msgh_bits & MACH_MSGH_BITS_COMPLEX)) { |
| 224 // This only works if the reply message is not complex, because otherwise, | 220 // This only works if the reply message is not complex, because otherwise, |
| 225 // the location of the RetCode field is not known. It should be possible | 221 // the location of the RetCode field is not known. It should be possible |
| 226 // to locate the RetCode field by looking beyond the descriptors in a | 222 // to locate the RetCode field by looking beyond the descriptors in a |
| 227 // complex reply message, but this is not currently done. This behavior | 223 // complex reply message, but this is not currently done. This behavior |
| 228 // has not proven itself necessary in practice, and it’s not done by | 224 // has not proven itself necessary in practice, and it’s not done by |
| 229 // mach_msg_server() or mach_msg_server_once() either. | 225 // mach_msg_server() or mach_msg_server_once() either. |
| 230 mig_reply_error_t* reply_mig = | 226 mig_reply_error_t* reply_mig = |
| 231 reinterpret_cast<mig_reply_error_t*>(reply_header); | 227 reinterpret_cast<mig_reply_error_t*>(reply_header); |
| 232 if (reply_mig->RetCode == MIG_NO_REPLY) { | 228 if (reply_mig->RetCode == MIG_NO_REPLY) { |
| 233 reply_header->msgh_remote_port = MACH_PORT_NULL; | 229 reply_header->msgh_remote_port = MACH_PORT_NULL; |
| 234 } else if (reply_mig->RetCode != KERN_SUCCESS && | 230 } else if (reply_mig->RetCode != KERN_SUCCESS && |
| 235 request_header->msgh_bits & MACH_MSGH_BITS_COMPLEX) { | 231 request_header->msgh_bits & MACH_MSGH_BITS_COMPLEX) { |
| 236 destroy_complex_request = true; | 232 destroy_complex_request = true; |
| 237 } | 233 } |
| 238 } | 234 } |
| 239 | 235 |
| 240 if (destroy_complex_request && | 236 if (destroy_complex_request && |
| 241 request_header->msgh_bits & MACH_MSGH_BITS_COMPLEX) { | 237 request_header->msgh_bits & MACH_MSGH_BITS_COMPLEX) { |
| 242 request_header->msgh_remote_port = MACH_PORT_NULL; | 238 request_header->msgh_remote_port = MACH_PORT_NULL; |
| 243 mach_msg_destroy(request_header); | 239 mach_msg_destroy(request_header); |
| 244 } | 240 } |
| 245 | 241 |
| 246 if (reply_header->msgh_remote_port != MACH_PORT_NULL) { | 242 if (reply_header->msgh_remote_port != MACH_PORT_NULL) { |
| 247 // If the reply port right is a send-once right, the send won’t block even | 243 // Avoid blocking indefinitely. This duplicates the logic in 10.9.5 |
| 248 // if the remote side isn’t waiting for a message. No timeout is used, | 244 // xnu-2422.115.4/libsyscall/mach/mach_msg.c mach_msg_server_once(), |
| 249 // which keeps the communication on the kernel’s fast path. If the reply | 245 // although the special provision for sending to a send-once right is not |
| 250 // port right is a send right, MACH_SEND_TIMEOUT is used to avoid blocking | 246 // made, because kernel keeps sends to a send-once right on the fast path |
| 251 // indefinitely. This duplicates the logic in 10.9.4 | 247 // without considering the user-specified timeout. See 10.9.5 |
| 252 // xnu-2422.110.17/libsyscall/mach/mach_msg.c mach_msg_server_once(). | 248 // xnu-2422.115.4/osfmk/ipc/ipc_mqueue.c ipc_mqueue_send(). |
| 253 mach_msg_option_t send_options = | 249 const MachMessageDeadline send_deadline = |
| 254 options | MACH_SEND_MSG | | 250 deadline == kMachMessageWaitIndefinitely ? kMachMessageNonblocking |
| 255 (MACH_MSGH_BITS_REMOTE(reply_header->msgh_bits) == | 251 : deadline; |
| 256 MACH_MSG_TYPE_MOVE_SEND_ONCE | |
| 257 ? 0 | |
| 258 : MACH_SEND_TIMEOUT); | |
| 259 | 252 |
| 260 bool running; | 253 kr = MachMessageWithDeadline(reply_header, |
| 261 do { | 254 options | MACH_SEND_MSG, |
| 262 // If |options| contains MACH_SEND_INTERRUPT, retry mach_msg() in a loop | 255 0, |
| 263 // when it returns MACH_SEND_INTERRUPTED to recompute |remaining_ms| | 256 MACH_PORT_NULL, |
| 264 // rather than allowing mach_msg() to retry using the original timeout | 257 send_deadline, |
| 265 // value. See 10.9.4 xnu-2422.110.17/libsyscall/mach/mach_msg.c | 258 MACH_PORT_NULL, |
| 266 // mach_msg(). | 259 true); |
| 267 mach_msg_timeout_t remaining_ms; | |
| 268 running = TimerRunning(deadline, &remaining_ms); | |
| 269 | 260 |
| 270 // Don’t return just yet even if |running| is false. If the timer ran | 261 if (kr != MACH_MSG_SUCCESS) { |
| 271 // out in between the time the request was received and now, at least | 262 if (kr == MACH_SEND_INVALID_DEST || |
| 272 // try to send the response. | 263 kr == MACH_SEND_TIMED_OUT || |
| 273 | 264 kr == MACH_SEND_INTERRUPTED) { |
| 274 kr = mach_msg(reply_header, | |
| 275 send_options, | |
| 276 reply_header->msgh_size, | |
| 277 0, | |
| 278 MACH_PORT_NULL, | |
| 279 remaining_ms, | |
| 280 MACH_PORT_NULL); | |
| 281 } while (kr == MACH_SEND_INTERRUPTED); | |
| 282 | |
| 283 if (kr != KERN_SUCCESS) { | |
| 284 if (kr == MACH_SEND_INVALID_DEST || kr == MACH_SEND_TIMED_OUT) { | |
| 285 mach_msg_destroy(reply_header); | 265 mach_msg_destroy(reply_header); |
| 286 } | 266 } |
| 287 return kr; | 267 return kr; |
| 288 } | 268 } |
| 289 | |
| 290 if (!running) { | |
| 291 // The reply message was sent successfuly, so act as though the deadline | |
| 292 // was reached before or during the subsequent receive operation when in | |
| 293 // persistent mode, and just return success when not in persistent mode. | |
| 294 return (persistent == kPersistent) ? MACH_RCV_TIMED_OUT : kr; | |
| 295 } | |
| 296 } | 269 } |
| 297 } while (persistent == kPersistent); | 270 } while (persistent == kPersistent || retry); |
| 298 | 271 |
| 299 return kr; | 272 return kr; |
| 300 } | 273 } |
| 301 | 274 |
| 302 } // namespace crashpad | 275 } // namespace crashpad |
| OLD | NEW |