mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-06-27 14:58:30 +00:00
Rewrite Windows accept()
This change should fix the Windows issues Qt Creator has been having, by ensuring accept() and accept4() work in O_NONBLOCK mode. I switched away from AcceptEx() which is buggy, back to using WSAAccept(). This requires making a tradeoff where we have to accept a busy loop. However it is low latency in nature, just like our new and improved Windows poll() code. I was furthermore able to eliminate a bunch of Windows-related test todos.
This commit is contained in:
parent
6f868fe1de
commit
acd6c32184
20 changed files with 622 additions and 209 deletions
|
@ -37,9 +37,6 @@
|
|||
#include "libc/thread/thread.h"
|
||||
|
||||
TEST(O_NONBLOCK, canBeSetBySocket_toMakeListenNonBlocking) {
|
||||
// TODO(jart): this doesn't make any sense on windows
|
||||
if (IsWindows())
|
||||
return;
|
||||
char buf[16] = {0};
|
||||
uint32_t addrsize = sizeof(struct sockaddr_in);
|
||||
struct sockaddr_in addr = {
|
||||
|
|
|
@ -33,8 +33,6 @@
|
|||
// two clients send a udp packet containing their local address
|
||||
// server verifies content of packet matches the peer's address
|
||||
TEST(recvfrom, test) {
|
||||
if (!IsWindows())
|
||||
return;
|
||||
uint32_t addrsize = sizeof(struct sockaddr_in);
|
||||
struct sockaddr_in server = {
|
||||
.sin_family = AF_INET,
|
||||
|
|
|
@ -41,9 +41,9 @@
|
|||
|
||||
void SetUpOnce(void) {
|
||||
if (IsNetbsd())
|
||||
exit(0);
|
||||
exit(0); // no sendfile support
|
||||
if (IsOpenbsd())
|
||||
exit(0);
|
||||
exit(0); // no sendfile support
|
||||
testlib_enable_tmp_setup_teardown();
|
||||
ASSERT_SYS(0, 0, pledge("stdio rpath wpath cpath proc inet", 0));
|
||||
}
|
||||
|
@ -102,9 +102,6 @@ TEST(sendfile, testSeeking) {
|
|||
}
|
||||
|
||||
TEST(sendfile, testPositioning) {
|
||||
// TODO(jart): fix test regression on windows
|
||||
if (IsWindows())
|
||||
return;
|
||||
char buf[1024];
|
||||
uint32_t addrsize = sizeof(struct sockaddr_in);
|
||||
struct sockaddr_in addr = {
|
||||
|
@ -130,9 +127,8 @@ TEST(sendfile, testPositioning) {
|
|||
ASSERT_TRUE(errno == EINVAL || errno == EPIPE);
|
||||
errno = 0;
|
||||
// XXX: WSL1 clobbers file offset on failure!
|
||||
if (!__iswsl1()) {
|
||||
if (!__iswsl1())
|
||||
ASSERT_EQ(12, GetFileOffset(5));
|
||||
}
|
||||
_Exit(0);
|
||||
}
|
||||
ASSERT_SYS(0, 0, close(3));
|
||||
|
|
|
@ -32,7 +32,9 @@ TEST_POSIX_DIRECTDEPS = \
|
|||
LIBC_INTRIN \
|
||||
LIBC_MEM \
|
||||
LIBC_PROC \
|
||||
LIBC_LOG \
|
||||
LIBC_RUNTIME \
|
||||
LIBC_SOCK \
|
||||
LIBC_STDIO \
|
||||
LIBC_STR \
|
||||
LIBC_SYSV \
|
||||
|
|
71
test/posix/accept4_nonblock_test.c
Normal file
71
test/posix/accept4_nonblock_test.c
Normal file
|
@ -0,0 +1,71 @@
|
|||
#include <arpa/inet.h>
|
||||
#include <cosmo.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <netinet/in.h>
|
||||
#include <poll.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int main() {
|
||||
|
||||
// Create server socket
|
||||
int server_fd;
|
||||
struct sockaddr_in address;
|
||||
int addrlen = sizeof(address);
|
||||
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
|
||||
return 1;
|
||||
address.sin_family = AF_INET;
|
||||
address.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
||||
address.sin_port = 0; // let os assign random port
|
||||
if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)))
|
||||
return 2;
|
||||
if (getsockname(server_fd, (struct sockaddr *)&address,
|
||||
(socklen_t *)&addrlen))
|
||||
return 3;
|
||||
if (listen(server_fd, SOMAXCONN))
|
||||
return 4;
|
||||
|
||||
{
|
||||
// poll server
|
||||
struct pollfd fds[2] = {
|
||||
{server_fd, POLLIN | POLLOUT},
|
||||
};
|
||||
int ret = poll(fds, 1, 0);
|
||||
if (ret != 0)
|
||||
return 5;
|
||||
}
|
||||
|
||||
// create client socket
|
||||
int client_fd;
|
||||
if ((client_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
|
||||
return 6;
|
||||
if (connect(client_fd, (struct sockaddr *)&address, sizeof(address)))
|
||||
return 7;
|
||||
|
||||
// accept client
|
||||
int server_client_fd;
|
||||
if ((server_client_fd = accept4(server_fd, 0, 0, SOCK_NONBLOCK)) == -1)
|
||||
return 8;
|
||||
|
||||
// check that it's non-blocking
|
||||
char buf[1];
|
||||
if (read(server_client_fd, buf, 1) != -1)
|
||||
return 9;
|
||||
if (errno != EAGAIN && errno != EWOULDBLOCK)
|
||||
return 10;
|
||||
|
||||
// Clean up
|
||||
if (close(server_client_fd))
|
||||
return 12;
|
||||
if (close(client_fd))
|
||||
return 13;
|
||||
if (close(server_fd))
|
||||
return 14;
|
||||
|
||||
CheckForMemoryLeaks();
|
||||
}
|
85
test/posix/accept_inherit_nonblock_test.c
Normal file
85
test/posix/accept_inherit_nonblock_test.c
Normal file
|
@ -0,0 +1,85 @@
|
|||
#include <arpa/inet.h>
|
||||
#include <cosmo.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <netinet/in.h>
|
||||
#include <poll.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
void on_signal(int sig) {
|
||||
}
|
||||
|
||||
int main() {
|
||||
|
||||
// Create server socket
|
||||
int server_fd;
|
||||
struct sockaddr_in address;
|
||||
int addrlen = sizeof(address);
|
||||
if ((server_fd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0)) == -1)
|
||||
return 1;
|
||||
address.sin_family = AF_INET;
|
||||
address.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
||||
address.sin_port = 0; // let os assign random port
|
||||
if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)))
|
||||
return 2;
|
||||
if (getsockname(server_fd, (struct sockaddr *)&address,
|
||||
(socklen_t *)&addrlen))
|
||||
return 3;
|
||||
if (listen(server_fd, SOMAXCONN))
|
||||
return 4;
|
||||
|
||||
{
|
||||
// poll server
|
||||
struct pollfd fds[] = {{server_fd, POLLIN | POLLOUT}};
|
||||
int ret = poll(fds, 1, 0);
|
||||
if (ret != 0)
|
||||
return 5;
|
||||
}
|
||||
|
||||
// verify server socket is non-blocking
|
||||
if (accept(server_fd, 0, 0) != -1)
|
||||
return 20;
|
||||
if (errno != EAGAIN && errno != EWOULDBLOCK)
|
||||
return 21;
|
||||
|
||||
// create client socket
|
||||
int client_fd;
|
||||
if ((client_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
|
||||
return 6;
|
||||
if (connect(client_fd, (struct sockaddr *)&address, sizeof(address)))
|
||||
return 7;
|
||||
|
||||
// prevent race condition
|
||||
// impacts platforms like openbsd
|
||||
fcntl(server_fd, F_SETFL, fcntl(server_fd, F_GETFL) & ~O_NONBLOCK);
|
||||
|
||||
// accept client
|
||||
int server_client_fd;
|
||||
if ((server_client_fd = accept(server_fd, 0, 0)) == -1)
|
||||
return 8;
|
||||
|
||||
// check that non-blocking wasn't inherited from listener
|
||||
char buf[1];
|
||||
sigaction(SIGALRM, &(struct sigaction){.sa_handler = on_signal}, 0);
|
||||
ualarm(100000, 0);
|
||||
if (read(server_client_fd, buf, 1) != -1)
|
||||
return 9;
|
||||
if (errno != EINTR)
|
||||
return 10;
|
||||
|
||||
// Clean up
|
||||
if (close(server_client_fd))
|
||||
return 12;
|
||||
if (close(client_fd))
|
||||
return 13;
|
||||
if (close(server_fd))
|
||||
return 14;
|
||||
|
||||
CheckForMemoryLeaks();
|
||||
}
|
94
test/posix/accept_poll_test.c
Normal file
94
test/posix/accept_poll_test.c
Normal file
|
@ -0,0 +1,94 @@
|
|||
#include <arpa/inet.h>
|
||||
#include <cosmo.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <netinet/in.h>
|
||||
#include <poll.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int main() {
|
||||
|
||||
// Create server socket
|
||||
int server_fd;
|
||||
struct sockaddr_in address;
|
||||
int addrlen = sizeof(address);
|
||||
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
|
||||
return 1;
|
||||
address.sin_family = AF_INET;
|
||||
address.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
||||
address.sin_port = 0; // let os assign random port
|
||||
if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)))
|
||||
return 2;
|
||||
if (getsockname(server_fd, (struct sockaddr *)&address,
|
||||
(socklen_t *)&addrlen))
|
||||
return 3;
|
||||
if (listen(server_fd, SOMAXCONN))
|
||||
return 4;
|
||||
|
||||
{
|
||||
// poll server
|
||||
struct pollfd fds[2] = {
|
||||
{server_fd, POLLIN | POLLOUT},
|
||||
};
|
||||
int ret = poll(fds, 1, 0);
|
||||
if (ret != 0)
|
||||
return 5;
|
||||
}
|
||||
|
||||
// create client socket
|
||||
int client_fd;
|
||||
if ((client_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
|
||||
return 6;
|
||||
if (connect(client_fd, (struct sockaddr *)&address, sizeof(address)))
|
||||
return 7;
|
||||
|
||||
{
|
||||
// poll server
|
||||
struct pollfd fds[] = {{server_fd, POLLIN | POLLOUT}};
|
||||
int ret = poll(fds, 1, -1u);
|
||||
if (ret != 1)
|
||||
return 8;
|
||||
if (!(fds[0].revents & POLLIN))
|
||||
return 9;
|
||||
if (fds[0].revents & POLLOUT)
|
||||
return 10;
|
||||
if (fds[0].revents & POLLHUP)
|
||||
return 11;
|
||||
if (fds[0].revents & POLLERR)
|
||||
return 12;
|
||||
}
|
||||
|
||||
{
|
||||
// poll server with invalid thing
|
||||
struct pollfd fds[] = {
|
||||
{server_fd, POLLIN | POLLOUT},
|
||||
{666, POLLIN | POLLOUT},
|
||||
};
|
||||
int ret = poll(fds, 2, -1u);
|
||||
if (ret != 2)
|
||||
return 18;
|
||||
if (!(fds[0].revents & POLLIN))
|
||||
return 19;
|
||||
if (fds[0].revents & POLLOUT)
|
||||
return 20;
|
||||
if (fds[1].revents & POLLIN)
|
||||
return 21;
|
||||
if (fds[1].revents & POLLOUT)
|
||||
return 22;
|
||||
if (!(fds[1].revents & POLLNVAL))
|
||||
return 23;
|
||||
}
|
||||
|
||||
// Clean up
|
||||
if (close(client_fd))
|
||||
return 13;
|
||||
if (close(server_fd))
|
||||
return 14;
|
||||
|
||||
CheckForMemoryLeaks();
|
||||
}
|
75
test/posix/nonblock_pipe2_test.c
Normal file
75
test/posix/nonblock_pipe2_test.c
Normal file
|
@ -0,0 +1,75 @@
|
|||
#include <cosmo.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int main() {
|
||||
int pipefd[2];
|
||||
char buf[PIPE_BUF];
|
||||
char buf2[PIPE_BUF];
|
||||
ssize_t bytes_read;
|
||||
ssize_t bytes_written;
|
||||
|
||||
// Create a pipe
|
||||
if (pipe2(pipefd, O_NONBLOCK) == -1)
|
||||
exit(1);
|
||||
|
||||
// Test 1: Reading from an empty pipe should fail with EAGAIN
|
||||
bytes_read = read(pipefd[0], buf, PIPE_BUF);
|
||||
if (bytes_read != -1 || errno != EAGAIN)
|
||||
exit(4);
|
||||
|
||||
// Test 2: Writing to the pipe
|
||||
bytes_written = write(pipefd[1], buf, PIPE_BUF);
|
||||
if (bytes_written != PIPE_BUF)
|
||||
exit(5);
|
||||
|
||||
// Test 3: Reading from the pipe after writing
|
||||
bytes_read = read(pipefd[0], buf2, PIPE_BUF);
|
||||
if (bytes_read != PIPE_BUF || memcmp(buf, buf2, PIPE_BUF))
|
||||
exit(6);
|
||||
|
||||
// Test 4: Fill the pipe buffer
|
||||
int ch = 10;
|
||||
size_t total_written = 0;
|
||||
for (;;) {
|
||||
memset(buf, ch, PIPE_BUF);
|
||||
bytes_written = write(pipefd[1], buf, PIPE_BUF);
|
||||
if (bytes_written == -1) {
|
||||
if (errno == EAGAIN || errno == EWOULDBLOCK) {
|
||||
break; // Pipe is full
|
||||
} else {
|
||||
exit(7); // Unexpected error
|
||||
}
|
||||
}
|
||||
total_written += bytes_written;
|
||||
}
|
||||
|
||||
// Test 5: Verify that we can read all the data we wrote
|
||||
ch = 10;
|
||||
size_t total_read = 0;
|
||||
while (total_read < total_written) {
|
||||
bytes_read = read(pipefd[0], buf2, PIPE_BUF);
|
||||
if (bytes_read == -1)
|
||||
exit(8);
|
||||
memset(buf, ch, PIPE_BUF);
|
||||
if (memcmp(buf, buf2, PIPE_BUF))
|
||||
exit(9);
|
||||
total_read += bytes_read;
|
||||
}
|
||||
if (total_read != total_written)
|
||||
exit(10);
|
||||
|
||||
// Clean up
|
||||
if (close(pipefd[0]))
|
||||
exit(11);
|
||||
if (close(pipefd[1]))
|
||||
exit(12);
|
||||
|
||||
CheckForMemoryLeaks();
|
||||
}
|
84
test/posix/nonblock_pipe_test.c
Normal file
84
test/posix/nonblock_pipe_test.c
Normal file
|
@ -0,0 +1,84 @@
|
|||
#include <cosmo.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int main() {
|
||||
int pipefd[2];
|
||||
char buf[PIPE_BUF];
|
||||
char buf2[PIPE_BUF];
|
||||
ssize_t bytes_read;
|
||||
ssize_t bytes_written;
|
||||
|
||||
// Create a pipe
|
||||
if (pipe(pipefd) == -1)
|
||||
exit(1);
|
||||
|
||||
// Set O_NONBLOCK flag on the pipe
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
int flags;
|
||||
if ((flags = fcntl(pipefd[i], F_GETFL, 0)) == -1)
|
||||
exit(2);
|
||||
if (fcntl(pipefd[i], F_SETFL, flags | O_NONBLOCK) == -1)
|
||||
exit(3);
|
||||
}
|
||||
|
||||
// Test 1: Reading from an empty pipe should fail with EAGAIN
|
||||
bytes_read = read(pipefd[0], buf, PIPE_BUF);
|
||||
if (bytes_read != -1 || errno != EAGAIN)
|
||||
exit(4);
|
||||
|
||||
// Test 2: Writing to the pipe
|
||||
bytes_written = write(pipefd[1], buf, PIPE_BUF);
|
||||
if (bytes_written != PIPE_BUF)
|
||||
exit(5);
|
||||
|
||||
// Test 3: Reading from the pipe after writing
|
||||
bytes_read = read(pipefd[0], buf2, PIPE_BUF);
|
||||
if (bytes_read != PIPE_BUF || memcmp(buf, buf2, PIPE_BUF))
|
||||
exit(6);
|
||||
|
||||
// Test 4: Fill the pipe buffer
|
||||
int ch = 10;
|
||||
size_t total_written = 0;
|
||||
for (;;) {
|
||||
memset(buf, ch, PIPE_BUF);
|
||||
bytes_written = write(pipefd[1], buf, PIPE_BUF);
|
||||
if (bytes_written == -1) {
|
||||
if (errno == EAGAIN || errno == EWOULDBLOCK) {
|
||||
break; // Pipe is full
|
||||
} else {
|
||||
exit(7); // Unexpected error
|
||||
}
|
||||
}
|
||||
total_written += bytes_written;
|
||||
}
|
||||
|
||||
// Test 5: Verify that we can read all the data we wrote
|
||||
ch = 10;
|
||||
size_t total_read = 0;
|
||||
while (total_read < total_written) {
|
||||
bytes_read = read(pipefd[0], buf2, PIPE_BUF);
|
||||
if (bytes_read == -1)
|
||||
exit(8);
|
||||
memset(buf, ch, PIPE_BUF);
|
||||
if (memcmp(buf, buf2, PIPE_BUF))
|
||||
exit(9);
|
||||
total_read += bytes_read;
|
||||
}
|
||||
if (total_read != total_written)
|
||||
exit(10);
|
||||
|
||||
// Clean up
|
||||
if (close(pipefd[0]))
|
||||
exit(11);
|
||||
if (close(pipefd[1]))
|
||||
exit(12);
|
||||
|
||||
CheckForMemoryLeaks();
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue