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

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

Issue 773943002: Add MachMessageWithDeadline() and supporting characters (Closed) Base URL: https://chromium.googlesource.com/crashpad/crashpad@mach_message_util_move
Patch Set: Created 6 years 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
« no previous file with comments | « util/mach/mach_message.h ('k') | util/mach/mach_message_test.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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.h" 15 #include "util/mach/mach_message.h"
16 16
17 #include <limits>
18
19 #include "base/basictypes.h"
20 #include "util/misc/clock.h"
21
17 namespace crashpad { 22 namespace crashpad {
18 23
24 namespace {
25
26 const int kNanosecondsPerMillisecond = 1E6;
27
28 // TimerRunning() determines whether |deadline| has passed. If |deadline| is
29 // kMachMessageWaitIndefinitely, |*timeout_options| is set to
30 // MACH_MSG_OPTION_NONE, |*remaining_ms| is set to MACH_MSG_TIMEOUT_NONE, and
31 // this function returns true. When used with mach_msg(), this will cause
32 // indefinite waiting. In any other case, |*timeout_options| is set to
33 // MACH_SEND_TIMEOUT | MACH_RCV_TIMEOUT, so mach_msg() will enforce a timeout
34 // specified by |*remaining_ms|. If |deadline| is in the future, |*remaining_ms|
35 // is set to the number of milliseconds remaining, which will always be a
36 // positive value, and this function returns true. If |deadline| is
37 // kMachMessageNonblocking (indicating that no timer is in effect),
38 // |*remaining_ms| is set to zero and this function returns true. Otherwise,
39 // this function sets |*remaining_ms| to zero and returns false.
40 bool TimerRunning(uint64_t deadline,
41 mach_msg_timeout_t* remaining_ms,
42 mach_msg_option_t* timeout_options) {
43 if (deadline == kMachMessageWaitIndefinitely) {
44 *remaining_ms = MACH_MSG_TIMEOUT_NONE;
45 *timeout_options = MACH_MSG_OPTION_NONE;
46 return true;
47 }
48
49 *timeout_options = MACH_SEND_TIMEOUT | MACH_RCV_TIMEOUT;
50
51 if (deadline == kMachMessageNonblocking) {
52 *remaining_ms = 0;
53 return true;
54 }
55
56 uint64_t now = ClockMonotonicNanoseconds();
57
58 if (now >= deadline) {
59 *remaining_ms = 0;
60 } else {
61 uint64_t remaining = deadline - now;
62
63 // Round to the nearest millisecond, taking care not to overflow.
64 const int kHalfMillisecondInNanoseconds = kNanosecondsPerMillisecond / 2;
65 if (remaining <=
66 std::numeric_limits<uint64_t>::max() - kHalfMillisecondInNanoseconds) {
67 *remaining_ms = (remaining + kHalfMillisecondInNanoseconds) /
68 kNanosecondsPerMillisecond;
69 } else {
70 *remaining_ms = remaining / kNanosecondsPerMillisecond;
71 }
72 }
73
74 return *remaining_ms != 0;
75 }
76
77 // This is an internal implementation detail of MachMessageWithDeadline(). It
78 // determines whether |deadline| has expired, and what timeout value and
79 // timeout-related options to pass to mach_msg() based on the value of
80 // |deadline|. mach_msg() will only be called if TimerRunning() returns true or
81 // if run_even_if_expired is true.
82 mach_msg_return_t MachMessageWithDeadlineInternal(mach_msg_header_t* message,
83 mach_msg_option_t options,
84 mach_msg_size_t receive_size,
85 mach_port_name_t receive_port,
86 MachMessageDeadline deadline,
87 mach_port_name_t notify_port,
88 bool run_even_if_expired) {
89 mach_msg_timeout_t remaining_ms;
90 mach_msg_option_t timeout_options;
91 if (!TimerRunning(deadline, &remaining_ms, &timeout_options) &&
92 !run_even_if_expired) {
93 // Simulate the timed-out return values from mach_msg().
94 if (options & MACH_SEND_MSG) {
95 return MACH_SEND_TIMED_OUT;
96 }
97 if (options & MACH_RCV_MSG) {
98 return MACH_RCV_TIMED_OUT;
99 }
100 return MACH_MSG_SUCCESS;
101 }
102
103 // Turn off the passed-in timeout bits and replace them with the ones from
104 // TimerRunning(). Get the send_size value from message->msgh_size if sending
105 // a message.
106 return mach_msg(
107 message,
108 (options & ~(MACH_SEND_TIMEOUT | MACH_RCV_TIMEOUT)) | timeout_options,
109 options & MACH_SEND_MSG ? message->msgh_size : 0,
110 receive_size,
111 receive_port,
112 remaining_ms,
113 notify_port);
114 }
115
116 } // namespace
117
118 MachMessageDeadline MachMessageDeadlineFromTimeout(
119 mach_msg_timeout_t timeout_ms) {
120 if (timeout_ms == 0) {
121 return kMachMessageNonblocking;
122 }
123
124 return ClockMonotonicNanoseconds() +
125 implicit_cast<uint64_t>(timeout_ms) * kNanosecondsPerMillisecond;
126 }
127
128 mach_msg_return_t MachMessageWithDeadline(mach_msg_header_t* message,
129 mach_msg_option_t options,
130 mach_msg_size_t receive_size,
131 mach_port_name_t receive_port,
132 MachMessageDeadline deadline,
133 mach_port_name_t notify_port,
134 bool run_even_if_expired) {
135 // mach_msg() actaully does return MACH_MSG_SUCCESS when not asked to send or
136 // receive anything. See 10.9.5 xnu-1504.15.3/osfmk/ipc/mach_msg.c
137 // mach_msg_overwrite_trap().
138 mach_msg_return_t mr = MACH_MSG_SUCCESS;
139
140 // Break up the send and receive into separate operations, so that the timeout
141 // can be recomputed from the deadline for each. Otherwise, the computed
142 // timeout will apply individually to the send and then to the receive, and
143 // the desired deadline could be exceeded.
144 //
145 // During sends, always set MACH_SEND_INTERRUPT, and during receives, always
146 // set MACH_RCV_INTERRUPT. If the caller didn’t specify these options, the
147 // calls will be retried with a recomputed deadline. If these bits weren’t
148 // set, the libsyscall wrapper (10.9.5
149 // xnu-2422.115.4/libsyscall/mach/mach_msg.c mach_msg() would restart
150 // interrupted calls with the original timeout value computed from the
151 // deadline, which would no longer correspond to the actual deadline. If the
152 // caller did specify these bits, don’t restart anything, because the caller
153 // wants to be notified of any interrupted calls.
154
155 if (options & MACH_SEND_MSG) {
156 do {
157 mr = MachMessageWithDeadlineInternal(
158 message,
159 (options & ~MACH_RCV_MSG) | MACH_SEND_INTERRUPT,
160 0,
161 MACH_PORT_NULL,
162 deadline,
163 notify_port,
164 run_even_if_expired);
165 } while (mr == MACH_SEND_INTERRUPTED && !(options & MACH_SEND_INTERRUPT));
166
167 if (mr != MACH_MSG_SUCCESS) {
168 return mr;
169 }
170 }
171
172 if (options & MACH_RCV_MSG) {
173 do {
174 mr = MachMessageWithDeadlineInternal(
175 message,
176 (options & ~MACH_SEND_MSG) | MACH_RCV_INTERRUPT,
177 receive_size,
178 receive_port,
179 deadline,
180 notify_port,
181 run_even_if_expired);
182 } while (mr == MACH_RCV_INTERRUPTED && !(options & MACH_RCV_INTERRUPT));
183 }
184
185 return mr;
186 }
187
19 void PrepareMIGReplyFromRequest(const mach_msg_header_t* in_header, 188 void PrepareMIGReplyFromRequest(const mach_msg_header_t* in_header,
20 mach_msg_header_t* out_header) { 189 mach_msg_header_t* out_header) {
21 out_header->msgh_bits = 190 out_header->msgh_bits =
22 MACH_MSGH_BITS(MACH_MSGH_BITS_REMOTE(in_header->msgh_bits), 0); 191 MACH_MSGH_BITS(MACH_MSGH_BITS_REMOTE(in_header->msgh_bits), 0);
23 out_header->msgh_remote_port = in_header->msgh_remote_port; 192 out_header->msgh_remote_port = in_header->msgh_remote_port;
24 out_header->msgh_size = sizeof(mig_reply_error_t); 193 out_header->msgh_size = sizeof(mig_reply_error_t);
25 out_header->msgh_local_port = MACH_PORT_NULL; 194 out_header->msgh_local_port = MACH_PORT_NULL;
26 out_header->msgh_id = in_header->msgh_id + 100; 195 out_header->msgh_id = in_header->msgh_id + 100;
27 reinterpret_cast<mig_reply_error_t*>(out_header)->NDR = NDR_record; 196 reinterpret_cast<mig_reply_error_t*>(out_header)->NDR = NDR_record;
28 197
29 // MIG-generated dispatch routines don’t do this, but they should. 198 // MIG-generated dispatch routines don’t do this, but they should.
30 out_header->msgh_reserved = 0; 199 out_header->msgh_reserved = 0;
31 } 200 }
32 201
33 void SetMIGReplyError(mach_msg_header_t* out_header, kern_return_t error) { 202 void SetMIGReplyError(mach_msg_header_t* out_header, kern_return_t error) {
34 reinterpret_cast<mig_reply_error_t*>(out_header)->RetCode = error; 203 reinterpret_cast<mig_reply_error_t*>(out_header)->RetCode = error;
35 } 204 }
36 205
37 const mach_msg_trailer_t* MachMessageTrailerFromHeader( 206 const mach_msg_trailer_t* MachMessageTrailerFromHeader(
38 const mach_msg_header_t* header) { 207 const mach_msg_header_t* header) {
39 vm_address_t header_address = reinterpret_cast<vm_address_t>(header); 208 vm_address_t header_address = reinterpret_cast<vm_address_t>(header);
40 vm_address_t trailer_address = header_address + round_msg(header->msgh_size); 209 vm_address_t trailer_address = header_address + round_msg(header->msgh_size);
41 return reinterpret_cast<const mach_msg_trailer_t*>(trailer_address); 210 return reinterpret_cast<const mach_msg_trailer_t*>(trailer_address);
42 } 211 }
43 212
44 } // namespace crashpad 213 } // namespace crashpad
OLDNEW
« no previous file with comments | « util/mach/mach_message.h ('k') | util/mach/mach_message_test.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698