/* * Copyright (c) 2024, Andrew Kaster * Copyright (c) 2025, Aliaksandr Kalenik * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once #include #include #include #include #include #include #include #include #include namespace IPC { class SendQueue : public AtomicRefCounted { public: void enqueue_message(Vector&& bytes, Vector&& fds); struct BytesAndFds { Vector bytes; Vector fds; }; BytesAndFds peek(size_t max_bytes); void discard(size_t bytes_count, size_t fds_count); private: AllocatingMemoryStream m_stream; Vector m_fds; Threading::Mutex m_mutex; }; class TransportSocket { AK_MAKE_NONCOPYABLE(TransportSocket); AK_MAKE_NONMOVABLE(TransportSocket); public: static constexpr socklen_t SOCKET_BUFFER_SIZE = 128 * KiB; explicit TransportSocket(NonnullOwnPtr socket); ~TransportSocket(); void set_up_read_hook(Function); bool is_open() const; void close(); void close_after_sending_all_pending_messages(); void wait_until_readable(); void post_message(Vector const&, Vector> const&); enum class ShouldShutdown { No, Yes, }; struct Message { Vector bytes; Queue fds; }; ShouldShutdown read_as_many_messages_as_possible_without_blocking(Function&&); // Obnoxious name to make it clear that this is a dangerous operation. ErrorOr release_underlying_transport_for_transfer(); ErrorOr clone_for_transfer(); private: enum class TransferState { Continue, SocketClosed, }; [[nodiscard]] TransferState transfer_data(ReadonlyBytes& bytes, Vector& fds); static ErrorOr send_message(Core::LocalSocket&, ReadonlyBytes& bytes, Vector& unowned_fds); enum class IOThreadState { Running, SendPendingMessagesAndStop, Stopped, }; intptr_t io_thread_loop(); void stop_io_thread(IOThreadState desired_state); void wake_io_thread(); void read_incoming_messages(); NonnullOwnPtr m_socket; // After file descriptor is sent, it is moved to the wait queue until an acknowledgement is received from the peer. // This is necessary to handle a specific behavior of the macOS kernel, which may prematurely garbage-collect the file // descriptor contained in the message before the peer receives it. https://openradar.me/9477351 Queue> m_fds_retained_until_received_by_peer; Threading::Mutex m_fds_retained_until_received_by_peer_mutex; RefPtr m_io_thread; RefPtr m_send_queue; Atomic m_io_thread_state { IOThreadState::Running }; Atomic m_peer_eof { false }; ByteBuffer m_unprocessed_bytes; Queue m_unprocessed_fds; Threading::Mutex m_incoming_mutex; Threading::ConditionVariable m_incoming_cv { m_incoming_mutex }; Vector> m_incoming_messages; RefPtr m_wakeup_io_thread_read_fd; RefPtr m_wakeup_io_thread_write_fd; RefPtr m_notify_hook_read_fd; RefPtr m_notify_hook_write_fd; RefPtr m_read_hook_notifier; Function m_on_read_hook; }; }