// Copyright 2019 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file.
#include"base/message_pump_kqueue.h"
#include <sys/errno.h>
#ifdef XP_IOS # include <BrowserEngineCore/BEkevent.h> #endif
// On iOS, the normal `kevent64` method is blocked by the content process // sandbox, so instead we use `be_kevent64` from the BrowserEngineCore library. static int platform_kevent64(int fd, const kevent64_s* changelist, int nchanges,
kevent64_s* eventlist, int nevents, int flags) { #ifdef XP_IOS return be_kevent64(fd, changelist, nchanges, eventlist, nevents, flags); #else return kevent64(fd, changelist, nchanges, eventlist, nevents, flags, nullptr); #endif
}
// Create a Mach port that will be used to wake up the pump by sending // a message in response to ScheduleWork(). This is significantly faster than // using an EVFILT_USER event, especially when triggered across threads.
mach_port_t wakeup;
kern_return_t kr =
mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &wakeup);
wakeup_.reset(wakeup);
CHECK(kr == KERN_SUCCESS)
<< "mach_port_allocate: " << mach_error_string(kr);
// Specify the wakeup port event to directly receive the Mach message as part // of the kevent64() syscall.
kevent64_s event{};
event.ident = wakeup_.get();
event.filter = EVFILT_MACHPORT;
event.flags = EV_ADD;
event.fflags = MACH_RCV_MSG;
event.ext[0] = reinterpret_cast<uint64_t>(&wakeup_buffer_);
event.ext[1] = sizeof(wakeup_buffer_);
void MessagePumpKqueue::ScheduleWork() {
mach_msg_empty_send_t message{};
message.header.msgh_size = sizeof(message);
message.header.msgh_bits =
MACH_MSGH_BITS_REMOTE(MACH_MSG_TYPE_MAKE_SEND_ONCE);
message.header.msgh_remote_port = wakeup_.get();
kern_return_t kr = mach_msg_send(&message.header);
if (kr != KERN_SUCCESS) { // If ScheduleWork() is being called by other threads faster than the pump // can dispatch work, the kernel message queue for the wakeup port can fill // up (this happens under base_perftests, for example). The kernel does // return a SEND_ONCE right in the case of failure, which must be destroyed // to avoid leaking.
if ((kr & ~MACH_MSG_IPC_SPACE) != MACH_SEND_NO_BUFFER) {
DLOG(ERROR) << "mach_msg_send: " << mach_error_string(kr);
}
mach_msg_destroy(&message.header);
}
}
void MessagePumpKqueue::ScheduleDelayedWork( const TimeTicks& delayed_work_time) { // Nothing to do. This MessagePump uses DoWork().
}
if (controller->fd() != -1 && controller->fd() != fd) {
DLOG(ERROR)
<< "Cannot use the same FileDescriptorWatcher on two different FDs"; return false;
}
StopWatchingFileDescriptor(controller);
if (mode & Mode::WATCH_READ) {
base_event.filter = EVFILT_READ;
events.AppendElement(base_event);
}
if (mode & Mode::WATCH_WRITE) {
base_event.filter = EVFILT_WRITE;
events.AppendElement(base_event);
}
int rv = HANDLE_EINTR(platform_kevent64(kqueue_.get(), events.Elements(),
events.Length(), nullptr, 0, 0));
if (rv < 0) DLOG(ERROR) << "StopWatchingFileDescriptor kevent64";
// The keys for the IDMap aren't recorded anywhere (they're attached to the // kevent object in the kernel), so locate the entries by controller pointer.
fd_controllers_.RemoveIf([&](auto& it) { return it.Data() == controller; });
bool MessagePumpKqueue::ProcessEvents(Delegate* delegate, int count) { bool did_work = false;
for (int i = 0; i < count; ++i) { auto* event = &events_[i];
if (event->filter == EVFILT_READ || event->filter == EVFILT_WRITE) {
did_work = true;
FileDescriptorWatcher* controller = fd_controllers_.Get(event->udata);
if (!controller) { // The controller was removed by some other work callout before // this event could be processed. continue;
}
Watcher* fd_watcher = controller->watcher();
if (event->flags & EV_ONESHOT) { // If this was a one-shot event, the Controller needs to stop tracking // the descriptor, so it is not double-removed when it is told to stop // watching.
controller->Reset();
fd_controllers_.Remove(event->udata);
--event_count_;
}
if (fd_watcher) {
if (event->filter == EVFILT_READ) {
fd_watcher->OnFileCanReadWithoutBlocking( static_cast<int>(event->ident));
} else if (event->filter == EVFILT_WRITE) {
fd_watcher->OnFileCanWriteWithoutBlocking( static_cast<int>(event->ident));
}
}
} else if (event->filter == EVFILT_MACHPORT) {
mach_port_t port = event->ident;
if (port == wakeup_.get()) { // The wakeup event has been received, do not treat this as "doing // work", this just wakes up the pump. continue;
}
did_work = true;
MachPortWatchController* controller = port_controllers_.Get(port); // The controller could have been removed by some other work callout // before this event could be processed.
if (controller) {
controller->watcher()->OnMachMessageReceived(port);
}
} else if (event->filter == EVFILT_TIMER) { // The wakeup timer fired.
DCHECK(!delayed_work_time_.is_null());
delayed_work_time_ = base::TimeTicks();
--event_count_;
} else {
NOTREACHED() << "Unexpected event for filter " << event->filter;
}
}
// The ident of the wakeup timer. There's only the one timer as the pair // (ident, filter) is the identity of the event.
constexpr uint64_t kWakeupTimerIdent = 0x0;
if (wakeup_time.is_null()) { // Clear the timer.
kevent64_s timer{};
timer.ident = kWakeupTimerIdent;
timer.filter = EVFILT_TIMER;
timer.flags = EV_DELETE;
int rv = ChangeOneEvent(kqueue_, &timer);
CHECK(rv == 0)
<< "kevent64, delete timer";
--event_count_;
} else { // Set/reset the timer.
kevent64_s timer{};
timer.ident = kWakeupTimerIdent;
timer.filter = EVFILT_TIMER; // This updates the timer if it already exists in |kqueue_|.
timer.flags = EV_ADD | EV_ONESHOT;
// Specify the sleep in microseconds to avoid undersleeping due to // numeric problems. // If wakeup_time is in the past, the delta below will be negative and the // timer is set immediately.
timer.fflags = NOTE_USECONDS;
timer.data = (wakeup_time - base::TimeTicks::Now()).InMicroseconds();
int rv = ChangeOneEvent(kqueue_, &timer);
CHECK(rv == 0)
<< "kevent64, set timer";
// Bump the event count if we just added the timer.
if (delayed_work_time_.is_null()) ++event_count_;
}
delayed_work_time_ = wakeup_time;
}
} // namespace base
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.15 Sekunden
(vorverarbeitet am 2026-08-26)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.