1*6777b538SAndroid Build Coastguard Worker // Copyright 2017 The Chromium Authors
2*6777b538SAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be
3*6777b538SAndroid Build Coastguard Worker // found in the LICENSE file.
4*6777b538SAndroid Build Coastguard Worker
5*6777b538SAndroid Build Coastguard Worker #include "base/synchronization/waitable_event.h"
6*6777b538SAndroid Build Coastguard Worker
7*6777b538SAndroid Build Coastguard Worker #include <mach/mach.h>
8*6777b538SAndroid Build Coastguard Worker #include <sys/event.h>
9*6777b538SAndroid Build Coastguard Worker
10*6777b538SAndroid Build Coastguard Worker #include <limits>
11*6777b538SAndroid Build Coastguard Worker #include <memory>
12*6777b538SAndroid Build Coastguard Worker
13*6777b538SAndroid Build Coastguard Worker #include "base/apple/mach_logging.h"
14*6777b538SAndroid Build Coastguard Worker #include "base/files/scoped_file.h"
15*6777b538SAndroid Build Coastguard Worker #include "base/notreached.h"
16*6777b538SAndroid Build Coastguard Worker #include "base/posix/eintr_wrapper.h"
17*6777b538SAndroid Build Coastguard Worker #include "base/threading/scoped_blocking_call.h"
18*6777b538SAndroid Build Coastguard Worker #include "base/time/time.h"
19*6777b538SAndroid Build Coastguard Worker #include "base/time/time_override.h"
20*6777b538SAndroid Build Coastguard Worker #include "build/build_config.h"
21*6777b538SAndroid Build Coastguard Worker
22*6777b538SAndroid Build Coastguard Worker namespace base {
23*6777b538SAndroid Build Coastguard Worker
WaitableEvent(ResetPolicy reset_policy,InitialState initial_state)24*6777b538SAndroid Build Coastguard Worker WaitableEvent::WaitableEvent(ResetPolicy reset_policy,
25*6777b538SAndroid Build Coastguard Worker InitialState initial_state)
26*6777b538SAndroid Build Coastguard Worker : policy_(reset_policy) {
27*6777b538SAndroid Build Coastguard Worker mach_port_options_t options{};
28*6777b538SAndroid Build Coastguard Worker options.flags = MPO_INSERT_SEND_RIGHT;
29*6777b538SAndroid Build Coastguard Worker options.mpl.mpl_qlimit = 1;
30*6777b538SAndroid Build Coastguard Worker
31*6777b538SAndroid Build Coastguard Worker mach_port_t name;
32*6777b538SAndroid Build Coastguard Worker kern_return_t kr =
33*6777b538SAndroid Build Coastguard Worker mach_port_construct(mach_task_self(), &options, /*context=*/0, &name);
34*6777b538SAndroid Build Coastguard Worker MACH_CHECK(kr == KERN_SUCCESS, kr) << "mach_port_construct";
35*6777b538SAndroid Build Coastguard Worker
36*6777b538SAndroid Build Coastguard Worker receive_right_ = new ReceiveRight(name);
37*6777b538SAndroid Build Coastguard Worker send_right_.reset(name);
38*6777b538SAndroid Build Coastguard Worker
39*6777b538SAndroid Build Coastguard Worker if (initial_state == InitialState::SIGNALED) {
40*6777b538SAndroid Build Coastguard Worker Signal();
41*6777b538SAndroid Build Coastguard Worker }
42*6777b538SAndroid Build Coastguard Worker }
43*6777b538SAndroid Build Coastguard Worker
Reset()44*6777b538SAndroid Build Coastguard Worker void WaitableEvent::Reset() {
45*6777b538SAndroid Build Coastguard Worker PeekPort(receive_right_->Name(), true);
46*6777b538SAndroid Build Coastguard Worker }
47*6777b538SAndroid Build Coastguard Worker
SignalImpl()48*6777b538SAndroid Build Coastguard Worker void WaitableEvent::SignalImpl() {
49*6777b538SAndroid Build Coastguard Worker mach_msg_empty_send_t msg{};
50*6777b538SAndroid Build Coastguard Worker msg.header.msgh_bits = MACH_MSGH_BITS_REMOTE(MACH_MSG_TYPE_COPY_SEND);
51*6777b538SAndroid Build Coastguard Worker msg.header.msgh_size = sizeof(&msg);
52*6777b538SAndroid Build Coastguard Worker msg.header.msgh_remote_port = send_right_.get();
53*6777b538SAndroid Build Coastguard Worker // If the event is already signaled, this will time out because the queue
54*6777b538SAndroid Build Coastguard Worker // has a length of one.
55*6777b538SAndroid Build Coastguard Worker kern_return_t kr =
56*6777b538SAndroid Build Coastguard Worker mach_msg(&msg.header, MACH_SEND_MSG | MACH_SEND_TIMEOUT, sizeof(msg),
57*6777b538SAndroid Build Coastguard Worker /*rcv_size=*/0, /*rcv_name=*/MACH_PORT_NULL, /*timeout=*/0,
58*6777b538SAndroid Build Coastguard Worker /*notify=*/MACH_PORT_NULL);
59*6777b538SAndroid Build Coastguard Worker MACH_CHECK(kr == KERN_SUCCESS || kr == MACH_SEND_TIMED_OUT, kr) << "mach_msg";
60*6777b538SAndroid Build Coastguard Worker }
61*6777b538SAndroid Build Coastguard Worker
IsSignaled()62*6777b538SAndroid Build Coastguard Worker bool WaitableEvent::IsSignaled() {
63*6777b538SAndroid Build Coastguard Worker return PeekPort(receive_right_->Name(), policy_ == ResetPolicy::AUTOMATIC);
64*6777b538SAndroid Build Coastguard Worker }
65*6777b538SAndroid Build Coastguard Worker
TimedWaitImpl(TimeDelta wait_delta)66*6777b538SAndroid Build Coastguard Worker bool WaitableEvent::TimedWaitImpl(TimeDelta wait_delta) {
67*6777b538SAndroid Build Coastguard Worker mach_msg_empty_rcv_t msg{};
68*6777b538SAndroid Build Coastguard Worker msg.header.msgh_local_port = receive_right_->Name();
69*6777b538SAndroid Build Coastguard Worker
70*6777b538SAndroid Build Coastguard Worker mach_msg_option_t options = MACH_RCV_MSG;
71*6777b538SAndroid Build Coastguard Worker
72*6777b538SAndroid Build Coastguard Worker if (!wait_delta.is_max()) {
73*6777b538SAndroid Build Coastguard Worker options |= MACH_RCV_TIMEOUT | MACH_RCV_INTERRUPT;
74*6777b538SAndroid Build Coastguard Worker }
75*6777b538SAndroid Build Coastguard Worker
76*6777b538SAndroid Build Coastguard Worker mach_msg_size_t rcv_size = sizeof(msg);
77*6777b538SAndroid Build Coastguard Worker if (policy_ == ResetPolicy::MANUAL) {
78*6777b538SAndroid Build Coastguard Worker // To avoid dequeuing the message, receive with a size of 0 and set
79*6777b538SAndroid Build Coastguard Worker // MACH_RCV_LARGE to keep the message in the queue.
80*6777b538SAndroid Build Coastguard Worker options |= MACH_RCV_LARGE;
81*6777b538SAndroid Build Coastguard Worker rcv_size = 0;
82*6777b538SAndroid Build Coastguard Worker }
83*6777b538SAndroid Build Coastguard Worker
84*6777b538SAndroid Build Coastguard Worker // TimeTicks takes care of overflow but we special case is_max() nonetheless
85*6777b538SAndroid Build Coastguard Worker // to avoid invoking TimeTicksNowIgnoringOverride() unnecessarily (same for
86*6777b538SAndroid Build Coastguard Worker // the increment step of the for loop if the condition variable returns
87*6777b538SAndroid Build Coastguard Worker // early). Ref: https://crbug.com/910524#c7
88*6777b538SAndroid Build Coastguard Worker const TimeTicks end_time =
89*6777b538SAndroid Build Coastguard Worker wait_delta.is_max() ? TimeTicks::Max()
90*6777b538SAndroid Build Coastguard Worker : subtle::TimeTicksNowIgnoringOverride() + wait_delta;
91*6777b538SAndroid Build Coastguard Worker // Fake |kr| value to bootstrap the for loop.
92*6777b538SAndroid Build Coastguard Worker kern_return_t kr = MACH_RCV_INTERRUPTED;
93*6777b538SAndroid Build Coastguard Worker for (mach_msg_timeout_t timeout =
94*6777b538SAndroid Build Coastguard Worker wait_delta.is_max() ? MACH_MSG_TIMEOUT_NONE
95*6777b538SAndroid Build Coastguard Worker : saturated_cast<mach_msg_timeout_t>(
96*6777b538SAndroid Build Coastguard Worker wait_delta.InMillisecondsRoundedUp());
97*6777b538SAndroid Build Coastguard Worker // If the thread is interrupted during mach_msg(), the system call will
98*6777b538SAndroid Build Coastguard Worker // be restarted. However, the libsyscall wrapper does not adjust the
99*6777b538SAndroid Build Coastguard Worker // timeout by the amount of time already waited. Using MACH_RCV_INTERRUPT
100*6777b538SAndroid Build Coastguard Worker // will instead return from mach_msg(), so that the call can be retried
101*6777b538SAndroid Build Coastguard Worker // with an adjusted timeout.
102*6777b538SAndroid Build Coastguard Worker kr == MACH_RCV_INTERRUPTED;
103*6777b538SAndroid Build Coastguard Worker timeout = end_time.is_max()
104*6777b538SAndroid Build Coastguard Worker ? MACH_MSG_TIMEOUT_NONE
105*6777b538SAndroid Build Coastguard Worker : std::max(mach_msg_timeout_t{0},
106*6777b538SAndroid Build Coastguard Worker saturated_cast<mach_msg_timeout_t>(
107*6777b538SAndroid Build Coastguard Worker (end_time -
108*6777b538SAndroid Build Coastguard Worker subtle::TimeTicksNowIgnoringOverride())
109*6777b538SAndroid Build Coastguard Worker .InMillisecondsRoundedUp()))) {
110*6777b538SAndroid Build Coastguard Worker kr = mach_msg(&msg.header, options, /*send_size=*/0, rcv_size,
111*6777b538SAndroid Build Coastguard Worker receive_right_->Name(), timeout, /*notify=*/MACH_PORT_NULL);
112*6777b538SAndroid Build Coastguard Worker }
113*6777b538SAndroid Build Coastguard Worker
114*6777b538SAndroid Build Coastguard Worker if (kr == KERN_SUCCESS) {
115*6777b538SAndroid Build Coastguard Worker return true;
116*6777b538SAndroid Build Coastguard Worker } else if (rcv_size == 0 && kr == MACH_RCV_TOO_LARGE) {
117*6777b538SAndroid Build Coastguard Worker return true;
118*6777b538SAndroid Build Coastguard Worker } else {
119*6777b538SAndroid Build Coastguard Worker MACH_CHECK(kr == MACH_RCV_TIMED_OUT, kr) << "mach_msg";
120*6777b538SAndroid Build Coastguard Worker return false;
121*6777b538SAndroid Build Coastguard Worker }
122*6777b538SAndroid Build Coastguard Worker }
123*6777b538SAndroid Build Coastguard Worker
124*6777b538SAndroid Build Coastguard Worker // static
WaitManyImpl(WaitableEvent ** raw_waitables,size_t count)125*6777b538SAndroid Build Coastguard Worker size_t WaitableEvent::WaitManyImpl(WaitableEvent** raw_waitables,
126*6777b538SAndroid Build Coastguard Worker size_t count) {
127*6777b538SAndroid Build Coastguard Worker // On macOS 10.11+, using Mach port sets may cause system instability, per
128*6777b538SAndroid Build Coastguard Worker // https://crbug.com/756102. On macOS 10.12+, a kqueue can be used
129*6777b538SAndroid Build Coastguard Worker // instead to work around that.
130*6777b538SAndroid Build Coastguard Worker enum WaitManyPrimitive {
131*6777b538SAndroid Build Coastguard Worker KQUEUE,
132*6777b538SAndroid Build Coastguard Worker PORT_SET,
133*6777b538SAndroid Build Coastguard Worker };
134*6777b538SAndroid Build Coastguard Worker #if BUILDFLAG(IS_IOS)
135*6777b538SAndroid Build Coastguard Worker const WaitManyPrimitive kPrimitive = PORT_SET;
136*6777b538SAndroid Build Coastguard Worker #else
137*6777b538SAndroid Build Coastguard Worker const WaitManyPrimitive kPrimitive = KQUEUE;
138*6777b538SAndroid Build Coastguard Worker #endif
139*6777b538SAndroid Build Coastguard Worker if (kPrimitive == KQUEUE) {
140*6777b538SAndroid Build Coastguard Worker std::vector<kevent64_s> events(count);
141*6777b538SAndroid Build Coastguard Worker for (size_t i = 0; i < count; ++i) {
142*6777b538SAndroid Build Coastguard Worker EV_SET64(&events[i], raw_waitables[i]->receive_right_->Name(),
143*6777b538SAndroid Build Coastguard Worker EVFILT_MACHPORT, EV_ADD, 0, 0, i, 0, 0);
144*6777b538SAndroid Build Coastguard Worker }
145*6777b538SAndroid Build Coastguard Worker
146*6777b538SAndroid Build Coastguard Worker std::vector<kevent64_s> out_events(count);
147*6777b538SAndroid Build Coastguard Worker
148*6777b538SAndroid Build Coastguard Worker ScopedFD wait_many(kqueue());
149*6777b538SAndroid Build Coastguard Worker PCHECK(wait_many.is_valid()) << "kqueue";
150*6777b538SAndroid Build Coastguard Worker
151*6777b538SAndroid Build Coastguard Worker const int count_int = checked_cast<int>(count);
152*6777b538SAndroid Build Coastguard Worker int rv = HANDLE_EINTR(kevent64(wait_many.get(), events.data(), count_int,
153*6777b538SAndroid Build Coastguard Worker out_events.data(), count_int, /*flags=*/0,
154*6777b538SAndroid Build Coastguard Worker /*timeout=*/nullptr));
155*6777b538SAndroid Build Coastguard Worker PCHECK(rv > 0) << "kevent64";
156*6777b538SAndroid Build Coastguard Worker
157*6777b538SAndroid Build Coastguard Worker size_t triggered = std::numeric_limits<size_t>::max();
158*6777b538SAndroid Build Coastguard Worker for (size_t i = 0; i < static_cast<size_t>(rv); ++i) {
159*6777b538SAndroid Build Coastguard Worker // WaitMany should return the lowest index in |raw_waitables| that was
160*6777b538SAndroid Build Coastguard Worker // triggered.
161*6777b538SAndroid Build Coastguard Worker size_t index = static_cast<size_t>(out_events[i].udata);
162*6777b538SAndroid Build Coastguard Worker triggered = std::min(triggered, index);
163*6777b538SAndroid Build Coastguard Worker }
164*6777b538SAndroid Build Coastguard Worker
165*6777b538SAndroid Build Coastguard Worker if (raw_waitables[triggered]->policy_ == ResetPolicy::AUTOMATIC) {
166*6777b538SAndroid Build Coastguard Worker // The message needs to be dequeued to reset the event.
167*6777b538SAndroid Build Coastguard Worker PeekPort(raw_waitables[triggered]->receive_right_->Name(),
168*6777b538SAndroid Build Coastguard Worker /*dequeue=*/true);
169*6777b538SAndroid Build Coastguard Worker }
170*6777b538SAndroid Build Coastguard Worker
171*6777b538SAndroid Build Coastguard Worker return triggered;
172*6777b538SAndroid Build Coastguard Worker } else {
173*6777b538SAndroid Build Coastguard Worker DCHECK_EQ(kPrimitive, PORT_SET);
174*6777b538SAndroid Build Coastguard Worker
175*6777b538SAndroid Build Coastguard Worker kern_return_t kr;
176*6777b538SAndroid Build Coastguard Worker
177*6777b538SAndroid Build Coastguard Worker apple::ScopedMachPortSet port_set;
178*6777b538SAndroid Build Coastguard Worker {
179*6777b538SAndroid Build Coastguard Worker mach_port_t name;
180*6777b538SAndroid Build Coastguard Worker kr =
181*6777b538SAndroid Build Coastguard Worker mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_PORT_SET, &name);
182*6777b538SAndroid Build Coastguard Worker MACH_CHECK(kr == KERN_SUCCESS, kr) << "mach_port_allocate";
183*6777b538SAndroid Build Coastguard Worker port_set.reset(name);
184*6777b538SAndroid Build Coastguard Worker }
185*6777b538SAndroid Build Coastguard Worker
186*6777b538SAndroid Build Coastguard Worker for (size_t i = 0; i < count; ++i) {
187*6777b538SAndroid Build Coastguard Worker kr = mach_port_insert_member(mach_task_self(),
188*6777b538SAndroid Build Coastguard Worker raw_waitables[i]->receive_right_->Name(),
189*6777b538SAndroid Build Coastguard Worker port_set.get());
190*6777b538SAndroid Build Coastguard Worker MACH_CHECK(kr == KERN_SUCCESS, kr) << "index " << i;
191*6777b538SAndroid Build Coastguard Worker }
192*6777b538SAndroid Build Coastguard Worker
193*6777b538SAndroid Build Coastguard Worker mach_msg_empty_rcv_t msg{};
194*6777b538SAndroid Build Coastguard Worker // Wait on the port set. Only specify space enough for the header, to
195*6777b538SAndroid Build Coastguard Worker // identify which port in the set is signaled. Otherwise, receiving from the
196*6777b538SAndroid Build Coastguard Worker // port set may dequeue a message for a manual-reset event object, which
197*6777b538SAndroid Build Coastguard Worker // would cause it to be reset.
198*6777b538SAndroid Build Coastguard Worker kr = mach_msg(&msg.header,
199*6777b538SAndroid Build Coastguard Worker MACH_RCV_MSG | MACH_RCV_LARGE | MACH_RCV_LARGE_IDENTITY,
200*6777b538SAndroid Build Coastguard Worker /*send_size=*/0, sizeof(msg.header), port_set.get(),
201*6777b538SAndroid Build Coastguard Worker /*timeout=*/0, /*notify=*/MACH_PORT_NULL);
202*6777b538SAndroid Build Coastguard Worker MACH_CHECK(kr == MACH_RCV_TOO_LARGE, kr) << "mach_msg";
203*6777b538SAndroid Build Coastguard Worker
204*6777b538SAndroid Build Coastguard Worker for (size_t i = 0; i < count; ++i) {
205*6777b538SAndroid Build Coastguard Worker WaitableEvent* event = raw_waitables[i];
206*6777b538SAndroid Build Coastguard Worker if (msg.header.msgh_local_port == event->receive_right_->Name()) {
207*6777b538SAndroid Build Coastguard Worker if (event->policy_ == ResetPolicy::AUTOMATIC) {
208*6777b538SAndroid Build Coastguard Worker // The message needs to be dequeued to reset the event.
209*6777b538SAndroid Build Coastguard Worker PeekPort(msg.header.msgh_local_port, true);
210*6777b538SAndroid Build Coastguard Worker }
211*6777b538SAndroid Build Coastguard Worker return i;
212*6777b538SAndroid Build Coastguard Worker }
213*6777b538SAndroid Build Coastguard Worker }
214*6777b538SAndroid Build Coastguard Worker
215*6777b538SAndroid Build Coastguard Worker NOTREACHED();
216*6777b538SAndroid Build Coastguard Worker return 0;
217*6777b538SAndroid Build Coastguard Worker }
218*6777b538SAndroid Build Coastguard Worker }
219*6777b538SAndroid Build Coastguard Worker
220*6777b538SAndroid Build Coastguard Worker // static
PeekPort(mach_port_t port,bool dequeue)221*6777b538SAndroid Build Coastguard Worker bool WaitableEvent::PeekPort(mach_port_t port, bool dequeue) {
222*6777b538SAndroid Build Coastguard Worker if (dequeue) {
223*6777b538SAndroid Build Coastguard Worker mach_msg_empty_rcv_t msg{};
224*6777b538SAndroid Build Coastguard Worker msg.header.msgh_local_port = port;
225*6777b538SAndroid Build Coastguard Worker kern_return_t kr =
226*6777b538SAndroid Build Coastguard Worker mach_msg(&msg.header, MACH_RCV_MSG | MACH_RCV_TIMEOUT, /*send_size=*/0,
227*6777b538SAndroid Build Coastguard Worker sizeof(msg), port, /*timeout=*/0, /*notify=*/MACH_PORT_NULL);
228*6777b538SAndroid Build Coastguard Worker if (kr == KERN_SUCCESS) {
229*6777b538SAndroid Build Coastguard Worker return true;
230*6777b538SAndroid Build Coastguard Worker } else {
231*6777b538SAndroid Build Coastguard Worker MACH_CHECK(kr == MACH_RCV_TIMED_OUT, kr) << "mach_msg";
232*6777b538SAndroid Build Coastguard Worker return false;
233*6777b538SAndroid Build Coastguard Worker }
234*6777b538SAndroid Build Coastguard Worker } else {
235*6777b538SAndroid Build Coastguard Worker mach_port_seqno_t seqno = 0;
236*6777b538SAndroid Build Coastguard Worker mach_msg_size_t size;
237*6777b538SAndroid Build Coastguard Worker mach_msg_id_t id;
238*6777b538SAndroid Build Coastguard Worker mach_msg_trailer_t trailer;
239*6777b538SAndroid Build Coastguard Worker mach_msg_type_number_t trailer_size = sizeof(trailer);
240*6777b538SAndroid Build Coastguard Worker kern_return_t kr = mach_port_peek(
241*6777b538SAndroid Build Coastguard Worker mach_task_self(), port, MACH_RCV_TRAILER_TYPE(MACH_RCV_TRAILER_NULL),
242*6777b538SAndroid Build Coastguard Worker &seqno, &size, &id, reinterpret_cast<mach_msg_trailer_info_t>(&trailer),
243*6777b538SAndroid Build Coastguard Worker &trailer_size);
244*6777b538SAndroid Build Coastguard Worker if (kr == KERN_SUCCESS) {
245*6777b538SAndroid Build Coastguard Worker return true;
246*6777b538SAndroid Build Coastguard Worker } else {
247*6777b538SAndroid Build Coastguard Worker MACH_CHECK(kr == KERN_FAILURE, kr) << "mach_port_peek";
248*6777b538SAndroid Build Coastguard Worker return false;
249*6777b538SAndroid Build Coastguard Worker }
250*6777b538SAndroid Build Coastguard Worker }
251*6777b538SAndroid Build Coastguard Worker }
252*6777b538SAndroid Build Coastguard Worker
ReceiveRight(mach_port_t name)253*6777b538SAndroid Build Coastguard Worker WaitableEvent::ReceiveRight::ReceiveRight(mach_port_t name) : right_(name) {}
254*6777b538SAndroid Build Coastguard Worker
255*6777b538SAndroid Build Coastguard Worker WaitableEvent::ReceiveRight::~ReceiveRight() = default;
256*6777b538SAndroid Build Coastguard Worker
257*6777b538SAndroid Build Coastguard Worker } // namespace base
258