Use RunAsync in multiple socket operations (#7053)
* Use RunAsync in multiple socket operations * EOF newline * Fix linux compilation * Fix compilation on macos
This commit is contained in:
parent
6cfd00e42d
commit
6264b6d43c
1 changed files with 208 additions and 119 deletions
|
@ -1093,59 +1093,88 @@ void SOC_U::RecvFromOther(Kernel::HLERequestContext& ctx) {
|
||||||
#endif // _WIN32
|
#endif // _WIN32
|
||||||
u32 addr_len = rp.Pop<u32>();
|
u32 addr_len = rp.Pop<u32>();
|
||||||
rp.PopPID();
|
rp.PopPID();
|
||||||
auto& buffer = rp.PopMappedBuffer();
|
|
||||||
|
|
||||||
CTRSockAddr ctr_src_addr;
|
bool needs_async = GetSocketBlocking(fd_info->second) && !dont_wait;
|
||||||
std::vector<u8> output_buff(len);
|
struct AsyncData {
|
||||||
std::vector<u8> addr_buff(addr_len);
|
// Input
|
||||||
|
u32 len{};
|
||||||
|
u32 flags{};
|
||||||
|
u32 addr_len{};
|
||||||
|
SocketHolder* fd_info;
|
||||||
|
#ifdef _WIN32
|
||||||
|
bool dont_wait;
|
||||||
|
bool was_blocking;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Output
|
||||||
|
s32 ret{};
|
||||||
|
int recv_error;
|
||||||
|
Kernel::MappedBuffer* buffer;
|
||||||
|
std::vector<u8> output_buff;
|
||||||
|
std::vector<u8> addr_buff;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto async_data = std::make_shared<AsyncData>();
|
||||||
|
async_data->buffer = &rp.PopMappedBuffer();
|
||||||
|
async_data->ret = -1;
|
||||||
|
async_data->len = len;
|
||||||
|
async_data->flags = flags;
|
||||||
|
async_data->addr_len = addr_len;
|
||||||
|
async_data->output_buff.resize(len);
|
||||||
|
async_data->addr_buff.resize(addr_len);
|
||||||
|
async_data->fd_info = &fd_info->second;
|
||||||
|
#ifdef _WIN32
|
||||||
|
async_data->dont_wait = dont_wait;
|
||||||
|
async_data->was_blocking = was_blocking;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
ctx.RunAsync(
|
||||||
|
[async_data](Kernel::HLERequestContext& ctx) {
|
||||||
sockaddr src_addr;
|
sockaddr src_addr;
|
||||||
socklen_t src_addr_len = sizeof(src_addr);
|
socklen_t src_addr_len = sizeof(src_addr);
|
||||||
|
CTRSockAddr ctr_src_addr;
|
||||||
s32 ret = -1;
|
if (async_data->addr_len > 0) {
|
||||||
if (GetSocketBlocking(fd_info->second) && !dont_wait) {
|
async_data->ret = static_cast<s32>(
|
||||||
PreTimerAdjust();
|
::recvfrom(async_data->fd_info->socket_fd,
|
||||||
}
|
reinterpret_cast<char*>(async_data->output_buff.data()),
|
||||||
|
async_data->len, async_data->flags, &src_addr, &src_addr_len));
|
||||||
if (addr_len > 0) {
|
if (async_data->ret >= 0 && src_addr_len > 0) {
|
||||||
ret = static_cast<s32>(::recvfrom(fd_info->second.socket_fd,
|
|
||||||
reinterpret_cast<char*>(output_buff.data()), len, flags,
|
|
||||||
&src_addr, &src_addr_len));
|
|
||||||
if (ret >= 0 && src_addr_len > 0) {
|
|
||||||
ctr_src_addr = CTRSockAddr::FromPlatform(src_addr);
|
ctr_src_addr = CTRSockAddr::FromPlatform(src_addr);
|
||||||
std::memcpy(addr_buff.data(), &ctr_src_addr, addr_len);
|
std::memcpy(async_data->addr_buff.data(), &ctr_src_addr, async_data->addr_len);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ret = static_cast<s32>(::recvfrom(fd_info->second.socket_fd,
|
async_data->ret = static_cast<s32>(
|
||||||
reinterpret_cast<char*>(output_buff.data()), len, flags,
|
::recvfrom(async_data->fd_info->socket_fd,
|
||||||
NULL, 0));
|
reinterpret_cast<char*>(async_data->output_buff.data()),
|
||||||
addr_buff.resize(0);
|
async_data->len, async_data->flags, NULL, 0));
|
||||||
|
async_data->addr_buff.resize(0);
|
||||||
}
|
}
|
||||||
int recv_error = (ret == SOCKET_ERROR_VALUE) ? GET_ERRNO : 0;
|
async_data->recv_error = (async_data->ret == SOCKET_ERROR_VALUE) ? GET_ERRNO : 0;
|
||||||
if (GetSocketBlocking(fd_info->second) && !dont_wait) {
|
return 0;
|
||||||
PostTimerAdjust(ctx, "RecvFromOther");
|
},
|
||||||
|
[this, async_data](Kernel::HLERequestContext& ctx) {
|
||||||
|
if (async_data->ret == SOCKET_ERROR_VALUE) {
|
||||||
|
async_data->ret = TranslateError(async_data->recv_error);
|
||||||
|
} else {
|
||||||
|
async_data->buffer->Write(async_data->output_buff.data(), 0, async_data->ret);
|
||||||
}
|
}
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
if (dont_wait && was_blocking) {
|
if (async_data->dont_wait && async_data->was_blocking) {
|
||||||
SetSocketBlocking(fd_info->second, true);
|
SetSocketBlocking(*async_data->fd_info, true);
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
(void)this;
|
||||||
#endif
|
#endif
|
||||||
if (ret == SOCKET_ERROR_VALUE) {
|
IPC::RequestBuilder rb(ctx, 0x07, 2, 4);
|
||||||
ret = TranslateError(recv_error);
|
|
||||||
} else {
|
|
||||||
buffer.Write(output_buff.data(), 0, ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(2, 4);
|
|
||||||
rb.Push(RESULT_SUCCESS);
|
rb.Push(RESULT_SUCCESS);
|
||||||
rb.Push(ret);
|
rb.Push(async_data->ret);
|
||||||
rb.PushStaticBuffer(std::move(addr_buff), 0);
|
rb.PushStaticBuffer(std::move(async_data->addr_buff), 0);
|
||||||
rb.PushMappedBuffer(buffer);
|
rb.PushMappedBuffer(*async_data->buffer);
|
||||||
|
},
|
||||||
|
needs_async);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SOC_U::RecvFrom(Kernel::HLERequestContext& ctx) {
|
void SOC_U::RecvFrom(Kernel::HLERequestContext& ctx) {
|
||||||
// TODO(Subv): Calling this function on a blocking socket will block the emu thread,
|
|
||||||
// preventing graceful shutdown when closing the emulator, this can be fixed by always
|
|
||||||
// performing nonblocking operations and spinlock until the data is available
|
|
||||||
IPC::RequestParser rp(ctx);
|
IPC::RequestParser rp(ctx);
|
||||||
u32 socket_handle = rp.Pop<u32>();
|
u32 socket_handle = rp.Pop<u32>();
|
||||||
auto fd_info = open_sockets.find(socket_handle);
|
auto fd_info = open_sockets.find(socket_handle);
|
||||||
|
@ -1172,55 +1201,89 @@ void SOC_U::RecvFrom(Kernel::HLERequestContext& ctx) {
|
||||||
u32 addr_len = rp.Pop<u32>();
|
u32 addr_len = rp.Pop<u32>();
|
||||||
rp.PopPID();
|
rp.PopPID();
|
||||||
|
|
||||||
CTRSockAddr ctr_src_addr;
|
bool needs_async = GetSocketBlocking(fd_info->second) && !dont_wait;
|
||||||
std::vector<u8> output_buff(len);
|
struct AsyncData {
|
||||||
std::vector<u8> addr_buff(addr_len);
|
// Input
|
||||||
|
u32 len{};
|
||||||
|
u32 flags{};
|
||||||
|
u32 addr_len{};
|
||||||
|
SocketHolder* fd_info;
|
||||||
|
#ifdef _WIN32
|
||||||
|
bool dont_wait;
|
||||||
|
bool was_blocking;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Output
|
||||||
|
s32 ret{};
|
||||||
|
int recv_error;
|
||||||
|
std::vector<u8> output_buff;
|
||||||
|
std::vector<u8> addr_buff;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto async_data = std::make_shared<AsyncData>();
|
||||||
|
async_data->ret = -1;
|
||||||
|
async_data->len = len;
|
||||||
|
async_data->flags = flags;
|
||||||
|
async_data->addr_len = addr_len;
|
||||||
|
async_data->output_buff.resize(len);
|
||||||
|
async_data->addr_buff.resize(addr_len);
|
||||||
|
async_data->fd_info = &fd_info->second;
|
||||||
|
#ifdef _WIN32
|
||||||
|
async_data->dont_wait = dont_wait;
|
||||||
|
async_data->was_blocking = was_blocking;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
ctx.RunAsync(
|
||||||
|
[async_data](Kernel::HLERequestContext& ctx) {
|
||||||
sockaddr src_addr;
|
sockaddr src_addr;
|
||||||
socklen_t src_addr_len = sizeof(src_addr);
|
socklen_t src_addr_len = sizeof(src_addr);
|
||||||
|
CTRSockAddr ctr_src_addr;
|
||||||
s32 ret = -1;
|
if (async_data->addr_len > 0) {
|
||||||
if (GetSocketBlocking(fd_info->second) && !dont_wait) {
|
|
||||||
PreTimerAdjust();
|
|
||||||
}
|
|
||||||
if (addr_len > 0) {
|
|
||||||
// Only get src adr if input adr available
|
// Only get src adr if input adr available
|
||||||
ret = static_cast<s32>(::recvfrom(fd_info->second.socket_fd,
|
async_data->ret = static_cast<s32>(
|
||||||
reinterpret_cast<char*>(output_buff.data()), len, flags,
|
::recvfrom(async_data->fd_info->socket_fd,
|
||||||
&src_addr, &src_addr_len));
|
reinterpret_cast<char*>(async_data->output_buff.data()),
|
||||||
if (ret >= 0 && src_addr_len > 0) {
|
async_data->len, async_data->flags, &src_addr, &src_addr_len));
|
||||||
|
if (async_data->ret >= 0 && src_addr_len > 0) {
|
||||||
ctr_src_addr = CTRSockAddr::FromPlatform(src_addr);
|
ctr_src_addr = CTRSockAddr::FromPlatform(src_addr);
|
||||||
std::memcpy(addr_buff.data(), &ctr_src_addr, addr_len);
|
std::memcpy(async_data->addr_buff.data(), &ctr_src_addr, async_data->addr_len);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ret = static_cast<s32>(::recvfrom(fd_info->second.socket_fd,
|
async_data->ret = static_cast<s32>(
|
||||||
reinterpret_cast<char*>(output_buff.data()), len, flags,
|
::recvfrom(async_data->fd_info->socket_fd,
|
||||||
NULL, 0));
|
reinterpret_cast<char*>(async_data->output_buff.data()),
|
||||||
addr_buff.resize(0);
|
async_data->len, async_data->flags, NULL, 0));
|
||||||
}
|
async_data->addr_buff.resize(0);
|
||||||
int recv_error = (ret == SOCKET_ERROR_VALUE) ? GET_ERRNO : 0;
|
|
||||||
if (GetSocketBlocking(fd_info->second) && !dont_wait) {
|
|
||||||
PostTimerAdjust(ctx, "RecvFrom");
|
|
||||||
}
|
}
|
||||||
|
async_data->recv_error = (async_data->ret == SOCKET_ERROR_VALUE) ? GET_ERRNO : 0;
|
||||||
|
return 0;
|
||||||
|
},
|
||||||
|
[this, async_data](Kernel::HLERequestContext& ctx) {
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
if (dont_wait && was_blocking) {
|
if (async_data->dont_wait && async_data->was_blocking) {
|
||||||
SetSocketBlocking(fd_info->second, true);
|
SetSocketBlocking(*async_data->fd_info, true);
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
(void)this;
|
||||||
#endif
|
#endif
|
||||||
s32 total_received = ret;
|
s32 total_received = async_data->ret;
|
||||||
if (ret == SOCKET_ERROR_VALUE) {
|
if (async_data->ret == SOCKET_ERROR_VALUE) {
|
||||||
ret = TranslateError(recv_error);
|
async_data->ret = TranslateError(async_data->recv_error);
|
||||||
total_received = 0;
|
total_received = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write only the data we received to avoid overwriting parts of the buffer with zeros
|
// Write only the data we received to avoid overwriting parts of the buffer with zeros
|
||||||
output_buff.resize(total_received);
|
async_data->output_buff.resize(total_received);
|
||||||
|
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(3, 4);
|
IPC::RequestBuilder rb(ctx, 0x08, 3, 4);
|
||||||
rb.Push(RESULT_SUCCESS);
|
rb.Push(RESULT_SUCCESS);
|
||||||
rb.Push(ret);
|
rb.Push(async_data->ret);
|
||||||
rb.Push(total_received);
|
rb.Push(total_received);
|
||||||
rb.PushStaticBuffer(std::move(output_buff), 0);
|
rb.PushStaticBuffer(std::move(async_data->output_buff), 0);
|
||||||
rb.PushStaticBuffer(std::move(addr_buff), 1);
|
rb.PushStaticBuffer(std::move(async_data->addr_buff), 1);
|
||||||
|
},
|
||||||
|
needs_async);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SOC_U::Poll(Kernel::HLERequestContext& ctx) {
|
void SOC_U::Poll(Kernel::HLERequestContext& ctx) {
|
||||||
|
@ -1230,45 +1293,71 @@ void SOC_U::Poll(Kernel::HLERequestContext& ctx) {
|
||||||
rp.PopPID();
|
rp.PopPID();
|
||||||
auto input_fds = rp.PopStaticBuffer();
|
auto input_fds = rp.PopStaticBuffer();
|
||||||
|
|
||||||
std::vector<CTRPollFD> ctr_fds(nfds);
|
struct AsyncData {
|
||||||
std::memcpy(ctr_fds.data(), input_fds.data(), nfds * sizeof(CTRPollFD));
|
// Input
|
||||||
|
s32 timeout;
|
||||||
|
u32 nfds;
|
||||||
|
|
||||||
|
// Input/Output
|
||||||
|
std::vector<pollfd> platform_pollfd;
|
||||||
|
std::vector<u8> has_libctru_bug;
|
||||||
|
std::vector<CTRPollFD> ctr_fds;
|
||||||
|
|
||||||
|
// Output
|
||||||
|
s32 ret;
|
||||||
|
int poll_error;
|
||||||
|
};
|
||||||
|
auto async_data = std::make_shared<AsyncData>();
|
||||||
|
async_data->timeout = timeout;
|
||||||
|
async_data->nfds = nfds;
|
||||||
|
|
||||||
|
async_data->ctr_fds.resize(nfds);
|
||||||
|
std::memcpy(async_data->ctr_fds.data(), input_fds.data(), nfds * sizeof(CTRPollFD));
|
||||||
|
|
||||||
// The 3ds_pollfd and the pollfd structures may be different (Windows/Linux have different
|
// The 3ds_pollfd and the pollfd structures may be different (Windows/Linux have different
|
||||||
// sizes)
|
// sizes)
|
||||||
// so we have to copy the data in order
|
// so we have to copy the data in order
|
||||||
std::vector<pollfd> platform_pollfd(nfds);
|
async_data->platform_pollfd.resize(nfds);
|
||||||
std::vector<u8> has_libctru_bug(nfds, false);
|
async_data->has_libctru_bug.resize(nfds, false);
|
||||||
for (u32 i = 0; i < nfds; i++) {
|
for (u32 i = 0; i < nfds; i++) {
|
||||||
platform_pollfd[i] = CTRPollFD::ToPlatform(*this, ctr_fds[i], has_libctru_bug[i]);
|
async_data->platform_pollfd[i] =
|
||||||
|
CTRPollFD::ToPlatform(*this, async_data->ctr_fds[i], async_data->has_libctru_bug[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (timeout) {
|
ctx.RunAsync(
|
||||||
PreTimerAdjust();
|
[async_data](Kernel::HLERequestContext& ctx) {
|
||||||
|
async_data->ret =
|
||||||
|
::poll(async_data->platform_pollfd.data(), async_data->nfds, async_data->timeout);
|
||||||
|
if (async_data->ret == SOCKET_ERROR_VALUE) {
|
||||||
|
async_data->poll_error = GET_ERRNO;
|
||||||
}
|
}
|
||||||
s32 ret = ::poll(platform_pollfd.data(), nfds, timeout);
|
return 0;
|
||||||
if (timeout) {
|
},
|
||||||
PostTimerAdjust(ctx, "Poll");
|
[this, async_data](Kernel::HLERequestContext& ctx) {
|
||||||
}
|
|
||||||
|
|
||||||
// Now update the output 3ds_pollfd structure
|
// Now update the output 3ds_pollfd structure
|
||||||
for (u32 i = 0; i < nfds; i++) {
|
for (u32 i = 0; i < async_data->nfds; i++) {
|
||||||
ctr_fds[i] = CTRPollFD::FromPlatform(*this, platform_pollfd[i], has_libctru_bug[i]);
|
async_data->ctr_fds[i] = CTRPollFD::FromPlatform(
|
||||||
|
*this, async_data->platform_pollfd[i], async_data->has_libctru_bug[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<u8> output_fds(nfds * sizeof(CTRPollFD));
|
std::vector<u8> output_fds(async_data->nfds * sizeof(CTRPollFD));
|
||||||
std::memcpy(output_fds.data(), ctr_fds.data(), nfds * sizeof(CTRPollFD));
|
std::memcpy(output_fds.data(), async_data->ctr_fds.data(),
|
||||||
|
async_data->nfds * sizeof(CTRPollFD));
|
||||||
|
|
||||||
if (ret == SOCKET_ERROR_VALUE) {
|
if (async_data->ret == SOCKET_ERROR_VALUE) {
|
||||||
int err = GET_ERRNO;
|
int err = async_data->poll_error;
|
||||||
LOG_ERROR(Service_SOC, "Socket error: {}", err);
|
LOG_DEBUG(Service_SOC, "Socket error: {}", err);
|
||||||
|
|
||||||
ret = TranslateError(GET_ERRNO);
|
async_data->ret = TranslateError(GET_ERRNO);
|
||||||
}
|
}
|
||||||
|
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(2, 2);
|
IPC::RequestBuilder rb(ctx, static_cast<u16>(ctx.CommandHeader().command_id.Value()), 2,
|
||||||
|
2);
|
||||||
rb.Push(RESULT_SUCCESS);
|
rb.Push(RESULT_SUCCESS);
|
||||||
rb.Push(ret);
|
rb.Push(async_data->ret);
|
||||||
rb.PushStaticBuffer(std::move(output_fds), 0);
|
rb.PushStaticBuffer(std::move(output_fds), 0);
|
||||||
|
},
|
||||||
|
timeout != 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SOC_U::GetSockName(Kernel::HLERequestContext& ctx) {
|
void SOC_U::GetSockName(Kernel::HLERequestContext& ctx) {
|
||||||
|
|
Loading…
Reference in a new issue