Files
ladybird/Libraries/LibWebView/MachPortServer.cpp
Aliaksandr Kalenik 3cb644500e Everywhere: Send IOSurface backing stores via main IPC route on macOS
Now that LibIPC uses Mach ports for transport on macOS, IOSurface port
rights can be sent as regular IPC message attachments instead of through
a separate ad-hoc Mach message side-channel. Introduce
Web::SharedBackingStore that wraps either a MachPort (macOS) or
ShareableBitmap (other platforms) with IPC encode/decode support,
unifying backing store allocation into the existing
did_allocate_backing_stores IPC message.
2026-03-23 23:22:38 +01:00

93 lines
3.6 KiB
C++

/*
* Copyright (c) 2024, Andrew Kaster <akaster@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/Debug.h>
#include <LibCore/Platform/MachMessageTypes.h>
#include <LibThreading/Thread.h>
#include <LibWebView/MachPortServer.h>
namespace WebView {
MachPortServer::MachPortServer(ByteString server_port_name)
: m_thread(Threading::Thread::construct("MachPortServer"sv, [this]() -> intptr_t { thread_loop(); return 0; }))
, m_server_port_name(move(server_port_name))
{
if (auto err = allocate_server_port(); err.is_error())
dbgln("Failed to allocate server port: {}", err.error());
else
start();
}
MachPortServer::~MachPortServer()
{
stop();
}
void MachPortServer::start()
{
m_thread->start();
}
void MachPortServer::stop()
{
// FIXME: We should join instead (after storing should_stop = false) when we have a way to interrupt the thread's mach_msg call
m_thread->detach();
m_should_stop.store(true, MemoryOrder::memory_order_release);
}
bool MachPortServer::is_initialized()
{
return MACH_PORT_VALID(m_server_port_recv_right.port()) && MACH_PORT_VALID(m_server_port_send_right.port());
}
ErrorOr<void> MachPortServer::allocate_server_port()
{
m_server_port_recv_right = TRY(Core::MachPort::create_with_right(Core::MachPort::PortRight::Receive));
m_server_port_send_right = TRY(m_server_port_recv_right.insert_right(Core::MachPort::MessageRight::MakeSend));
TRY(m_server_port_recv_right.register_with_bootstrap_server(m_server_port_name));
dbgln_if(MACH_PORT_DEBUG, "Success! we created and attached mach port {:x} to bootstrap server with name {}", m_server_port_recv_right.port(), m_server_port_name);
return {};
}
void MachPortServer::thread_loop()
{
while (!m_should_stop.load(MemoryOrder::memory_order_acquire)) {
Core::Platform::ReceivedMachMessage message {};
// Get the pid of the child from the audit trailer so we can associate the port w/it
mach_msg_options_t const options = MACH_RCV_MSG | MACH_RCV_TRAILER_TYPE(MACH_RCV_TRAILER_AUDIT) | MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AUDIT);
// FIXME: How can we interrupt this call during application shutdown?
auto const ret = mach_msg(&message.header, options, 0, sizeof(message), m_server_port_recv_right.port(), MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
if (ret != KERN_SUCCESS) {
dbgln("mach_msg failed: {}", mach_error_string(ret));
break;
}
if (message.header.msgh_id == Core::Platform::SELF_TASK_PORT_MESSAGE_ID) {
auto const& task_port_message = message.body;
VERIFY(MACH_MSGH_BITS_LOCAL(message.header.msgh_bits) == MACH_MSG_TYPE_MOVE_SEND);
VERIFY(task_port_message.body.msgh_descriptor_count == 1);
VERIFY(task_port_message.port_descriptor.type == MACH_MSG_PORT_DESCRIPTOR);
auto pid = static_cast<pid_t>(task_port_message.trailer.msgh_audit.val[5]);
auto child_port = Core::MachPort::adopt_right(task_port_message.port_descriptor.name, Core::MachPort::PortRight::Send);
// Extract reply port from the message header (kernel swaps local/remote on receive)
auto reply_port = Core::MachPort::adopt_right(message.header.msgh_remote_port, Core::MachPort::PortRight::SendOnce);
dbgln_if(MACH_PORT_DEBUG, "Received child port {:x} from pid {} (reply port {:x})", child_port.port(), pid, reply_port.port());
if (on_receive_child_mach_port)
on_receive_child_mach_port({ pid, move(child_port), move(reply_port) });
continue;
}
VERIFY_NOT_REACHED();
}
}
}