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).
This commit is contained in:
stasoid 2025-02-07 21:37:11 +05:00 committed by Andrew Kaster
parent 8088ab5306
commit e1f70d532c
Notes: github-actions[bot] 2025-02-10 19:47:27 +00:00
2 changed files with 165 additions and 1 deletions

View file

@ -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()

View file

@ -0,0 +1,163 @@
/*
* Copyright (c) 2018-2021, Andreas Kling <andreas@ladybird.org>
* Copyright (c) 2021, sin-ack <sin-ack@protonmail.com>
* Copyright (c) 2025, stasoid <stasoid@yahoo.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibCore/Socket.h>
#include <LibCore/System.h>
#include <AK/Windows.h>
#define MSG_DONTWAIT 0x40
namespace Core {
ErrorOr<Bytes> 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<ULONG>(buffer.size()), reinterpret_cast<CHAR*>(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<size_t> 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<ULONG>(buffer.size()), reinterpret_cast<CHAR*>(const_cast<u8*>(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<bool> PosixSocketHelper::can_read_without_blocking(int timeout) const
{
struct pollfd pollfd = {
.fd = static_cast<SOCKET>(m_fd),
.events = POLLIN,
.revents = 0
};
auto result = WSAPoll(&pollfd, 1, timeout);
if (result == SOCKET_ERROR)
return Error::from_windows_error();
return result;
}
ErrorOr<void> PosixSocketHelper::set_blocking(bool)
{
// FIXME: Implement Core::PosixSocketHelper::set_blocking
// Currently does nothing, sockets are always blocking.
return {};
}
ErrorOr<void> 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<size_t> 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<Bytes> LocalSocket::read_without_waiting(Bytes buffer)
{
return m_helper.read(buffer, MSG_DONTWAIT);
}
ErrorOr<NonnullOwnPtr<LocalSocket>> 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<int> LocalSocket::fd() const
{
if (!is_open())
return {};
return m_helper.fd();
}
ErrorOr<int> LocalSocket::release_fd()
{
if (!is_open()) {
return Error::from_errno(ENOTCONN);
}
auto fd = m_helper.fd();
m_helper.set_fd(-1);
return fd;
}
}