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

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: 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| parameter allowing this function to not block indefinitely.
97 mach_msg_return_t MachMessageServer(MachMessageServerInterface* interface,
98 mach_port_t receive_port,
99 mach_msg_options_t options,
100 bool persistent,
101 bool nonblocking,
102 mach_msg_timeout_t timeout) {
103 options &= ~(MACH_RCV_MSG | MACH_SEND_MSG);
104
105 mach_msg_options_t timeout_options = MACH_RCV_TIMEOUT | MACH_SEND_TIMEOUT |
106 MACH_RCV_INTERRUPT | MACH_SEND_INTERRUPT;
107
108 uint64_t deadline;
109 if (nonblocking) {
110 options |= timeout_options;
111 deadline = 0;
112 } else if (timeout != MACH_MSG_TIMEOUT_NONE) {
113 options |= timeout_options;
114 deadline = NanosecondTicks() +
115 static_cast<uint64_t>(timeout) * kNanosecondsPerMillisecond;
116 } else {
117 options &= ~timeout_options;
118 deadline = 0;
119 }
120
121 mach_msg_size_t trailer_alloc = REQUESTED_TRAILER_SIZE(options);
122 mach_msg_size_t max_request_size = interface->MachMessageServerRequestSize();
123 mach_msg_size_t request_alloc = round_page(max_request_size + trailer_alloc);
124 mach_msg_size_t request_size = (options & MACH_RCV_LARGE)
125 ? request_alloc
126 : max_request_size + trailer_alloc;
127
128 mach_msg_size_t max_reply_size = interface->MachMessageServerReplySize();
129 mach_msg_size_t reply_alloc = round_page(
130 (options & MACH_SEND_TRAILER) ? (max_reply_size + MAX_TRAILER_SIZE)
131 : max_reply_size);
132
133 mach_port_t self = mach_task_self();
134
135 kern_return_t kr;
136
137 do {
138 mach_msg_size_t this_request_alloc = request_alloc;
139 mach_msg_size_t this_request_size = request_size;
140
141 base::mac::ScopedMachVM request_scoper;
142 mach_msg_header_t* request_header = NULL;
143
144 while (!request_scoper.address()) {
145 vm_address_t request_addr;
146 kr = vm_allocate(self,
147 &request_addr,
148 this_request_alloc,
149 VM_FLAGS_ANYWHERE | VM_MAKE_TAG(VM_MEMORY_MACH_MSG));
150 if (kr != KERN_SUCCESS) {
151 return kr;
152 }
153 base::mac::ScopedMachVM trial_request_scoper(request_addr,
154 this_request_alloc);
155 request_header = reinterpret_cast<mach_msg_header_t*>(request_addr);
156
157 do {
158 // If |options| contains MACH_RCV_INTERRUPT, retry mach_msg in a loop
159 // when it returns MACH_RCV_INTERRUPTED to recompute |remaining_ms|
160 // rather than allowing mach_msg to retry using the original timeout
161 // value. See 10.9.4 xnu-2422.110.17/libsyscall/mach/mach_msg.c
162 // mach_msg().
163 mach_msg_timeout_t remaining_ms;
164 if (!TimerRunning(deadline, &remaining_ms)) {
165 return MACH_RCV_TIMED_OUT;
166 }
167
168 kr = mach_msg(request_header,
169 options | MACH_RCV_MSG,
170 0,
171 this_request_size,
172 receive_port,
173 remaining_ms,
174 MACH_PORT_NULL);
175 } while (kr == MACH_RCV_INTERRUPTED);
176
177 if (kr == MACH_MSG_SUCCESS) {
178 request_scoper.swap(trial_request_scoper);
179 } else if (kr == MACH_RCV_TOO_LARGE && options & MACH_RCV_LARGE) {
180 this_request_size =
181 round_page(request_header->msgh_size + trailer_alloc);
182 this_request_alloc = this_request_size;
183 } else {
184 return kr;
185 }
186 }
187
188 vm_address_t reply_addr;
189 kr = vm_allocate(self,
190 &reply_addr,
191 reply_alloc,
192 VM_FLAGS_ANYWHERE | VM_MAKE_TAG(VM_MEMORY_MACH_MSG));
193 if (kr != KERN_SUCCESS) {
194 return kr;
195 }
196
197 base::mac::ScopedMachVM reply_scoper(reply_addr, reply_alloc);
198
199 mach_msg_header_t* reply_header =
200 reinterpret_cast<mach_msg_header_t*>(reply_addr);
201 bool destroy_complex_request = false;
202 interface->MachMessageServerFunction(
203 request_header, reply_header, &destroy_complex_request);
204
205 if (!(reply_header->msgh_bits & MACH_MSGH_BITS_COMPLEX)) {
206 mig_reply_error_t* reply_mig =
207 reinterpret_cast<mig_reply_error_t*>(reply_header);
208 if (reply_mig->RetCode == MIG_NO_REPLY) {
209 reply_header->msgh_remote_port = MACH_PORT_NULL;
210 } else if (reply_mig->RetCode != KERN_SUCCESS &&
211 request_header->msgh_bits & MACH_MSGH_BITS_COMPLEX) {
212 destroy_complex_request = true;
213 }
214 }
215
216 if (destroy_complex_request &&
217 request_header->msgh_bits & MACH_MSGH_BITS_COMPLEX) {
218 request_header->msgh_remote_port = MACH_PORT_NULL;
219 mach_msg_destroy(request_header);
220 }
221
222 if (reply_header->msgh_remote_port != MACH_PORT_NULL) {
223 mach_msg_option_t send_options =
224 options | MACH_SEND_MSG |
225 (MACH_MSGH_BITS_REMOTE(reply_header->msgh_bits) ==
226 MACH_MSG_TYPE_MOVE_SEND_ONCE
227 ? 0
228 : MACH_SEND_TIMEOUT);
Robert Sesek 2014/09/08 16:28:47 Why is SEND_ONCE a gate for SEND_TIMEOUT?
229
230 bool running;
231 do {
232 // If |options| contains MACH_SEND_INTERRUPT, retry mach_msg in a loop
233 // when it returns MACH_SEND_INTERRUPTED to recompute |remaining_ms|
234 // rather than allowing mach_msg to retry using the original timeout
235 // value. See 10.9.4 xnu-2422.110.17/libsyscall/mach/mach_msg.c
236 // mach_msg().
237 mach_msg_timeout_t remaining_ms;
238 running = TimerRunning(deadline, &remaining_ms);
239 if (!running) {
240 // Don’t return just yet. If the timer ran out in between the time the
241 // request was received and now, at least try to send the response.
242 remaining_ms = 0;
243 }
244
245 kr = mach_msg(reply_header,
246 send_options,
247 reply_header->msgh_size,
248 0,
249 MACH_PORT_NULL,
250 remaining_ms,
251 MACH_PORT_NULL);
252 } while (kr == MACH_SEND_INTERRUPTED);
253
254 if (kr != KERN_SUCCESS) {
255 if (kr == MACH_SEND_INVALID_DEST || kr == MACH_SEND_TIMED_OUT) {
256 mach_msg_destroy(reply_header);
257 }
258 return kr;
259 }
260
261 if (!running) {
262 // The reply message was sent successfuly, so act as though the deadline
263 // was reached before or during the subsequent receive operation when in
264 // persistent mode, and just return success when not in persistent mode.
265 return persistent ? MACH_RCV_TIMED_OUT : kr;
266 }
267 }
268 } while (persistent);
269
270 return kr;
271 }
272
273 } // namespace crashpad
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698