From e1f70d532cdd0ff65e66a6dcfbab87f254a0e30d Mon Sep 17 00:00:00 2001 From: stasoid Date: Fri, 7 Feb 2025 21:37:11 +0500 Subject: [PATCH] LibCore: Implement LocalSocket on Windows Windows flavor of non-blocking IO, overlapped IO, differs from that on Linux. On Windows, the OS handles writing to overlapped buffer, while on Linux user must do it manually. Additionally, we can only have overlapped sockets because it is the requirement to be able to wait on them - WSAEventSelect automatically sets socket to nonblocking mode. So we end up emulating Linux-nonblocking sockets with Windows-nonblocking sockets. Pending IO state (ERROR_IO_PENDING) must not escape read/write functions. If that happens, all synchronization like WSAPoll and WaitForMultipleObjects stops working (WaitForMultipleObjects stops working because with overlapped IO you are supposed to wait on an event in OVERLAPPED structure, while we are waiting on WSA Event, see EventLoopImplementationWindows.cpp). --- Libraries/LibCore/CMakeLists.txt | 3 +- Libraries/LibCore/SocketWindows.cpp | 163 ++++++++++++++++++++++++++++ 2 files changed, 165 insertions(+), 1 deletion(-) create mode 100644 Libraries/LibCore/SocketWindows.cpp diff --git a/Libraries/LibCore/CMakeLists.txt b/Libraries/LibCore/CMakeLists.txt index c4b90d12b6..0a6799a034 100644 --- a/Libraries/LibCore/CMakeLists.txt +++ b/Libraries/LibCore/CMakeLists.txt @@ -43,7 +43,6 @@ set(SOURCES Resource.cpp ResourceImplementation.cpp ResourceImplementationFile.cpp - Socket.cpp SystemServerTakeover.cpp TCPServer.cpp ThreadEventQueue.cpp @@ -54,11 +53,13 @@ set(SOURCES if (WIN32) list(APPEND SOURCES ProcessWindows.cpp + SocketWindows.cpp AnonymousBufferWindows.cpp EventLoopImplementationWindows.cpp) else() list(APPEND SOURCES Process.cpp + Socket.cpp AnonymousBuffer.cpp EventLoopImplementationUnix.cpp) endif() diff --git a/Libraries/LibCore/SocketWindows.cpp b/Libraries/LibCore/SocketWindows.cpp new file mode 100644 index 0000000000..084a5a549d --- /dev/null +++ b/Libraries/LibCore/SocketWindows.cpp @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2018-2021, Andreas Kling + * Copyright (c) 2021, sin-ack + * Copyright (c) 2025, stasoid + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include + +#include + +#define MSG_DONTWAIT 0x40 + +namespace Core { + +ErrorOr PosixSocketHelper::read(Bytes buffer, int flags) +{ + if (!is_open()) + return Error::from_errno(ENOTCONN); + + // FIXME: also take into account if PosixSocketHelper is blocking/non-blocking (see set_blocking) + bool blocking = !(flags & MSG_DONTWAIT); + WSABUF buf = { static_cast(buffer.size()), reinterpret_cast(buffer.data()) }; + DWORD nread = 0; + DWORD fl = 0; + OVERLAPPED ov = {}; + + if (WSARecv(m_fd, &buf, 1, &nread, &fl, blocking ? NULL : &ov, NULL) == SOCKET_ERROR) { + + auto error = GetLastError(); + + if (error == WSA_IO_PENDING) { + CancelIo(to_handle(m_fd)); + return Error::from_errno(EWOULDBLOCK); + } + + if (error == WSAECONNRESET) + return Error::from_errno(ECONNRESET); + + return Error::from_windows_error(error); + } + + if (nread == 0) + did_reach_eof_on_read(); + + return buffer.trim(nread); +} + +void PosixSocketHelper::did_reach_eof_on_read() +{ + m_last_read_was_eof = true; + + // If a socket read is EOF, then no more data can be read from it because + // the protocol has disconnected. In this case, we can just disable the + // notifier if we have one. + if (m_notifier) + m_notifier->set_enabled(false); +} + +ErrorOr PosixSocketHelper::write(ReadonlyBytes buffer, int flags) +{ + if (!is_open()) + return Error::from_errno(ENOTCONN); + + // FIXME: Implement non-blocking PosixSocketHelper::write + (void)flags; + WSABUF buf = { static_cast(buffer.size()), reinterpret_cast(const_cast(buffer.data())) }; + DWORD nwritten = 0; + + if (WSASend(m_fd, &buf, 1, &nwritten, 0, NULL, NULL) == SOCKET_ERROR) + return Error::from_windows_error(); + + return nwritten; +} + +ErrorOr PosixSocketHelper::can_read_without_blocking(int timeout) const +{ + struct pollfd pollfd = { + .fd = static_cast(m_fd), + .events = POLLIN, + .revents = 0 + }; + + auto result = WSAPoll(&pollfd, 1, timeout); + if (result == SOCKET_ERROR) + return Error::from_windows_error(); + return result; +} + +ErrorOr PosixSocketHelper::set_blocking(bool) +{ + // FIXME: Implement Core::PosixSocketHelper::set_blocking + // Currently does nothing, sockets are always blocking. + return {}; +} + +ErrorOr PosixSocketHelper::set_close_on_exec(bool enabled) +{ + if (!SetHandleInformation(to_handle(m_fd), HANDLE_FLAG_INHERIT, enabled ? 0 : HANDLE_FLAG_INHERIT)) + return Error::from_windows_error(); + return {}; +} + +ErrorOr PosixSocketHelper::pending_bytes() const +{ + VERIFY(0 && "Core::PosixSocketHelper::pending_bytes is not implemented"); +} + +void PosixSocketHelper::setup_notifier() +{ + if (!m_notifier) + m_notifier = Notifier::construct(m_fd, Notifier::Type::Read); +} + +void PosixSocketHelper::close() +{ + if (!is_open()) + return; + + if (m_notifier) + m_notifier->set_enabled(false); + + MUST(System::close(m_fd)); + m_fd = -1; +} + +ErrorOr LocalSocket::read_without_waiting(Bytes buffer) +{ + return m_helper.read(buffer, MSG_DONTWAIT); +} + +ErrorOr> LocalSocket::adopt_fd(int fd, PreventSIGPIPE prevent_sigpipe) +{ + if (fd == -1) + return Error::from_errno(EBADF); + + auto socket = adopt_own(*new LocalSocket(prevent_sigpipe)); + socket->m_helper.set_fd(fd); + socket->setup_notifier(); + return socket; +} + +Optional LocalSocket::fd() const +{ + if (!is_open()) + return {}; + return m_helper.fd(); +} + +ErrorOr LocalSocket::release_fd() +{ + if (!is_open()) { + return Error::from_errno(ENOTCONN); + } + + auto fd = m_helper.fd(); + m_helper.set_fd(-1); + return fd; +} + +}