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.
123 lines
3.5 KiB
C++
123 lines
3.5 KiB
C++
/*
|
|
* Copyright (c) 2026, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
|
|
*
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include <AK/Platform.h>
|
|
|
|
#if !defined(AK_OS_MACOS)
|
|
# error "TransportMachPort is only available on macOS"
|
|
#endif
|
|
|
|
#include <AK/Atomic.h>
|
|
#include <AK/Queue.h>
|
|
#include <AK/RefPtr.h>
|
|
#include <AK/Vector.h>
|
|
#include <LibCore/MachPort.h>
|
|
#include <LibCore/Notifier.h>
|
|
#include <LibIPC/Attachment.h>
|
|
#include <LibIPC/AutoCloseFileDescriptor.h>
|
|
#include <LibIPC/Forward.h>
|
|
#include <LibIPC/ReceivedMessageBytes.h>
|
|
#include <LibIPC/TransportHandle.h>
|
|
#include <LibSync/ConditionVariable.h>
|
|
#include <LibSync/Mutex.h>
|
|
#include <LibThreading/Thread.h>
|
|
|
|
namespace IPC {
|
|
|
|
class TransportMachPort {
|
|
AK_MAKE_NONCOPYABLE(TransportMachPort);
|
|
AK_MAKE_NONMOVABLE(TransportMachPort);
|
|
|
|
public:
|
|
struct Paired {
|
|
NonnullOwnPtr<TransportMachPort> local;
|
|
TransportHandle remote_handle;
|
|
};
|
|
static ErrorOr<Paired> create_paired();
|
|
|
|
TransportMachPort(Core::MachPort receive_right, Core::MachPort send_right);
|
|
~TransportMachPort();
|
|
|
|
void set_up_read_hook(Function<void()>);
|
|
bool is_open() const;
|
|
|
|
void close();
|
|
void close_after_sending_all_pending_messages();
|
|
|
|
void wait_until_readable();
|
|
|
|
void post_message(MessageDataType, Vector<Attachment>& attachments);
|
|
|
|
enum class ShouldShutdown {
|
|
No,
|
|
Yes,
|
|
};
|
|
struct Message {
|
|
ReceivedMessageBytes bytes;
|
|
Queue<Attachment> attachments;
|
|
};
|
|
ShouldShutdown read_as_many_messages_as_possible_without_blocking(Function<void(Message&&)>&&);
|
|
|
|
ErrorOr<TransportHandle> release_for_transfer();
|
|
|
|
private:
|
|
static constexpr unsigned int IPC_DATA_MESSAGE_ID = 0x4950C001;
|
|
static constexpr unsigned int IPC_INLINE_DATA_MESSAGE_ID = 0x4950C002;
|
|
static constexpr unsigned int IPC_WAKEUP_MESSAGE_ID = 0x4950C003;
|
|
|
|
struct PendingMessage {
|
|
MessageDataType bytes;
|
|
Vector<Attachment> attachments;
|
|
};
|
|
|
|
enum class IOThreadState {
|
|
Running,
|
|
SendPendingMessagesAndStop,
|
|
Stopped,
|
|
};
|
|
|
|
intptr_t io_thread_loop();
|
|
void stop_io_thread(IOThreadState desired_state);
|
|
void wake_io_thread();
|
|
bool schedule_read_notification_if_needed_locked();
|
|
void write_read_notification_byte();
|
|
void mark_peer_eof();
|
|
void send_mach_message(PendingMessage&);
|
|
void process_received_message(u8* buffer);
|
|
|
|
Core::MachPort m_receive_port;
|
|
Core::MachPort m_send_port;
|
|
Core::MachPort m_port_set;
|
|
Core::MachPort m_wakeup_receive_port;
|
|
Core::MachPort m_wakeup_send_port;
|
|
|
|
Atomic<bool> m_is_open { true };
|
|
// True while release_for_transfer() is moving this transport's rights to a new owner. In that state,
|
|
// shutdown from the old endpoint is part of the handoff and must not be reported as peer EOF.
|
|
Atomic<bool> m_is_being_transferred { false };
|
|
|
|
RefPtr<Threading::Thread> m_io_thread;
|
|
Atomic<IOThreadState> m_io_thread_state { IOThreadState::Running };
|
|
Atomic<bool> m_peer_eof { false };
|
|
|
|
Vector<PendingMessage> m_pending_send_messages;
|
|
Sync::Mutex m_send_mutex;
|
|
Vector<u8> m_send_buffer;
|
|
|
|
Sync::Mutex m_incoming_mutex;
|
|
Sync::ConditionVariable m_incoming_cv { m_incoming_mutex };
|
|
Vector<NonnullOwnPtr<Message>> m_incoming_messages;
|
|
bool m_read_notification_pending { false };
|
|
|
|
RefPtr<AutoCloseFileDescriptor> m_notify_hook_read_fd;
|
|
RefPtr<AutoCloseFileDescriptor> m_notify_hook_write_fd;
|
|
RefPtr<Core::Notifier> m_read_hook_notifier;
|
|
Function<void()> m_on_read_hook;
|
|
};
|
|
|
|
}
|