mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2025-12-09 13:19:00 +00:00
select (#3353)
* select * select for windows * fixed windows only function * windows error converts * fixed up * fixed compile * another implementation for windows * draft rewrite for windows * implementation for windows * added some debugging info * more debugging * extensive log * Windows: Add device files to output fd_sets Cyberpunk 2077 breaks without this. Also added some formatting changes, to bring the implementation closer to our typical coding standards. * Clang * Formatting + cleanup Removed some excessive logging used for debugging, and did some cleanup on the non-Windows implementation. --------- Co-authored-by: georgemoralis <giorgosmrls@gmail.com> Co-authored-by: Stephen Miller <millerste004@gmail.com>
This commit is contained in:
committed by
GitHub
parent
f9ab6c48e3
commit
befc5ec17b
@@ -213,13 +213,13 @@ public:
|
||||
IOFile out(path, FileAccessMode::Write);
|
||||
return out.Write(data);
|
||||
}
|
||||
std::FILE* file = nullptr;
|
||||
|
||||
private:
|
||||
std::filesystem::path file_path;
|
||||
FileAccessMode file_access_mode{};
|
||||
FileType file_type{};
|
||||
|
||||
std::FILE* file = nullptr;
|
||||
uintptr_t file_mapping = 0;
|
||||
};
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <ranges>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/error.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/scope_exit.h"
|
||||
#include "common/singleton.h"
|
||||
@@ -24,6 +25,13 @@
|
||||
#include "core/memory.h"
|
||||
#include "kernel.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <io.h>
|
||||
#include <winsock2.h>
|
||||
#else
|
||||
#include <sys/select.h>
|
||||
#endif
|
||||
|
||||
namespace D = Core::Devices;
|
||||
using FactoryDevice = std::function<std::shared_ptr<D::BaseDevice>(u32, const char*, int, u16)>;
|
||||
|
||||
@@ -1068,6 +1076,295 @@ s32 PS4_SYSV_ABI sceKernelUnlink(const char* path) {
|
||||
return result;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
#define __FD_SETSIZE 1024
|
||||
|
||||
typedef struct {
|
||||
unsigned long fds_bits[__FD_SETSIZE / (8 * sizeof(unsigned long))];
|
||||
} fd_set_posix;
|
||||
|
||||
#define FD_SET_POSIX(fd, set) \
|
||||
((set)->fds_bits[(fd) / (8 * sizeof(unsigned long))] |= \
|
||||
(1UL << ((fd) % (8 * sizeof(unsigned long)))))
|
||||
|
||||
#define FD_CLR_POSIX(fd, set) \
|
||||
((set)->fds_bits[(fd) / (8 * sizeof(unsigned long))] &= \
|
||||
~(1UL << ((fd) % (8 * sizeof(unsigned long)))))
|
||||
|
||||
#define FD_ISSET_POSIX(fd, set) \
|
||||
(((set)->fds_bits[(fd) / (8 * sizeof(unsigned long))] & \
|
||||
(1UL << ((fd) % (8 * sizeof(unsigned long))))) != 0)
|
||||
|
||||
#define FD_ZERO_POSIX(set) memset((set), 0, sizeof(fd_set_posix))
|
||||
|
||||
s32 PS4_SYSV_ABI posix_select(s32 nfds, fd_set_posix* readfds, fd_set_posix* writefds,
|
||||
fd_set_posix* exceptfds, OrbisKernelTimeval* timeout) {
|
||||
LOG_DEBUG(Kernel_Fs, "nfds = {}, readfds = {}, writefds = {}, exceptfds = {}, timeout = {}",
|
||||
nfds, fmt::ptr(readfds), fmt::ptr(writefds), fmt::ptr(exceptfds), fmt::ptr(timeout));
|
||||
|
||||
auto* h = Common::Singleton<Core::FileSys::HandleTable>::Instance();
|
||||
|
||||
fd_set read_host = {}, write_host = {}, except_host = {};
|
||||
FD_ZERO(&read_host);
|
||||
FD_ZERO(&write_host);
|
||||
FD_ZERO(&except_host);
|
||||
|
||||
fd_set_posix read_ready, write_ready, except_ready;
|
||||
FD_ZERO_POSIX(&read_ready);
|
||||
FD_ZERO_POSIX(&write_ready);
|
||||
FD_ZERO_POSIX(&except_ready);
|
||||
|
||||
std::map<s32, s32> host_to_guest;
|
||||
s32 socket_max_fd = -1;
|
||||
|
||||
for (s32 i = 0; i < nfds; ++i) {
|
||||
bool want_read = readfds && FD_ISSET_POSIX(i, readfds);
|
||||
bool want_write = writefds && FD_ISSET_POSIX(i, writefds);
|
||||
bool want_except = exceptfds && FD_ISSET_POSIX(i, exceptfds);
|
||||
if (!(want_read || want_write || want_except)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto* file = h->GetFile(i);
|
||||
if (!file || ((file->type == Core::FileSys::FileType::Regular && !file->f.IsOpen()) ||
|
||||
(file->type == Core::FileSys::FileType::Socket && !file->is_opened))) {
|
||||
LOG_ERROR(Kernel_Fs, "fd {} is null or not opened", i);
|
||||
*__Error() = POSIX_EBADF;
|
||||
return -1;
|
||||
}
|
||||
|
||||
s32 native_fd = -1;
|
||||
switch (file->type) {
|
||||
case Core::FileSys::FileType::Regular:
|
||||
native_fd = static_cast<s32>(file->f.GetFileMapping());
|
||||
break;
|
||||
case Core::FileSys::FileType::Socket: {
|
||||
auto sock = file->socket->Native();
|
||||
native_fd = sock ? static_cast<s32>(*sock) : -1;
|
||||
break;
|
||||
}
|
||||
case Core::FileSys::FileType::Device:
|
||||
native_fd = -1;
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
break;
|
||||
}
|
||||
|
||||
if (file->type == Core::FileSys::FileType::Regular ||
|
||||
file->type == Core::FileSys::FileType::Device) {
|
||||
// Disk files always ready
|
||||
if (want_read) {
|
||||
FD_SET_POSIX(i, &read_ready);
|
||||
}
|
||||
if (want_write) {
|
||||
FD_SET_POSIX(i, &write_ready);
|
||||
}
|
||||
// exceptfds not supported on regular files
|
||||
} else if (file->type == Core::FileSys::FileType::Socket) {
|
||||
if (want_read) {
|
||||
FD_SET(native_fd, &read_host);
|
||||
}
|
||||
if (want_write) {
|
||||
FD_SET(native_fd, &write_host);
|
||||
}
|
||||
if (want_except) {
|
||||
FD_SET(native_fd, &except_host);
|
||||
}
|
||||
socket_max_fd = std::max(socket_max_fd, native_fd);
|
||||
}
|
||||
|
||||
if (native_fd == -1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
host_to_guest[native_fd] = i;
|
||||
}
|
||||
|
||||
LOG_DEBUG(Kernel_Fs,
|
||||
"Before select(): read_host.fd_count = {}, write_host.fd_count = {}, "
|
||||
"except_host.fd_count = {}",
|
||||
read_host.fd_count, write_host.fd_count, except_host.fd_count);
|
||||
|
||||
if (read_host.fd_count == 0 && write_host.fd_count == 0 && except_host.fd_count == 0) {
|
||||
LOG_WARNING(Kernel_Fs, "No sockets in fd_sets, select() will return immediately");
|
||||
}
|
||||
|
||||
if (readfds) {
|
||||
FD_ZERO_POSIX(readfds);
|
||||
}
|
||||
if (writefds) {
|
||||
FD_ZERO_POSIX(writefds);
|
||||
}
|
||||
if (exceptfds) {
|
||||
FD_ZERO_POSIX(exceptfds);
|
||||
}
|
||||
|
||||
s32 result = 0;
|
||||
if (socket_max_fd != -1) {
|
||||
timeval tv = {};
|
||||
timeval* tv_ptr = nullptr;
|
||||
if (timeout) {
|
||||
tv.tv_sec = timeout->tv_sec;
|
||||
tv.tv_usec = timeout->tv_usec;
|
||||
tv_ptr = &tv;
|
||||
}
|
||||
result = select(0, read_host.fd_count > 0 ? &read_host : nullptr,
|
||||
write_host.fd_count > 0 ? &write_host : nullptr,
|
||||
except_host.fd_count > 0 ? &except_host : nullptr, tv_ptr);
|
||||
if (result == SOCKET_ERROR) {
|
||||
s32 err = WSAGetLastError();
|
||||
LOG_ERROR(Kernel_Fs, "select() failed with error {}", err);
|
||||
switch (err) {
|
||||
case WSAEFAULT:
|
||||
*__Error() = POSIX_EFAULT;
|
||||
break;
|
||||
case WSAEINVAL:
|
||||
*__Error() = POSIX_EINVAL;
|
||||
break;
|
||||
case WSAENOBUFS:
|
||||
*__Error() = POSIX_ENOBUFS;
|
||||
break;
|
||||
default:
|
||||
LOG_ERROR(Kernel_Fs, "Unhandled error case {}", err);
|
||||
break;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (s32 i = 0; i < read_host.fd_count; ++i) {
|
||||
s32 fd = static_cast<s32>(read_host.fd_array[i]);
|
||||
FD_SET_POSIX(host_to_guest[fd], readfds);
|
||||
}
|
||||
for (s32 i = 0; i < write_host.fd_count; ++i) {
|
||||
s32 fd = static_cast<s32>(write_host.fd_array[i]);
|
||||
FD_SET_POSIX(host_to_guest[fd], writefds);
|
||||
}
|
||||
for (s32 i = 0; i < except_host.fd_count; ++i) {
|
||||
s32 fd = static_cast<s32>(except_host.fd_array[i]);
|
||||
FD_SET_POSIX(host_to_guest[fd], exceptfds);
|
||||
}
|
||||
}
|
||||
|
||||
// Add regular/device files ready count
|
||||
s32 disk_ready = 0;
|
||||
for (s32 i = 0; i < nfds; ++i) {
|
||||
if (FD_ISSET_POSIX(i, &read_ready)) {
|
||||
FD_SET_POSIX(i, readfds);
|
||||
disk_ready++;
|
||||
}
|
||||
if (FD_ISSET_POSIX(i, &write_ready)) {
|
||||
FD_SET_POSIX(i, writefds);
|
||||
disk_ready++;
|
||||
}
|
||||
}
|
||||
|
||||
return result + disk_ready;
|
||||
}
|
||||
#else
|
||||
s32 PS4_SYSV_ABI posix_select(s32 nfds, fd_set* readfds, fd_set* writefds, fd_set* exceptfds,
|
||||
OrbisKernelTimeval* timeout) {
|
||||
LOG_DEBUG(Kernel_Fs, "nfds = {}, readfds = {}, writefds = {}, exceptfds = {}, timeout = {}",
|
||||
nfds, fmt::ptr(readfds), fmt::ptr(writefds), fmt::ptr(exceptfds), fmt::ptr(timeout));
|
||||
|
||||
auto* h = Common::Singleton<Core::FileSys::HandleTable>::Instance();
|
||||
fd_set read_host, write_host, except_host;
|
||||
FD_ZERO(&read_host);
|
||||
FD_ZERO(&write_host);
|
||||
FD_ZERO(&except_host);
|
||||
|
||||
std::map<s32, s32> host_to_guest;
|
||||
s32 max_fd = -1;
|
||||
|
||||
for (s32 i = 0; i < nfds; ++i) {
|
||||
auto read = readfds && FD_ISSET(i, readfds);
|
||||
auto write = writefds && FD_ISSET(i, writefds);
|
||||
auto except = exceptfds && FD_ISSET(i, exceptfds);
|
||||
if (read || write || except) {
|
||||
auto* file = h->GetFile(i);
|
||||
if (file == nullptr ||
|
||||
((file->type == Core::FileSys::FileType::Regular && !file->f.IsOpen()) ||
|
||||
(file->type == Core::FileSys::FileType::Socket && !file->is_opened))) {
|
||||
LOG_ERROR(Kernel_Fs, "fd {} is null or not opened", i);
|
||||
*__Error() = POSIX_EBADF;
|
||||
return -1;
|
||||
}
|
||||
|
||||
s32 native_fd = [&] {
|
||||
switch (file->type) {
|
||||
case Core::FileSys::FileType::Regular:
|
||||
return static_cast<s32>(file->f.GetFileMapping());
|
||||
case Core::FileSys::FileType::Device:
|
||||
return -1;
|
||||
case Core::FileSys::FileType::Socket: {
|
||||
auto sock = file->socket->Native();
|
||||
// until P2P sockets contain a proper socket
|
||||
return sock ? static_cast<s32>(*sock) : -1;
|
||||
}
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
}();
|
||||
if (native_fd == -1) {
|
||||
continue;
|
||||
}
|
||||
host_to_guest.emplace(native_fd, i);
|
||||
|
||||
max_fd = std::max(max_fd, native_fd);
|
||||
|
||||
if (read) {
|
||||
FD_SET(native_fd, &read_host);
|
||||
}
|
||||
if (write) {
|
||||
FD_SET(native_fd, &write_host);
|
||||
}
|
||||
if (except) {
|
||||
FD_SET(native_fd, &except_host);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (max_fd == -1) {
|
||||
LOG_WARNING(Kernel_Fs, "all requested file descriptors are unsupported");
|
||||
return 0;
|
||||
}
|
||||
|
||||
s32 ret = select(max_fd + 1, &read_host, &write_host, &except_host, (timeval*)timeout);
|
||||
|
||||
if (ret > 0) {
|
||||
if (readfds) {
|
||||
FD_ZERO(readfds);
|
||||
}
|
||||
if (writefds) {
|
||||
FD_ZERO(writefds);
|
||||
}
|
||||
if (exceptfds) {
|
||||
FD_ZERO(exceptfds);
|
||||
}
|
||||
|
||||
for (s32 i = 0; i < max_fd + 1; ++i) {
|
||||
if (readfds && FD_ISSET(i, &read_host)) {
|
||||
FD_SET(host_to_guest[i], readfds);
|
||||
}
|
||||
if (writefds && FD_ISSET(i, &write_host)) {
|
||||
FD_SET(host_to_guest[i], writefds);
|
||||
}
|
||||
if (exceptfds && FD_ISSET(i, &except_host)) {
|
||||
FD_SET(host_to_guest[i], exceptfds);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ret < 0) {
|
||||
s32 error = errno;
|
||||
LOG_ERROR(Kernel_Fs, "native select call failed with {} ({})", error,
|
||||
Common::NativeErrorToString(error));
|
||||
SetPosixErrno(error);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
void RegisterFileSystem(Core::Loader::SymbolsResolver* sym) {
|
||||
LIB_FUNCTION("6c3rCVE-fTU", "libkernel", 1, "libkernel", 1, 1, open);
|
||||
LIB_FUNCTION("wuCroIGjt2g", "libScePosix", 1, "libkernel", 1, 1, posix_open);
|
||||
@@ -1125,6 +1422,8 @@ void RegisterFileSystem(Core::Loader::SymbolsResolver* sym) {
|
||||
LIB_FUNCTION("nKWi-N2HBV4", "libkernel", 1, "libkernel", 1, 1, sceKernelPwrite);
|
||||
LIB_FUNCTION("mBd4AfLP+u8", "libkernel", 1, "libkernel", 1, 1, sceKernelPwritev);
|
||||
LIB_FUNCTION("AUXVxWeJU-A", "libkernel", 1, "libkernel", 1, 1, sceKernelUnlink);
|
||||
LIB_FUNCTION("T8fER+tIGgk", "libScePosix", 1, "libkernel", 1, 1, posix_select);
|
||||
LIB_FUNCTION("T8fER+tIGgk", "libkernel", 1, "libkernel", 1, 1, posix_select);
|
||||
}
|
||||
|
||||
} // namespace Libraries::Kernel
|
||||
|
||||
@@ -58,6 +58,7 @@ struct Socket {
|
||||
virtual int GetSocketAddress(OrbisNetSockaddr* name, u32* namelen) = 0;
|
||||
virtual int GetPeerName(OrbisNetSockaddr* addr, u32* namelen) = 0;
|
||||
virtual int fstat(Libraries::Kernel::OrbisKernelStat* stat) = 0;
|
||||
virtual std::optional<net_socket> Native() = 0;
|
||||
std::mutex m_mutex;
|
||||
std::mutex receive_mutex;
|
||||
};
|
||||
@@ -93,6 +94,9 @@ struct PosixSocket : public Socket {
|
||||
int GetSocketAddress(OrbisNetSockaddr* name, u32* namelen) override;
|
||||
int GetPeerName(OrbisNetSockaddr* addr, u32* namelen) override;
|
||||
int fstat(Libraries::Kernel::OrbisKernelStat* stat) override;
|
||||
std::optional<net_socket> Native() override {
|
||||
return sock;
|
||||
}
|
||||
};
|
||||
|
||||
struct P2PSocket : public Socket {
|
||||
@@ -113,6 +117,9 @@ struct P2PSocket : public Socket {
|
||||
int GetSocketAddress(OrbisNetSockaddr* name, u32* namelen) override;
|
||||
int GetPeerName(OrbisNetSockaddr* addr, u32* namelen) override;
|
||||
int fstat(Libraries::Kernel::OrbisKernelStat* stat) override;
|
||||
std::optional<net_socket> Native() override {
|
||||
return {};
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Libraries::Net
|
||||
Reference in New Issue
Block a user