Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(488)

Side by Side Diff: util/mach/mach_message_server.cc

Issue 544393002: Add MachMessageServer and its test (Closed) Base URL: https://chromium.googlesource.com/crashpad/crashpad@master
Patch Set: Address review feedback Created 6 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2014 The Crashpad Authors. All rights reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (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
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "util/mach/mach_message_server.h"
16
17 #include <mach/mach_time.h>
18
19 #include <limits>
20
21 #include "base/mac/mach_logging.h"
22 #include "base/mac/scoped_mach_vm.h"
23
24 namespace crashpad {
25
26 namespace {
27
28 mach_timebase_info_data_t* TimebaseInternal() {
29 mach_timebase_info_data_t* timebase_info = new mach_timebase_info_data_t;
30 kern_return_t kr = mach_timebase_info(timebase_info);
31 MACH_CHECK(kr == KERN_SUCCESS, kr) << "mach_timebase_info";
32 return timebase_info;
33 }
34
35 mach_timebase_info_data_t* Timebase() {
36 static mach_timebase_info_data_t* timebase_info = TimebaseInternal();
37 return timebase_info;
38 }
39
40 uint64_t NanosecondTicks() {
41 uint64_t absolute_time = mach_absolute_time();
42 mach_timebase_info_data_t* timebase_info = Timebase();
43 return absolute_time * timebase_info->numer / timebase_info->denom;
44 }
45
46 const int kNanosecondsPerMillisecond = 1E6;
47
48 // TimerRunning determines whether |deadline| has passed. If |deadline| is in
49 // the future, |*remaining_ms| is set to the number of milliseconds remaining,
50 // which will always be a positive value, and this function returns true. If
51 // |deadline| is zero (indicating that no timer is in effect), |*remaining_ms|
52 // is set to zero and this function returns true. Otherwise, this function
53 // returns false. |deadline| is specified on the same time base as is returned
54 // by NanosecondTicks().
55 bool TimerRunning(uint64_t deadline, mach_msg_timeout_t* remaining_ms) {
56 if (!deadline) {
57 *remaining_ms = MACH_MSG_TIMEOUT_NONE;
58 return true;
59 }
60
61 uint64_t now = NanosecondTicks();
62
63 if (now >= deadline) {
64 return false;
65 }
66
67 uint64_t remaining = deadline - now;
68
69 // Round to the nearest millisecond, taking care not to overflow.
70 const int kHalfMillisecondInNanoseconds = kNanosecondsPerMillisecond / 2;
71 mach_msg_timeout_t remaining_mach;
72 if (remaining <=
73 std::numeric_limits<uint64_t>::max() - kHalfMillisecondInNanoseconds) {
74 remaining_mach = (remaining + kHalfMillisecondInNanoseconds) /
75 kNanosecondsPerMillisecond;
76 } else {
77 remaining_mach = remaining / kNanosecondsPerMillisecond;
78 }
79
80 if (remaining_mach == MACH_MSG_TIMEOUT_NONE) {
81 return false;
82 }
83
84 *remaining_ms = remaining_mach;
85 return true;
86 }
87
88 } // namespace
89
90 // This implementation is based on 10.9.4
91 // xnu-2422.110.17/libsyscall/mach/mach_msg.c mach_msg_server_once(), but
92 // adapted to local style using scopers, replacing the server callback function
93 // and |max_size| parameter with a C++ interface, and with the addition of the
94 // the |persistent| parameter allowing this function to serve as a stand-in for
95 // mach_msg_server(), the |nonblocking| parameter to control blocking directly,
96 // and the |timeout_ms| parameter allowing this function to not block
97 // indefinitely.
98 //
99 // static
100 mach_msg_return_t MachMessageServer::Run(Interface* interface,
101 mach_port_t receive_port,
102 mach_msg_options_t options,
103 Persistent persistent,
104 Nonblocking nonblocking,
105 mach_msg_timeout_t timeout_ms) {
106 options &= ~(MACH_RCV_MSG | MACH_SEND_MSG);
107
108 mach_msg_options_t timeout_options = MACH_RCV_TIMEOUT | MACH_SEND_TIMEOUT |
109 MACH_RCV_INTERRUPT | MACH_SEND_INTERRUPT;
110
111 uint64_t deadline;
112 if (nonblocking == kNonblocking) {
113 options |= timeout_options;
114 deadline = 0;
115 } else if (timeout_ms != MACH_MSG_TIMEOUT_NONE) {
116 options |= timeout_options;
117 deadline = NanosecondTicks() +
118 static_cast<uint64_t>(timeout_ms) * kNanosecondsPerMillisecond;
119 } else {
120 options &= ~timeout_options;
121 deadline = 0;
122 }
123
124 mach_msg_size_t trailer_alloc = REQUESTED_TRAILER_SIZE(options);
125 mach_msg_size_t max_request_size = interface->MachMessageServerRequestSize();
126 mach_msg_size_t request_alloc = round_page(max_request_size + trailer_alloc);
127 mach_msg_size_t request_size = (options & MACH_RCV_LARGE)
128 ? request_alloc
129 : max_request_size + trailer_alloc;
130
131 mach_msg_size_t max_reply_size = interface->MachMessageServerReplySize();
132 mach_msg_size_t reply_alloc = round_page(
133 (options & MACH_SEND_TRAILER) ? (max_reply_size + MAX_TRAILER_SIZE)
134 : max_reply_size);
135
136 mach_port_t self = mach_task_self();
137
138 kern_return_t kr;
139
140 do {
141 mach_msg_size_t this_request_alloc = request_alloc;
142 mach_msg_size_t this_request_size = request_size;
143
144 base::mac::ScopedMachVM request_scoper;
145 mach_msg_header_t* request_header = NULL;
146
147 while (!request_scoper.address()) {
148 vm_address_t request_addr;
149 kr = vm_allocate(self,
150 &request_addr,
151 this_request_alloc,
152 VM_FLAGS_ANYWHERE | VM_MAKE_TAG(VM_MEMORY_MACH_MSG));
153 if (kr != KERN_SUCCESS) {
154 return kr;
155 }
156 base::mac::ScopedMachVM trial_request_scoper(request_addr,
157 this_request_alloc);
158 request_header = reinterpret_cast<mach_msg_header_t*>(request_addr);
159
160 do {
161 // If |options| contains MACH_RCV_INTERRUPT, retry mach_msg() in a loop
162 // when it returns MACH_RCV_INTERRUPTED to recompute |remaining_ms|
163 // rather than allowing mach_msg() to retry using the original timeout
164 // value. See 10.9.4 xnu-2422.110.17/libsyscall/mach/mach_msg.c
165 // mach_msg().
166 mach_msg_timeout_t remaining_ms;
167 if (!TimerRunning(deadline, &remaining_ms)) {
168 return MACH_RCV_TIMED_OUT;
169 }
170
171 kr = mach_msg(request_header,
172 options | MACH_RCV_MSG,
173 0,
174 this_request_size,
175 receive_port,
176 remaining_ms,
177 MACH_PORT_NULL);
178 } while (kr == MACH_RCV_INTERRUPTED);
179
180 if (kr == MACH_MSG_SUCCESS) {
181 request_scoper.swap(trial_request_scoper);
182 } else if (kr == MACH_RCV_TOO_LARGE && options & MACH_RCV_LARGE) {
183 this_request_size =
184 round_page(request_header->msgh_size + trailer_alloc);
185 this_request_alloc = this_request_size;
186 } else {
187 return kr;
188 }
189 }
190
191 vm_address_t reply_addr;
192 kr = vm_allocate(self,
193 &reply_addr,
194 reply_alloc,
195 VM_FLAGS_ANYWHERE | VM_MAKE_TAG(VM_MEMORY_MACH_MSG));
196 if (kr != KERN_SUCCESS) {
197 return kr;
198 }
199
200 base::mac::ScopedMachVM reply_scoper(reply_addr, reply_alloc);
201
202 mach_msg_header_t* reply_header =
203 reinterpret_cast<mach_msg_header_t*>(reply_addr);
204 bool destroy_complex_request = false;
205 interface->MachMessageServerFunction(
206 request_header, reply_header, &destroy_complex_request);
207
208 if (!(reply_header->msgh_bits & MACH_MSGH_BITS_COMPLEX)) {
209 mig_reply_error_t* reply_mig =
210 reinterpret_cast<mig_reply_error_t*>(reply_header);
211 if (reply_mig->RetCode == MIG_NO_REPLY) {
212 reply_header->msgh_remote_port = MACH_PORT_NULL;
213 } else if (reply_mig->RetCode != KERN_SUCCESS &&
214 request_header->msgh_bits & MACH_MSGH_BITS_COMPLEX) {
215 destroy_complex_request = true;
216 }
217 }
218
219 if (destroy_complex_request &&
220 request_header->msgh_bits & MACH_MSGH_BITS_COMPLEX) {
221 request_header->msgh_remote_port = MACH_PORT_NULL;
222 mach_msg_destroy(request_header);
223 }
224
225 if (reply_header->msgh_remote_port != MACH_PORT_NULL) {
226 // If the reply port right is a send-once right, the send won’t block even
227 // if the remote side isn’t waiting for a message. No timeout is used,
228 // which keeps the communication on the kernel’s fast path. If the reply
229 // port right is a send right, MACH_SEND_TIMEOUT is used to avoid blocking
230 // indefinitely. This duplicates the logic in 10.9.4
231 // xnu-2422.110.17/libsyscall/mach/mach_msg.c mach_msg_server_once().
232 mach_msg_option_t send_options =
233 options | MACH_SEND_MSG |
234 (MACH_MSGH_BITS_REMOTE(reply_header->msgh_bits) ==
235 MACH_MSG_TYPE_MOVE_SEND_ONCE
236 ? 0
237 : MACH_SEND_TIMEOUT);
238
239 bool running;
240 do {
241 // If |options| contains MACH_SEND_INTERRUPT, retry mach_msg() in a loop
242 // when it returns MACH_SEND_INTERRUPTED to recompute |remaining_ms|
243 // rather than allowing mach_msg() to retry using the original timeout
244 // value. See 10.9.4 xnu-2422.110.17/libsyscall/mach/mach_msg.c
245 // mach_msg().
246 mach_msg_timeout_t remaining_ms;
247 running = TimerRunning(deadline, &remaining_ms);
248 if (!running) {
249 // Don’t return just yet. If the timer ran out in between the time the
250 // request was received and now, at least try to send the response.
251 remaining_ms = 0;
252 }
253
254 kr = mach_msg(reply_header,
255 send_options,
256 reply_header->msgh_size,
257 0,
258 MACH_PORT_NULL,
259 remaining_ms,
260 MACH_PORT_NULL);
261 } while (kr == MACH_SEND_INTERRUPTED);
262
263 if (kr != KERN_SUCCESS) {
264 if (kr == MACH_SEND_INVALID_DEST || kr == MACH_SEND_TIMED_OUT) {
265 mach_msg_destroy(reply_header);
266 }
267 return kr;
268 }
269
270 if (!running) {
271 // The reply message was sent successfuly, so act as though the deadline
272 // was reached before or during the subsequent receive operation when in
273 // persistent mode, and just return success when not in persistent mode.
274 return (persistent == kPersistent) ? MACH_RCV_TIMED_OUT : kr;
275 }
276 }
277 } while (persistent == kPersistent);
278
279 return kr;
280 }
281
282 } // namespace crashpad
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698