mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2025-12-10 05:38:49 +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);
|
IOFile out(path, FileAccessMode::Write);
|
||||||
return out.Write(data);
|
return out.Write(data);
|
||||||
}
|
}
|
||||||
|
std::FILE* file = nullptr;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::filesystem::path file_path;
|
std::filesystem::path file_path;
|
||||||
FileAccessMode file_access_mode{};
|
FileAccessMode file_access_mode{};
|
||||||
FileType file_type{};
|
FileType file_type{};
|
||||||
|
|
||||||
std::FILE* file = nullptr;
|
|
||||||
uintptr_t file_mapping = 0;
|
uintptr_t file_mapping = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#include <ranges>
|
#include <ranges>
|
||||||
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
|
#include "common/error.h"
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
#include "common/scope_exit.h"
|
#include "common/scope_exit.h"
|
||||||
#include "common/singleton.h"
|
#include "common/singleton.h"
|
||||||
@@ -24,6 +25,13 @@
|
|||||||
#include "core/memory.h"
|
#include "core/memory.h"
|
||||||
#include "kernel.h"
|
#include "kernel.h"
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <io.h>
|
||||||
|
#include <winsock2.h>
|
||||||
|
#else
|
||||||
|
#include <sys/select.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace D = Core::Devices;
|
namespace D = Core::Devices;
|
||||||
using FactoryDevice = std::function<std::shared_ptr<D::BaseDevice>(u32, const char*, int, u16)>;
|
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;
|
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) {
|
void RegisterFileSystem(Core::Loader::SymbolsResolver* sym) {
|
||||||
LIB_FUNCTION("6c3rCVE-fTU", "libkernel", 1, "libkernel", 1, 1, open);
|
LIB_FUNCTION("6c3rCVE-fTU", "libkernel", 1, "libkernel", 1, 1, open);
|
||||||
LIB_FUNCTION("wuCroIGjt2g", "libScePosix", 1, "libkernel", 1, 1, posix_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("nKWi-N2HBV4", "libkernel", 1, "libkernel", 1, 1, sceKernelPwrite);
|
||||||
LIB_FUNCTION("mBd4AfLP+u8", "libkernel", 1, "libkernel", 1, 1, sceKernelPwritev);
|
LIB_FUNCTION("mBd4AfLP+u8", "libkernel", 1, "libkernel", 1, 1, sceKernelPwritev);
|
||||||
LIB_FUNCTION("AUXVxWeJU-A", "libkernel", 1, "libkernel", 1, 1, sceKernelUnlink);
|
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
|
} // namespace Libraries::Kernel
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ struct Socket {
|
|||||||
virtual int GetSocketAddress(OrbisNetSockaddr* name, u32* namelen) = 0;
|
virtual int GetSocketAddress(OrbisNetSockaddr* name, u32* namelen) = 0;
|
||||||
virtual int GetPeerName(OrbisNetSockaddr* addr, u32* namelen) = 0;
|
virtual int GetPeerName(OrbisNetSockaddr* addr, u32* namelen) = 0;
|
||||||
virtual int fstat(Libraries::Kernel::OrbisKernelStat* stat) = 0;
|
virtual int fstat(Libraries::Kernel::OrbisKernelStat* stat) = 0;
|
||||||
|
virtual std::optional<net_socket> Native() = 0;
|
||||||
std::mutex m_mutex;
|
std::mutex m_mutex;
|
||||||
std::mutex receive_mutex;
|
std::mutex receive_mutex;
|
||||||
};
|
};
|
||||||
@@ -93,6 +94,9 @@ struct PosixSocket : public Socket {
|
|||||||
int GetSocketAddress(OrbisNetSockaddr* name, u32* namelen) override;
|
int GetSocketAddress(OrbisNetSockaddr* name, u32* namelen) override;
|
||||||
int GetPeerName(OrbisNetSockaddr* addr, u32* namelen) override;
|
int GetPeerName(OrbisNetSockaddr* addr, u32* namelen) override;
|
||||||
int fstat(Libraries::Kernel::OrbisKernelStat* stat) override;
|
int fstat(Libraries::Kernel::OrbisKernelStat* stat) override;
|
||||||
|
std::optional<net_socket> Native() override {
|
||||||
|
return sock;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct P2PSocket : public Socket {
|
struct P2PSocket : public Socket {
|
||||||
@@ -113,6 +117,9 @@ struct P2PSocket : public Socket {
|
|||||||
int GetSocketAddress(OrbisNetSockaddr* name, u32* namelen) override;
|
int GetSocketAddress(OrbisNetSockaddr* name, u32* namelen) override;
|
||||||
int GetPeerName(OrbisNetSockaddr* addr, u32* namelen) override;
|
int GetPeerName(OrbisNetSockaddr* addr, u32* namelen) override;
|
||||||
int fstat(Libraries::Kernel::OrbisKernelStat* stat) override;
|
int fstat(Libraries::Kernel::OrbisKernelStat* stat) override;
|
||||||
|
std::optional<net_socket> Native() override {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Libraries::Net
|
} // namespace Libraries::Net
|
||||||
Reference in New Issue
Block a user