LibIPC: Coalesce Mach read notifications
TransportMachPort wrote to its event-loop notification pipe for every received message. Large bursts could spend significant time in the IO thread just waking the main loop, even when a previous read notification was still pending and would already drain the queued messages. Track whether a read notification is pending while holding the incoming queue mutex, and only write a new pipe byte when the queue needs a wake. EOF still schedules a notification, and synchronous waiters continue to use the condition variable for every arrival.
This commit is contained in:
parent
5c3e61efb6
commit
1a0f8d4372
2 changed files with 32 additions and 6 deletions
|
|
@ -179,7 +179,16 @@ void TransportMachPort::wake_io_thread()
|
||||||
mach_msg(&header, MACH_SEND_MSG | MACH_SEND_TIMEOUT, sizeof(header), 0, MACH_PORT_NULL, 0, MACH_PORT_NULL);
|
mach_msg(&header, MACH_SEND_MSG | MACH_SEND_TIMEOUT, sizeof(header), 0, MACH_PORT_NULL, 0, MACH_PORT_NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TransportMachPort::notify_read_available()
|
bool TransportMachPort::schedule_read_notification_if_needed_locked()
|
||||||
|
{
|
||||||
|
if (m_read_notification_pending)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
m_read_notification_pending = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TransportMachPort::write_read_notification_byte()
|
||||||
{
|
{
|
||||||
if (!m_notify_hook_write_fd)
|
if (!m_notify_hook_write_fd)
|
||||||
return;
|
return;
|
||||||
|
|
@ -190,12 +199,15 @@ void TransportMachPort::notify_read_available()
|
||||||
|
|
||||||
void TransportMachPort::mark_peer_eof()
|
void TransportMachPort::mark_peer_eof()
|
||||||
{
|
{
|
||||||
|
bool should_write_notification = false;
|
||||||
{
|
{
|
||||||
Sync::MutexLocker locker(m_incoming_mutex);
|
Sync::MutexLocker locker(m_incoming_mutex);
|
||||||
m_peer_eof = true;
|
m_peer_eof = true;
|
||||||
|
should_write_notification = schedule_read_notification_if_needed_locked();
|
||||||
}
|
}
|
||||||
m_incoming_cv.broadcast();
|
m_incoming_cv.broadcast();
|
||||||
notify_read_available();
|
if (should_write_notification)
|
||||||
|
write_read_notification_byte();
|
||||||
}
|
}
|
||||||
|
|
||||||
intptr_t TransportMachPort::io_thread_loop()
|
intptr_t TransportMachPort::io_thread_loop()
|
||||||
|
|
@ -415,12 +427,17 @@ void TransportMachPort::process_received_message(u8* buffer)
|
||||||
if (message->bytes.is_empty() && message->attachments.is_empty())
|
if (message->bytes.is_empty() && message->attachments.is_empty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
bool should_write_notification = false;
|
||||||
{
|
{
|
||||||
Sync::MutexLocker locker(m_incoming_mutex);
|
Sync::MutexLocker locker(m_incoming_mutex);
|
||||||
|
auto const was_empty = m_incoming_messages.is_empty();
|
||||||
m_incoming_messages.append(move(message));
|
m_incoming_messages.append(move(message));
|
||||||
|
if (was_empty)
|
||||||
|
should_write_notification = schedule_read_notification_if_needed_locked();
|
||||||
}
|
}
|
||||||
m_incoming_cv.signal();
|
m_incoming_cv.signal();
|
||||||
notify_read_available();
|
if (should_write_notification)
|
||||||
|
write_read_notification_byte();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TransportMachPort::set_up_read_hook(Function<void()> hook)
|
void TransportMachPort::set_up_read_hook(Function<void()> hook)
|
||||||
|
|
@ -430,15 +447,22 @@ void TransportMachPort::set_up_read_hook(Function<void()> hook)
|
||||||
m_read_hook_notifier->on_activation = [this] {
|
m_read_hook_notifier->on_activation = [this] {
|
||||||
char buf[64];
|
char buf[64];
|
||||||
(void)Core::System::read(m_notify_hook_read_fd->value(), { buf, sizeof(buf) });
|
(void)Core::System::read(m_notify_hook_read_fd->value(), { buf, sizeof(buf) });
|
||||||
|
{
|
||||||
|
Sync::MutexLocker locker(m_incoming_mutex);
|
||||||
|
m_read_notification_pending = false;
|
||||||
|
}
|
||||||
if (m_on_read_hook)
|
if (m_on_read_hook)
|
||||||
m_on_read_hook();
|
m_on_read_hook();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
bool should_write_notification = false;
|
||||||
{
|
{
|
||||||
Sync::MutexLocker locker(m_incoming_mutex);
|
Sync::MutexLocker locker(m_incoming_mutex);
|
||||||
if (!m_incoming_messages.is_empty())
|
if (!m_incoming_messages.is_empty() || m_peer_eof)
|
||||||
notify_read_available();
|
should_write_notification = schedule_read_notification_if_needed_locked();
|
||||||
}
|
}
|
||||||
|
if (should_write_notification)
|
||||||
|
write_read_notification_byte();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TransportMachPort::is_open() const
|
bool TransportMachPort::is_open() const
|
||||||
|
|
|
||||||
|
|
@ -84,7 +84,8 @@ private:
|
||||||
intptr_t io_thread_loop();
|
intptr_t io_thread_loop();
|
||||||
void stop_io_thread(IOThreadState desired_state);
|
void stop_io_thread(IOThreadState desired_state);
|
||||||
void wake_io_thread();
|
void wake_io_thread();
|
||||||
void notify_read_available();
|
bool schedule_read_notification_if_needed_locked();
|
||||||
|
void write_read_notification_byte();
|
||||||
void mark_peer_eof();
|
void mark_peer_eof();
|
||||||
void send_mach_message(PendingMessage&);
|
void send_mach_message(PendingMessage&);
|
||||||
void process_received_message(u8* buffer);
|
void process_received_message(u8* buffer);
|
||||||
|
|
@ -111,6 +112,7 @@ private:
|
||||||
Sync::Mutex m_incoming_mutex;
|
Sync::Mutex m_incoming_mutex;
|
||||||
Sync::ConditionVariable m_incoming_cv { m_incoming_mutex };
|
Sync::ConditionVariable m_incoming_cv { m_incoming_mutex };
|
||||||
Vector<NonnullOwnPtr<Message>> m_incoming_messages;
|
Vector<NonnullOwnPtr<Message>> m_incoming_messages;
|
||||||
|
bool m_read_notification_pending { false };
|
||||||
|
|
||||||
RefPtr<AutoCloseFileDescriptor> m_notify_hook_read_fd;
|
RefPtr<AutoCloseFileDescriptor> m_notify_hook_read_fd;
|
||||||
RefPtr<AutoCloseFileDescriptor> m_notify_hook_write_fd;
|
RefPtr<AutoCloseFileDescriptor> m_notify_hook_write_fd;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue