mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-03-03 07:29:23 +00:00
Fix bugs in poll(), select(), ppoll(), and pselect()
poll() and select() now delegate to ppoll() and pselect() for assurances that both polyfill implementations are correct and well-tested. Poll now polyfills XNU and BSD quirks re: the hanndling of POLLNVAL and the other similar status flags. This change resolves a misunderstanding concerning how select(exceptfds) is intended to map to POLPRI. We now use E2BIG for bouncing requests that exceed the 64 handle limit on Windows. With pipes and consoles on Windows our poll impl will now report POLLHUP correctly. Issues with Windows path generation have been fixed. For example, it was problematic on Windows to say: posix_spawn_file_actions_addchdir_np("/") due to the need to un-UNC paths in some additional places. Calling fstat on UNC style volume path handles will now work. posix_spawn now supports simulating the opening of /dev/null and other special paths on Windows. Cosmopolitan no longer defines epoll(). I think wepoll is a nice project for using epoll() on Windows socket handles. However we need generalized file descriptor support to make epoll() for Windows work well enough for inclusion in a C library. It's also not worth having epoll() if we can't get it to work on XNU and BSD OSes which provide different abstractions. Even epoll() on Linux isn't that great of an abstraction since it's full of footguns. Last time I tried to get it to be useful I had little luck. Considering how long it took to get poll() and select() to be consistent across platforms, we really have no business claiming to have epoll too. While it'd be nice to have fully implemented, the only software that use epoll() are event i/o libraries used by things like nodejs. Event i/o is not the best paradigm for handling i/o; threads make so much more sense.
This commit is contained in:
parent
39e7f24947
commit
2ec413b5a9
27 changed files with 664 additions and 2132 deletions
316
examples/spawn.c
316
examples/spawn.c
|
@ -86,147 +86,281 @@
|
||||||
// performance is critical.
|
// performance is critical.
|
||||||
|
|
||||||
#define _GNU_SOURCE
|
#define _GNU_SOURCE
|
||||||
|
#include <errno.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
#include <poll.h>
|
||||||
|
#include <signal.h>
|
||||||
#include <spawn.h>
|
#include <spawn.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <sys/select.h>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#define max(X, Y) ((Y) < (X) ? (X) : (Y))
|
||||||
|
|
||||||
|
#define USE_SELECT 0 // want poll() or select()? they both work great
|
||||||
|
|
||||||
#define PIPE_READ 0
|
#define PIPE_READ 0
|
||||||
#define PIPE_WRITE 1
|
#define PIPE_WRITE 1
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
pid_t pid;
|
errno_t err;
|
||||||
int status, ret;
|
|
||||||
posix_spawnattr_t attr;
|
|
||||||
posix_spawn_file_actions_t actions;
|
|
||||||
char *const argv[] = {"ls", "--dired", NULL};
|
|
||||||
int pipe_stdout[2], pipe_stderr[2];
|
|
||||||
|
|
||||||
// Initialize file actions
|
// Create spawn attributes object.
|
||||||
ret = posix_spawnattr_init(&attr);
|
posix_spawnattr_t attr;
|
||||||
if (ret != 0) {
|
err = posix_spawnattr_init(&attr);
|
||||||
fprintf(stderr, "posix_spawnattr_init failed: %s\n", strerror(ret));
|
if (err != 0) {
|
||||||
return 1;
|
fprintf(stderr, "posix_spawnattr_init failed: %s\n", strerror(err));
|
||||||
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Explicitly request vfork() from posix_spawn() implementation
|
// Explicitly request vfork() from posix_spawn() implementation.
|
||||||
//
|
//
|
||||||
// This is currently the default for Cosmopolitan Libc, however you
|
// This is currently the default for Cosmopolitan Libc, however you
|
||||||
// may want to set this anyway, for portability with other platforms.
|
// may want to set this anyway, for portability with other platforms.
|
||||||
// Please note that vfork() isn't officially specified by POSIX, so
|
// Please note that vfork() isn't officially specified by POSIX, so
|
||||||
// portable code may want to omit this and just use the default.
|
// portable code may want to omit this and just use the default.
|
||||||
ret = posix_spawnattr_setflags(&attr, POSIX_SPAWN_USEVFORK);
|
err = posix_spawnattr_setflags(&attr, POSIX_SPAWN_USEVFORK);
|
||||||
if (ret != 0) {
|
if (err != 0) {
|
||||||
fprintf(stderr, "posix_spawnattr_setflags failed: %s\n", strerror(ret));
|
fprintf(stderr, "posix_spawnattr_setflags: %s\n", strerror(err));
|
||||||
return 1;
|
exit(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize file actions
|
// Create file actions object.
|
||||||
ret = posix_spawn_file_actions_init(&actions);
|
posix_spawn_file_actions_t actions;
|
||||||
if (ret != 0) {
|
err = posix_spawn_file_actions_init(&actions);
|
||||||
fprintf(stderr, "posix_spawn_file_actions_init failed: %s\n",
|
if (err != 0) {
|
||||||
strerror(ret));
|
fprintf(stderr, "posix_spawn_file_actions_init: %s\n", strerror(err));
|
||||||
return 1;
|
exit(3);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Change directory to $HOME
|
// Change directory to root directory in child process.
|
||||||
ret = posix_spawn_file_actions_addchdir_np(&actions, getenv("HOME"));
|
err = posix_spawn_file_actions_addchdir_np(&actions, "/");
|
||||||
if (ret != 0) {
|
if (err != 0) {
|
||||||
fprintf(stderr, "posix_spawn_file_actions_addchdir_np failed: %s\n",
|
fprintf(stderr, "posix_spawn_file_actions_addchdir_np: %s\n",
|
||||||
strerror(ret));
|
strerror(err));
|
||||||
return 1;
|
exit(4);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create pipes for stdout and stderr
|
// Disable stdin in child process.
|
||||||
if (pipe(pipe_stdout) == -1 || pipe(pipe_stderr) == -1) {
|
//
|
||||||
|
// By default, if you launch this example in your terminal, then child
|
||||||
|
// processes can read from your teletypewriter's keyboard too. You can
|
||||||
|
// avoid this by assigning /dev/null to standard input so if the child
|
||||||
|
// tries to read input, read() will return zero, indicating eof.
|
||||||
|
if ((err = posix_spawn_file_actions_addopen(&actions, STDIN_FILENO,
|
||||||
|
"/dev/null", O_RDONLY, 0644))) {
|
||||||
|
fprintf(stderr, "posix_spawn_file_actions_addopen: %s\n", strerror(err));
|
||||||
|
exit(5);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create pipes for stdout and stderr.
|
||||||
|
//
|
||||||
|
// Using O_DIRECT puts the pipe in message mode. This way we have some
|
||||||
|
// visibility into how the child process is using write(). It can also
|
||||||
|
// help ensure that logged lines won't be chopped up here, which could
|
||||||
|
// happen more frequently on platforms like Windows, which is somewhat
|
||||||
|
// less sophisticated than Linux with how it performs buffering.
|
||||||
|
//
|
||||||
|
// You can also specify O_CLOEXEC, which is a nice touch that lets you
|
||||||
|
// avoid needing to call posix_spawn_file_actions_addclose() later on.
|
||||||
|
// That's because all file descriptors are inherited by child programs
|
||||||
|
// by default. This is even the case with Cosmopolitan Libc on Windows
|
||||||
|
//
|
||||||
|
// XXX: We assume that stdin/stdout/stderr exist in this process. It's
|
||||||
|
// possible for a rogue parent process to launch this example, in
|
||||||
|
// a way where the following spawn logic will break.
|
||||||
|
int pipe_stdout[2];
|
||||||
|
int pipe_stderr[2];
|
||||||
|
if (pipe2(pipe_stdout, O_DIRECT) == -1 ||
|
||||||
|
pipe2(pipe_stderr, O_DIRECT) == -1) {
|
||||||
perror("pipe");
|
perror("pipe");
|
||||||
return 1;
|
exit(6);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Redirect child's stdout to pipe
|
// Redirect child's stdout/stderr to pipes
|
||||||
ret = posix_spawn_file_actions_adddup2(&actions, pipe_stdout[PIPE_WRITE],
|
if ((err = posix_spawn_file_actions_adddup2(&actions, pipe_stdout[PIPE_WRITE],
|
||||||
STDOUT_FILENO);
|
STDOUT_FILENO)) ||
|
||||||
if (ret != 0) {
|
(err = posix_spawn_file_actions_adddup2(&actions, pipe_stderr[PIPE_WRITE],
|
||||||
fprintf(stderr, "posix_spawn_file_actions_adddup2 (stdout) failed: %s\n",
|
STDERR_FILENO))) {
|
||||||
strerror(ret));
|
fprintf(stderr, "posix_spawn_file_actions_adddup2: %s\n", strerror(err));
|
||||||
return 1;
|
exit(7);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Redirect child's stderr to pipe
|
// Close unwanted write ends of pipes in the child process
|
||||||
ret = posix_spawn_file_actions_adddup2(&actions, pipe_stderr[PIPE_WRITE],
|
if ((err = posix_spawn_file_actions_addclose(&actions,
|
||||||
STDERR_FILENO);
|
pipe_stdout[PIPE_READ])) ||
|
||||||
if (ret != 0) {
|
(err = posix_spawn_file_actions_addclose(&actions,
|
||||||
fprintf(stderr, "posix_spawn_file_actions_adddup2 (stderr) failed: %s\n",
|
pipe_stderr[PIPE_READ]))) {
|
||||||
strerror(ret));
|
fprintf(stderr, "posix_spawn_file_actions_addclose: %s\n", strerror(err));
|
||||||
return 1;
|
exit(8);
|
||||||
}
|
};
|
||||||
|
|
||||||
// Close unused write ends of pipes in the child process
|
// Asynchronously launch the child process.
|
||||||
ret = posix_spawn_file_actions_addclose(&actions, pipe_stdout[PIPE_READ]);
|
pid_t pid;
|
||||||
if (ret != 0) {
|
char *const argv[] = {"ls", "--dired", NULL};
|
||||||
fprintf(stderr,
|
printf("** Launching `ls --dired` in root directory\n");
|
||||||
"posix_spawn_file_actions_addclose (stdout read) failed: %s\n",
|
err = posix_spawnp(&pid, argv[0], &actions, NULL, argv, NULL);
|
||||||
strerror(ret));
|
if (err) {
|
||||||
return 1;
|
fprintf(stderr, "posix_spawn: %s\n", strerror(err));
|
||||||
}
|
exit(9);
|
||||||
ret = posix_spawn_file_actions_addclose(&actions, pipe_stderr[PIPE_READ]);
|
|
||||||
if (ret != 0) {
|
|
||||||
fprintf(stderr,
|
|
||||||
"posix_spawn_file_actions_addclose (stderr read) failed: %s\n",
|
|
||||||
strerror(ret));
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Spawn the child process
|
|
||||||
ret = posix_spawnp(&pid, "ls", &actions, NULL, argv, NULL);
|
|
||||||
if (ret != 0) {
|
|
||||||
fprintf(stderr, "posix_spawn failed: %s\n", strerror(ret));
|
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close unused write ends of pipes in the parent process
|
// Close unused write ends of pipes in the parent process
|
||||||
close(pipe_stdout[PIPE_WRITE]);
|
close(pipe_stdout[PIPE_WRITE]);
|
||||||
close(pipe_stderr[PIPE_WRITE]);
|
close(pipe_stderr[PIPE_WRITE]);
|
||||||
|
|
||||||
// Read and print output from child process
|
// we need poll() or select() because we're multiplexing output
|
||||||
char buffer[4096];
|
// both poll() and select() work across all supported platforms
|
||||||
ssize_t bytes_read;
|
#if USE_SELECT
|
||||||
|
// Relay output from child process using select()
|
||||||
printf("Stdout from child process:\n");
|
char buffer[512];
|
||||||
while ((bytes_read = read(pipe_stdout[PIPE_READ], buffer, sizeof(buffer))) >
|
ssize_t got_stdout = 1;
|
||||||
0) {
|
ssize_t got_stderr = 1;
|
||||||
write(STDOUT_FILENO, buffer, bytes_read);
|
while (got_stdout > 0 || got_stderr > 0) {
|
||||||
|
fd_set rfds;
|
||||||
|
FD_ZERO(&rfds);
|
||||||
|
if (got_stdout > 0)
|
||||||
|
FD_SET(pipe_stdout[PIPE_READ], &rfds);
|
||||||
|
if (got_stderr > 0)
|
||||||
|
FD_SET(pipe_stderr[PIPE_READ], &rfds);
|
||||||
|
int nfds = max(pipe_stdout[PIPE_READ], pipe_stderr[PIPE_READ]) + 1;
|
||||||
|
if (select(nfds, &rfds, 0, 0, 0) == -1) {
|
||||||
|
perror("select");
|
||||||
|
exit(10);
|
||||||
|
}
|
||||||
|
if (FD_ISSET(pipe_stdout[PIPE_READ], &rfds)) {
|
||||||
|
got_stdout = read(pipe_stdout[PIPE_READ], buffer, sizeof(buffer));
|
||||||
|
printf("\n");
|
||||||
|
if (got_stdout > 0) {
|
||||||
|
printf("** Got stdout from child process:\n");
|
||||||
|
fflush(stdout);
|
||||||
|
write(STDOUT_FILENO, buffer, got_stdout);
|
||||||
|
} else if (!got_stdout) {
|
||||||
|
printf("** Got stdout EOF from child process\n");
|
||||||
|
} else {
|
||||||
|
printf("** Got stdout read() error from child process: %s\n",
|
||||||
|
strerror(errno));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (FD_ISSET(pipe_stderr[PIPE_READ], &rfds)) {
|
||||||
|
got_stderr = read(pipe_stderr[PIPE_READ], buffer, sizeof(buffer));
|
||||||
|
printf("\n");
|
||||||
|
if (got_stderr > 0) {
|
||||||
|
printf("** Got stderr from child process:\n");
|
||||||
|
fflush(stdout);
|
||||||
|
write(STDOUT_FILENO, buffer, got_stderr);
|
||||||
|
} else if (!got_stderr) {
|
||||||
|
printf("** Got stderr EOF from child process\n");
|
||||||
|
} else {
|
||||||
|
printf("** Got stderr read() error from child process: %s\n",
|
||||||
|
strerror(errno));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("\nStderr from child process:\n");
|
#else
|
||||||
while ((bytes_read = read(pipe_stderr[PIPE_READ], buffer, sizeof(buffer))) >
|
// Relay output from child process using poll()
|
||||||
0) {
|
char buffer[512];
|
||||||
write(STDERR_FILENO, buffer, bytes_read);
|
ssize_t got_stdout = 1;
|
||||||
|
ssize_t got_stderr = 1;
|
||||||
|
while (got_stdout > 0 || got_stderr > 0) {
|
||||||
|
struct pollfd fds[2];
|
||||||
|
fds[0].fd = got_stdout > 0 ? pipe_stdout[PIPE_READ] : -1;
|
||||||
|
fds[0].events = POLLIN; // POLLHUP, POLLNVAL, and POLLERR are implied
|
||||||
|
fds[1].fd = got_stderr > 0 ? pipe_stderr[PIPE_READ] : -1;
|
||||||
|
fds[1].events = POLLIN; // POLLHUP, POLLNVAL, and POLLERR are implied
|
||||||
|
if (poll(fds, 2, -1) == -1) {
|
||||||
|
perror("select");
|
||||||
|
exit(10);
|
||||||
|
}
|
||||||
|
if (fds[0].revents) {
|
||||||
|
printf("\n");
|
||||||
|
if (fds[0].revents & POLLIN)
|
||||||
|
printf("** Got POLLIN on stdout from child process\n");
|
||||||
|
if (fds[0].revents & POLLHUP)
|
||||||
|
printf("** Got POLLHUP on stdout from child process\n");
|
||||||
|
if (fds[0].revents & POLLERR)
|
||||||
|
printf("** Got POLLERR on stdout from child process\n");
|
||||||
|
if (fds[0].revents & POLLNVAL)
|
||||||
|
printf("** Got POLLNVAL on stdout from child process\n");
|
||||||
|
got_stdout = read(pipe_stdout[PIPE_READ], buffer, sizeof(buffer));
|
||||||
|
if (got_stdout > 0) {
|
||||||
|
printf("** Got stdout from child process:\n");
|
||||||
|
fflush(stdout);
|
||||||
|
write(STDOUT_FILENO, buffer, got_stdout);
|
||||||
|
} else if (!got_stdout) {
|
||||||
|
printf("** Got stdout EOF from child process\n");
|
||||||
|
} else {
|
||||||
|
printf("** Got stdout read() error from child process: %s\n",
|
||||||
|
strerror(errno));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (fds[1].revents) {
|
||||||
|
printf("\n");
|
||||||
|
if (fds[1].revents & POLLIN)
|
||||||
|
printf("** Got POLLIN on stderr from child process\n");
|
||||||
|
if (fds[1].revents & POLLHUP)
|
||||||
|
printf("** Got POLLHUP on stderr from child process\n");
|
||||||
|
if (fds[1].revents & POLLERR)
|
||||||
|
printf("** Got POLLERR on stderr from child process\n");
|
||||||
|
if (fds[1].revents & POLLNVAL)
|
||||||
|
printf("** Got POLLNVAL on stderr from child process\n");
|
||||||
|
got_stderr = read(pipe_stderr[PIPE_READ], buffer, sizeof(buffer));
|
||||||
|
if (got_stderr > 0) {
|
||||||
|
printf("** Got stderr from child process:\n");
|
||||||
|
fflush(stdout);
|
||||||
|
write(STDOUT_FILENO, buffer, got_stderr);
|
||||||
|
} else if (!got_stderr) {
|
||||||
|
printf("** Got stderr EOF from child process\n");
|
||||||
|
} else {
|
||||||
|
printf("** Got stderr read() error from child process: %s\n",
|
||||||
|
strerror(errno));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// Wait for the child process to complete
|
// Wait for child process to die.
|
||||||
if (waitpid(pid, &status, 0) == -1) {
|
int wait_status;
|
||||||
|
if (waitpid(pid, &wait_status, 0) == -1) {
|
||||||
perror("waitpid");
|
perror("waitpid");
|
||||||
return 1;
|
exit(11);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clean up
|
// Clean up resources.
|
||||||
posix_spawn_file_actions_destroy(&actions);
|
posix_spawn_file_actions_destroy(&actions);
|
||||||
posix_spawnattr_destroy(&attr);
|
posix_spawnattr_destroy(&attr);
|
||||||
close(pipe_stdout[PIPE_READ]);
|
close(pipe_stdout[PIPE_READ]);
|
||||||
close(pipe_stderr[PIPE_READ]);
|
close(pipe_stderr[PIPE_READ]);
|
||||||
|
|
||||||
if (WIFEXITED(status)) {
|
// Report wait status.
|
||||||
printf("Child process exited with status %d\n", WEXITSTATUS(status));
|
//
|
||||||
} else if (WIFSIGNALED(status)) {
|
// When a process dies, it's almost always due to calling _Exit() or
|
||||||
printf("Child process terminated with signal %s\n",
|
// being killed due to an unhandled signal. On both UNIX and Windows
|
||||||
strsignal(WTERMSIG(status)));
|
// this information will be propagated to the parent. That status is
|
||||||
|
// able to be propagated to the parent of this process too.
|
||||||
|
printf("\n");
|
||||||
|
if (WIFEXITED(wait_status)) {
|
||||||
|
printf("** Child process exited with exit code %d\n",
|
||||||
|
WEXITSTATUS(wait_status));
|
||||||
|
exit(WEXITSTATUS(wait_status));
|
||||||
|
} else if (WIFSIGNALED(wait_status)) {
|
||||||
|
printf("** Child process terminated with signal %s\n",
|
||||||
|
strsignal(WTERMSIG(wait_status)));
|
||||||
|
fflush(stdout);
|
||||||
|
sigset_t sm;
|
||||||
|
sigemptyset(&sm);
|
||||||
|
sigaddset(&sm, WTERMSIG(wait_status));
|
||||||
|
sigprocmask(SIG_UNBLOCK, &sm, 0);
|
||||||
|
signal(SIGABRT, SIG_DFL);
|
||||||
|
raise(WTERMSIG(wait_status));
|
||||||
|
exit(128 + WTERMSIG(wait_status));
|
||||||
} else {
|
} else {
|
||||||
printf("Child process did not exit normally\n");
|
printf("** Child process exited weirdly with wait status 0x%08x\n",
|
||||||
|
wait_status);
|
||||||
|
exit(12);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,11 +52,6 @@ textwindows int sys_close_nt(int fd, int fildes) {
|
||||||
FlushFileBuffers(f->handle);
|
FlushFileBuffers(f->handle);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case kFdEpoll:
|
|
||||||
if (_weaken(sys_close_epoll_nt)) {
|
|
||||||
return _weaken(sys_close_epoll_nt)(fd);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case kFdSocket:
|
case kFdSocket:
|
||||||
if (_weaken(sys_closesocket_nt)) {
|
if (_weaken(sys_closesocket_nt)) {
|
||||||
return _weaken(sys_closesocket_nt)(g_fds.p + fd);
|
return _weaken(sys_closesocket_nt)(g_fds.p + fd);
|
||||||
|
|
|
@ -20,12 +20,12 @@
|
||||||
#include "libc/calls/calls.h"
|
#include "libc/calls/calls.h"
|
||||||
#include "libc/calls/internal.h"
|
#include "libc/calls/internal.h"
|
||||||
#include "libc/calls/state.internal.h"
|
#include "libc/calls/state.internal.h"
|
||||||
#include "libc/intrin/fds.h"
|
|
||||||
#include "libc/calls/struct/sigset.internal.h"
|
#include "libc/calls/struct/sigset.internal.h"
|
||||||
#include "libc/calls/syscall-nt.internal.h"
|
#include "libc/calls/syscall-nt.internal.h"
|
||||||
#include "libc/calls/syscall-sysv.internal.h"
|
#include "libc/calls/syscall-sysv.internal.h"
|
||||||
#include "libc/dce.h"
|
#include "libc/dce.h"
|
||||||
#include "libc/errno.h"
|
#include "libc/errno.h"
|
||||||
|
#include "libc/intrin/fds.h"
|
||||||
#include "libc/intrin/kprintf.h"
|
#include "libc/intrin/kprintf.h"
|
||||||
#include "libc/intrin/strace.h"
|
#include "libc/intrin/strace.h"
|
||||||
#include "libc/intrin/weaken.h"
|
#include "libc/intrin/weaken.h"
|
||||||
|
@ -74,7 +74,6 @@ static int close_impl(int fd) {
|
||||||
* - openat()
|
* - openat()
|
||||||
* - socket()
|
* - socket()
|
||||||
* - accept()
|
* - accept()
|
||||||
* - epoll_create()
|
|
||||||
* - landlock_create_ruleset()
|
* - landlock_create_ruleset()
|
||||||
*
|
*
|
||||||
* This function should never be reattempted if an error is returned;
|
* This function should never be reattempted if an error is returned;
|
||||||
|
|
|
@ -19,13 +19,13 @@
|
||||||
#include "libc/assert.h"
|
#include "libc/assert.h"
|
||||||
#include "libc/calls/calls.h"
|
#include "libc/calls/calls.h"
|
||||||
#include "libc/calls/internal.h"
|
#include "libc/calls/internal.h"
|
||||||
#include "libc/intrin/fds.h"
|
|
||||||
#include "libc/calls/struct/stat.h"
|
#include "libc/calls/struct/stat.h"
|
||||||
#include "libc/calls/struct/stat.internal.h"
|
#include "libc/calls/struct/stat.internal.h"
|
||||||
#include "libc/calls/syscall_support-nt.internal.h"
|
#include "libc/calls/syscall_support-nt.internal.h"
|
||||||
#include "libc/fmt/wintime.internal.h"
|
#include "libc/fmt/wintime.internal.h"
|
||||||
#include "libc/intrin/atomic.h"
|
#include "libc/intrin/atomic.h"
|
||||||
#include "libc/intrin/bsr.h"
|
#include "libc/intrin/bsr.h"
|
||||||
|
#include "libc/intrin/fds.h"
|
||||||
#include "libc/intrin/strace.h"
|
#include "libc/intrin/strace.h"
|
||||||
#include "libc/macros.h"
|
#include "libc/macros.h"
|
||||||
#include "libc/mem/alloca.h"
|
#include "libc/mem/alloca.h"
|
||||||
|
@ -119,7 +119,6 @@ textwindows int sys_fstat_nt_handle(int64_t handle, const char16_t *path,
|
||||||
|
|
||||||
// Always set st_blksize to avoid divide by zero issues.
|
// Always set st_blksize to avoid divide by zero issues.
|
||||||
// The Linux kernel sets this for /dev/tty and similar too.
|
// The Linux kernel sets this for /dev/tty and similar too.
|
||||||
// TODO(jart): GetVolumeInformationByHandle?
|
|
||||||
st.st_blksize = 4096;
|
st.st_blksize = 4096;
|
||||||
st.st_gid = st.st_uid = sys_getuid_nt();
|
st.st_gid = st.st_uid = sys_getuid_nt();
|
||||||
|
|
||||||
|
@ -141,59 +140,66 @@ textwindows int sys_fstat_nt_handle(int64_t handle, const char16_t *path,
|
||||||
break;
|
break;
|
||||||
case kNtFileTypeDisk: {
|
case kNtFileTypeDisk: {
|
||||||
struct NtByHandleFileInformation wst;
|
struct NtByHandleFileInformation wst;
|
||||||
if (!GetFileInformationByHandle(handle, &wst)) {
|
if (GetFileInformationByHandle(handle, &wst)) {
|
||||||
return __winerr();
|
st.st_mode = 0444 & ~umask;
|
||||||
}
|
if ((wst.dwFileAttributes & kNtFileAttributeDirectory) ||
|
||||||
st.st_mode = 0444 & ~umask;
|
IsWindowsExecutable(handle, path)) {
|
||||||
if ((wst.dwFileAttributes & kNtFileAttributeDirectory) ||
|
st.st_mode |= 0111 & ~umask;
|
||||||
IsWindowsExecutable(handle, path)) {
|
|
||||||
st.st_mode |= 0111 & ~umask;
|
|
||||||
}
|
|
||||||
st.st_flags = wst.dwFileAttributes;
|
|
||||||
if (!(wst.dwFileAttributes & kNtFileAttributeReadonly)) {
|
|
||||||
st.st_mode |= 0222 & ~umask;
|
|
||||||
}
|
|
||||||
if (wst.dwFileAttributes & kNtFileAttributeReparsePoint) {
|
|
||||||
st.st_mode |= S_IFLNK;
|
|
||||||
} else if (wst.dwFileAttributes & kNtFileAttributeDirectory) {
|
|
||||||
st.st_mode |= S_IFDIR;
|
|
||||||
} else {
|
|
||||||
st.st_mode |= S_IFREG;
|
|
||||||
}
|
|
||||||
st.st_atim = FileTimeToTimeSpec(wst.ftLastAccessFileTime);
|
|
||||||
st.st_mtim = FileTimeToTimeSpec(wst.ftLastWriteFileTime);
|
|
||||||
st.st_birthtim = FileTimeToTimeSpec(wst.ftCreationFileTime);
|
|
||||||
// compute time of last status change
|
|
||||||
if (timespec_cmp(st.st_atim, st.st_mtim) > 0) {
|
|
||||||
st.st_ctim = st.st_atim;
|
|
||||||
} else {
|
|
||||||
st.st_ctim = st.st_mtim;
|
|
||||||
}
|
|
||||||
st.st_size = (wst.nFileSizeHigh + 0ull) << 32 | wst.nFileSizeLow;
|
|
||||||
st.st_dev = wst.dwVolumeSerialNumber;
|
|
||||||
st.st_ino = (wst.nFileIndexHigh + 0ull) << 32 | wst.nFileIndexLow;
|
|
||||||
st.st_nlink = wst.nNumberOfLinks;
|
|
||||||
if (S_ISLNK(st.st_mode)) {
|
|
||||||
if (!st.st_size) {
|
|
||||||
long size = GetSizeOfReparsePoint(handle);
|
|
||||||
if (size == -1)
|
|
||||||
return -1;
|
|
||||||
st.st_size = size;
|
|
||||||
}
|
}
|
||||||
} else {
|
st.st_flags = wst.dwFileAttributes;
|
||||||
// st_size = uncompressed size
|
if (!(wst.dwFileAttributes & kNtFileAttributeReadonly)) {
|
||||||
// st_blocks*512 = physical size
|
st.st_mode |= 0222 & ~umask;
|
||||||
uint64_t physicalsize;
|
}
|
||||||
struct NtFileCompressionInfo fci;
|
if (wst.dwFileAttributes & kNtFileAttributeReparsePoint) {
|
||||||
if (!(wst.dwFileAttributes &
|
st.st_mode |= S_IFLNK;
|
||||||
(kNtFileAttributeDirectory | kNtFileAttributeReparsePoint)) &&
|
} else if (wst.dwFileAttributes & kNtFileAttributeDirectory) {
|
||||||
GetFileInformationByHandleEx(handle, kNtFileCompressionInfo, &fci,
|
st.st_mode |= S_IFDIR;
|
||||||
sizeof(fci))) {
|
|
||||||
physicalsize = fci.CompressedFileSize;
|
|
||||||
} else {
|
} else {
|
||||||
physicalsize = st.st_size;
|
st.st_mode |= S_IFREG;
|
||||||
}
|
}
|
||||||
st.st_blocks = ROUNDUP(physicalsize, st.st_blksize) / 512;
|
st.st_atim = FileTimeToTimeSpec(wst.ftLastAccessFileTime);
|
||||||
|
st.st_mtim = FileTimeToTimeSpec(wst.ftLastWriteFileTime);
|
||||||
|
st.st_birthtim = FileTimeToTimeSpec(wst.ftCreationFileTime);
|
||||||
|
// compute time of last status change
|
||||||
|
if (timespec_cmp(st.st_atim, st.st_mtim) > 0) {
|
||||||
|
st.st_ctim = st.st_atim;
|
||||||
|
} else {
|
||||||
|
st.st_ctim = st.st_mtim;
|
||||||
|
}
|
||||||
|
st.st_size = (wst.nFileSizeHigh + 0ull) << 32 | wst.nFileSizeLow;
|
||||||
|
st.st_dev = wst.dwVolumeSerialNumber;
|
||||||
|
st.st_ino = (wst.nFileIndexHigh + 0ull) << 32 | wst.nFileIndexLow;
|
||||||
|
st.st_nlink = wst.nNumberOfLinks;
|
||||||
|
if (S_ISLNK(st.st_mode)) {
|
||||||
|
if (!st.st_size) {
|
||||||
|
long size = GetSizeOfReparsePoint(handle);
|
||||||
|
if (size == -1)
|
||||||
|
return -1;
|
||||||
|
st.st_size = size;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// st_size = uncompressed size
|
||||||
|
// st_blocks*512 = physical size
|
||||||
|
uint64_t physicalsize;
|
||||||
|
struct NtFileCompressionInfo fci;
|
||||||
|
if (!(wst.dwFileAttributes &
|
||||||
|
(kNtFileAttributeDirectory | kNtFileAttributeReparsePoint)) &&
|
||||||
|
GetFileInformationByHandleEx(handle, kNtFileCompressionInfo, &fci,
|
||||||
|
sizeof(fci))) {
|
||||||
|
physicalsize = fci.CompressedFileSize;
|
||||||
|
} else {
|
||||||
|
physicalsize = st.st_size;
|
||||||
|
}
|
||||||
|
st.st_blocks = ROUNDUP(physicalsize, st.st_blksize) / 512;
|
||||||
|
}
|
||||||
|
} else if (GetVolumeInformationByHandle(
|
||||||
|
handle, 0, 0, &wst.dwVolumeSerialNumber, 0, 0, 0, 0)) {
|
||||||
|
st.st_dev = wst.dwVolumeSerialNumber;
|
||||||
|
st.st_mode = S_IFDIR | (0444 & ~umask);
|
||||||
|
} else {
|
||||||
|
// both GetFileInformationByHandle and
|
||||||
|
// GetVolumeInformationByHandle failed
|
||||||
|
return __winerr();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,8 +62,18 @@ static textwindows int __mkntpathath_impl(int64_t dirhand, const char *path,
|
||||||
dir[dirlen] = u'\\';
|
dir[dirlen] = u'\\';
|
||||||
memcpy(dir + dirlen + 1, file, (filelen + 1) * sizeof(char16_t));
|
memcpy(dir + dirlen + 1, file, (filelen + 1) * sizeof(char16_t));
|
||||||
memcpy(file, dir, ((n = dirlen + 1 + filelen) + 1) * sizeof(char16_t));
|
memcpy(file, dir, ((n = dirlen + 1 + filelen) + 1) * sizeof(char16_t));
|
||||||
int res = __normntpath(file, n);
|
n = __normntpath(file, n);
|
||||||
return res;
|
|
||||||
|
// UNC paths break some things when they are not needed.
|
||||||
|
if (n > 4 && n < 260 && //
|
||||||
|
file[0] == '\\' && //
|
||||||
|
file[1] == '\\' && //
|
||||||
|
file[2] == '?' && //
|
||||||
|
file[3] == '\\') {
|
||||||
|
memmove(file, file + 4, (n - 4 + 1) * sizeof(char16_t));
|
||||||
|
}
|
||||||
|
|
||||||
|
return n;
|
||||||
} else {
|
} else {
|
||||||
return filelen;
|
return filelen;
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,6 +56,19 @@
|
||||||
|
|
||||||
#define POLL_INTERVAL_MS 10
|
#define POLL_INTERVAL_MS 10
|
||||||
|
|
||||||
|
// <sync libc/sysv/consts.sh>
|
||||||
|
#define POLLERR_ 0x0001 // implied in events
|
||||||
|
#define POLLHUP_ 0x0002 // implied in events
|
||||||
|
#define POLLNVAL_ 0x0004 // implied in events
|
||||||
|
#define POLLIN_ 0x0300
|
||||||
|
#define POLLRDNORM_ 0x0100
|
||||||
|
#define POLLRDBAND_ 0x0200
|
||||||
|
#define POLLOUT_ 0x0010
|
||||||
|
#define POLLWRNORM_ 0x0010
|
||||||
|
#define POLLWRBAND_ 0x0020 // MSDN undocumented
|
||||||
|
#define POLLPRI_ 0x0400 // MSDN unsupported
|
||||||
|
// </sync libc/sysv/consts.sh>
|
||||||
|
|
||||||
// Polls on the New Technology.
|
// Polls on the New Technology.
|
||||||
//
|
//
|
||||||
// This function is used to implement poll() and select(). You may poll
|
// This function is used to implement poll() and select(). You may poll
|
||||||
|
@ -86,16 +99,16 @@ static textwindows int sys_poll_nt_impl(struct pollfd *fds, uint64_t nfds,
|
||||||
if (__isfdopen(fds[i].fd)) {
|
if (__isfdopen(fds[i].fd)) {
|
||||||
if (__isfdkind(fds[i].fd, kFdSocket)) {
|
if (__isfdkind(fds[i].fd, kFdSocket)) {
|
||||||
if (sn < ARRAYLEN(sockfds)) {
|
if (sn < ARRAYLEN(sockfds)) {
|
||||||
// the magnums for POLLIN/OUT/PRI on NT include the other ones too
|
// WSAPoll whines if we pass POLLNVAL, POLLHUP, or POLLERR.
|
||||||
// we need to clear ones like POLLNVAL or else WSAPoll shall whine
|
|
||||||
sockindices[sn] = i;
|
sockindices[sn] = i;
|
||||||
sockfds[sn].handle = g_fds.p[fds[i].fd].handle;
|
sockfds[sn].handle = g_fds.p[fds[i].fd].handle;
|
||||||
sockfds[sn].events = fds[i].events & (POLLPRI | POLLIN | POLLOUT);
|
sockfds[sn].events =
|
||||||
|
fds[i].events & (POLLRDNORM_ | POLLRDBAND_ | POLLWRNORM_);
|
||||||
sockfds[sn].revents = 0;
|
sockfds[sn].revents = 0;
|
||||||
++sn;
|
++sn;
|
||||||
} else {
|
} else {
|
||||||
// too many socket fds
|
// too many socket fds
|
||||||
rc = enomem();
|
rc = e2big();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else if (pn < ARRAYLEN(pipefds)) {
|
} else if (pn < ARRAYLEN(pipefds)) {
|
||||||
|
@ -105,13 +118,13 @@ static textwindows int sys_poll_nt_impl(struct pollfd *fds, uint64_t nfds,
|
||||||
pipefds[pn].revents = 0;
|
pipefds[pn].revents = 0;
|
||||||
switch (g_fds.p[fds[i].fd].flags & O_ACCMODE) {
|
switch (g_fds.p[fds[i].fd].flags & O_ACCMODE) {
|
||||||
case O_RDONLY:
|
case O_RDONLY:
|
||||||
pipefds[pn].events = fds[i].events & POLLIN;
|
pipefds[pn].events = fds[i].events & POLLIN_;
|
||||||
break;
|
break;
|
||||||
case O_WRONLY:
|
case O_WRONLY:
|
||||||
pipefds[pn].events = fds[i].events & POLLOUT;
|
pipefds[pn].events = fds[i].events & POLLOUT_;
|
||||||
break;
|
break;
|
||||||
case O_RDWR:
|
case O_RDWR:
|
||||||
pipefds[pn].events = fds[i].events & (POLLIN | POLLOUT);
|
pipefds[pn].events = fds[i].events & (POLLIN_ | POLLOUT_);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
@ -119,7 +132,7 @@ static textwindows int sys_poll_nt_impl(struct pollfd *fds, uint64_t nfds,
|
||||||
++pn;
|
++pn;
|
||||||
} else {
|
} else {
|
||||||
// too many non-socket fds
|
// too many non-socket fds
|
||||||
rc = enomem();
|
rc = e2big();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -127,52 +140,60 @@ static textwindows int sys_poll_nt_impl(struct pollfd *fds, uint64_t nfds,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
__fds_unlock();
|
__fds_unlock();
|
||||||
if (rc) {
|
if (rc)
|
||||||
// failed to create a polling solution
|
// failed to create a polling solution
|
||||||
return rc;
|
return rc;
|
||||||
}
|
|
||||||
|
|
||||||
// perform the i/o and sleeping and looping
|
// perform the i/o and sleeping and looping
|
||||||
for (;;) {
|
for (;;) {
|
||||||
// see if input is available on non-sockets
|
// see if input is available on non-sockets
|
||||||
for (gotpipes = i = 0; i < pn; ++i) {
|
for (gotpipes = i = 0; i < pn; ++i) {
|
||||||
if (pipefds[i].events & POLLOUT) {
|
if (pipefds[i].events & POLLWRNORM_)
|
||||||
// we have no way of polling if a non-socket is writeable yet
|
// we have no way of polling if a non-socket is writeable yet
|
||||||
// therefore we assume that if it can happen, it shall happen
|
// therefore we assume that if it can happen, it shall happen
|
||||||
pipefds[i].revents |= POLLOUT;
|
pipefds[i].revents |= POLLWRNORM_;
|
||||||
}
|
if (GetFileType(pipefds[i].handle) == kNtFileTypePipe) {
|
||||||
if (pipefds[i].events & POLLIN) {
|
ok = PeekNamedPipe(pipefds[i].handle, 0, 0, 0, &avail, 0);
|
||||||
if (GetFileType(pipefds[i].handle) == kNtFileTypePipe) {
|
POLLTRACE("PeekNamedPipe(%ld, 0, 0, 0, [%'u], 0) → {%hhhd, %d}",
|
||||||
ok = PeekNamedPipe(pipefds[i].handle, 0, 0, 0, &avail, 0);
|
pipefds[i].handle, avail, ok, GetLastError());
|
||||||
POLLTRACE("PeekNamedPipe(%ld, 0, 0, 0, [%'u], 0) → %hhhd% m",
|
if (ok) {
|
||||||
pipefds[i].handle, avail, ok);
|
if (avail)
|
||||||
if (ok) {
|
pipefds[i].revents |= POLLRDNORM_;
|
||||||
if (avail) {
|
} else if (GetLastError() == kNtErrorHandleEof ||
|
||||||
pipefds[i].revents |= POLLIN;
|
GetLastError() == kNtErrorBrokenPipe) {
|
||||||
}
|
pipefds[i].revents &= ~POLLWRNORM_;
|
||||||
} else {
|
pipefds[i].revents |= POLLHUP_;
|
||||||
pipefds[i].revents |= POLLERR;
|
|
||||||
}
|
|
||||||
} else if (GetConsoleMode(pipefds[i].handle, &cm)) {
|
|
||||||
if (CountConsoleInputBytes()) {
|
|
||||||
pipefds[i].revents |= POLLIN; // both >0 and -1 (eof) are pollin
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// we have no way of polling if a non-socket is readable yet
|
pipefds[i].revents &= ~POLLWRNORM_;
|
||||||
// therefore we assume that if it can happen it shall happen
|
pipefds[i].revents |= POLLERR_;
|
||||||
pipefds[i].revents |= POLLIN;
|
|
||||||
}
|
}
|
||||||
|
} else if (GetConsoleMode(pipefds[i].handle, &cm)) {
|
||||||
|
switch (CountConsoleInputBytes()) {
|
||||||
|
case 0:
|
||||||
|
break;
|
||||||
|
case -1:
|
||||||
|
pipefds[i].revents &= ~POLLWRNORM_;
|
||||||
|
pipefds[i].revents |= POLLHUP_;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
pipefds[i].revents |= POLLRDNORM_;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// we have no way of polling if a non-socket is readable yet
|
||||||
|
// therefore we assume that if it can happen it shall happen
|
||||||
|
pipefds[i].revents |= POLLRDNORM_;
|
||||||
}
|
}
|
||||||
if (pipefds[i].revents) {
|
if (!(pipefds[i].events & POLLRDNORM_))
|
||||||
|
pipefds[i].revents &= ~POLLRDNORM_;
|
||||||
|
if (pipefds[i].revents)
|
||||||
++gotpipes;
|
++gotpipes;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// if we haven't found any good results yet then here we
|
// if we haven't found any good results yet then here we
|
||||||
// compute a small time slice we don't mind sleeping for
|
// compute a small time slice we don't mind sleeping for
|
||||||
if (sn) {
|
if (sn) {
|
||||||
if ((gotsocks = WSAPoll(sockfds, sn, 0)) == -1) {
|
if ((gotsocks = WSAPoll(sockfds, sn, 0)) == -1)
|
||||||
return __winsockerr();
|
return __winsockerr();
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
gotsocks = 0;
|
gotsocks = 0;
|
||||||
}
|
}
|
||||||
|
@ -190,18 +211,16 @@ static textwindows int sys_poll_nt_impl(struct pollfd *fds, uint64_t nfds,
|
||||||
if (waitfor) {
|
if (waitfor) {
|
||||||
POLLTRACE("poll() sleeping for %'d out of %'lu ms", waitfor,
|
POLLTRACE("poll() sleeping for %'d out of %'lu ms", waitfor,
|
||||||
timespec_tomillis(remain));
|
timespec_tomillis(remain));
|
||||||
if ((rc = _park_norestart(waitfor, sigmask)) == -1) {
|
if ((rc = _park_norestart(waitfor, sigmask)) == -1)
|
||||||
return -1; // eintr, ecanceled, etc.
|
return -1; // eintr, ecanceled, etc.
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// we gave all the sockets and all the named pipes a shot
|
// we gave all the sockets and all the named pipes a shot
|
||||||
// if we found anything at all then it's time to end work
|
// if we found anything at all then it's time to end work
|
||||||
if (gotinvals || gotpipes || gotsocks || !waitfor) {
|
if (gotinvals || gotpipes || gotsocks || !waitfor)
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// the system call is going to succeed
|
// the system call is going to succeed
|
||||||
|
@ -210,15 +229,13 @@ static textwindows int sys_poll_nt_impl(struct pollfd *fds, uint64_t nfds,
|
||||||
if (fds[i].fd < 0 || __isfdopen(fds[i].fd)) {
|
if (fds[i].fd < 0 || __isfdopen(fds[i].fd)) {
|
||||||
fds[i].revents = 0;
|
fds[i].revents = 0;
|
||||||
} else {
|
} else {
|
||||||
fds[i].revents = POLLNVAL;
|
fds[i].revents = POLLNVAL_;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (i = 0; i < pn; ++i) {
|
for (i = 0; i < pn; ++i)
|
||||||
fds[pipeindices[i]].revents = pipefds[i].revents;
|
fds[pipeindices[i]].revents = pipefds[i].revents;
|
||||||
}
|
for (i = 0; i < sn; ++i)
|
||||||
for (i = 0; i < sn; ++i) {
|
|
||||||
fds[sockindices[i]].revents = sockfds[i].revents;
|
fds[sockindices[i]].revents = sockfds[i].revents;
|
||||||
}
|
|
||||||
|
|
||||||
// and finally return
|
// and finally return
|
||||||
return gotinvals + gotpipes + gotsocks;
|
return gotinvals + gotpipes + gotsocks;
|
||||||
|
|
|
@ -16,46 +16,50 @@
|
||||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/calls/cp.internal.h"
|
#include "libc/calls/struct/timespec.h"
|
||||||
#include "libc/dce.h"
|
|
||||||
#include "libc/intrin/strace.h"
|
|
||||||
#include "libc/sock/struct/pollfd.h"
|
#include "libc/sock/struct/pollfd.h"
|
||||||
#include "libc/sock/struct/pollfd.internal.h"
|
|
||||||
#include "libc/stdckdint.h"
|
|
||||||
#include "libc/sysv/errfuns.h"
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Waits for something to happen on multiple file descriptors at once.
|
* Checks status on multiple file descriptors at once.
|
||||||
*
|
*
|
||||||
* Warning: XNU has an inconsistency with other platforms. If you have
|
* Servers that need to handle an unbounded number of client connections
|
||||||
* pollfds with fd≥0 and none of the meaningful events flags are added
|
* should just create a separate thread for each client. poll() isn't a
|
||||||
* e.g. POLLIN then XNU won't check for POLLNVAL. This matters because
|
* scalable i/o solution on any platform.
|
||||||
* one of the use-cases for poll() is quickly checking for open files.
|
|
||||||
*
|
*
|
||||||
* Note: Polling works best on Windows for sockets. We're able to poll
|
* On Windows it's only possible to poll 64 file descriptors at a time.
|
||||||
* input on named pipes. But for anything that isn't a socket, or pipe
|
* This is a limitation imposed by WSAPoll(). Cosmopolitan Libc's poll()
|
||||||
* with POLLIN, (e.g. regular file) then POLLIN/POLLOUT are always set
|
* polyfill can go higher in some cases. For example, you can actually
|
||||||
* into revents if they're requested, provided they were opened with a
|
* poll 64 sockets and 64 pipes/terminals at the same time. Furthermore,
|
||||||
* mode that permits reading and/or writing.
|
* elements whose fd field is set to a negative number are ignored and
|
||||||
|
* will not count against this limit.
|
||||||
*
|
*
|
||||||
* Note: Windows has a limit of 64 file descriptors and ENOMEM with -1
|
* One of the use cases for poll() is to quickly check if a number of
|
||||||
* is returned if that limit is exceeded. In practice the limit is not
|
* file descriptors are valid. The canonical way to do this is to set
|
||||||
* this low. For example, pollfds with fd<0 don't count. So the caller
|
* events to 0 which prevents blocking and causes only the invalid,
|
||||||
* could flip the sign bit with a short timeout, to poll a larger set.
|
* hangup, and error statuses to be checked.
|
||||||
|
*
|
||||||
|
* On XNU, the POLLHUP and POLLERR statuses aren't checked unless either
|
||||||
|
* POLLIN, POLLOUT, or POLLPRI are specified in the events field. Cosmo
|
||||||
|
* will however polyfill the checking of POLLNVAL on XNU with the events
|
||||||
|
* doesn't specify any of the above i/o events.
|
||||||
|
*
|
||||||
|
* When XNU and BSD OSes report POLLHUP, they will always set POLLIN too
|
||||||
|
* when POLLIN is requested, even in cases when there isn't unread data.
|
||||||
*
|
*
|
||||||
* @param fds[𝑖].fd should be a socket, input pipe, or conosle input
|
* @param fds[𝑖].fd should be a socket, input pipe, or conosle input
|
||||||
* and if it's a negative number then the entry is ignored
|
* and if it's a negative number then the entry is ignored, plus
|
||||||
|
* revents will be set to zero
|
||||||
* @param fds[𝑖].events flags can have POLLIN, POLLOUT, POLLPRI,
|
* @param fds[𝑖].events flags can have POLLIN, POLLOUT, POLLPRI,
|
||||||
* POLLRDNORM, POLLWRNORM, POLLRDBAND, POLLWRBAND as well as
|
* POLLRDNORM, POLLWRNORM, POLLRDBAND, POLLWRBAND as well as
|
||||||
* POLLERR, POLLHUP, and POLLNVAL although the latter are
|
* POLLERR, POLLHUP, and POLLNVAL although the latter are
|
||||||
* always implied (assuming fd≥0) so they're ignored here
|
* always implied (assuming fd≥0) so they're ignored here
|
||||||
* @param timeout_ms if 0 means don't wait and -1 means wait forever
|
* @param timeout_ms if 0 means don't wait and negative waits forever
|
||||||
* @return number of items fds whose revents field has been set to
|
* @return number of `fds` whose revents field has been set to a nonzero
|
||||||
* nonzero to describe its events, or 0 if the timeout elapsed,
|
* number, 0 if the timeout elapsed without events, or -1 w/ errno
|
||||||
* or -1 w/ errno
|
|
||||||
* @return fds[𝑖].revents is always zero initializaed and then will
|
* @return fds[𝑖].revents is always zero initializaed and then will
|
||||||
* be populated with POLL{IN,OUT,PRI,HUP,ERR,NVAL} if something
|
* be populated with POLL{IN,OUT,PRI,HUP,ERR,NVAL} if something
|
||||||
* was determined about the file descriptor
|
* was determined about the file descriptor
|
||||||
|
* @raise E2BIG if we exceeded the 64 socket limit on Windows
|
||||||
* @raise ECANCELED if thread was cancelled in masked mode
|
* @raise ECANCELED if thread was cancelled in masked mode
|
||||||
* @raise EINTR if signal was delivered
|
* @raise EINTR if signal was delivered
|
||||||
* @cancelationpoint
|
* @cancelationpoint
|
||||||
|
@ -63,22 +67,14 @@
|
||||||
* @norestart
|
* @norestart
|
||||||
*/
|
*/
|
||||||
int poll(struct pollfd *fds, size_t nfds, int timeout_ms) {
|
int poll(struct pollfd *fds, size_t nfds, int timeout_ms) {
|
||||||
int rc;
|
struct timespec ts;
|
||||||
BEGIN_CANCELATION_POINT;
|
struct timespec *tsp;
|
||||||
|
if (timeout_ms >= 0) {
|
||||||
if (!IsWindows()) {
|
ts.tv_sec = timeout_ms / 1000;
|
||||||
if (!IsMetal()) {
|
ts.tv_nsec = timeout_ms % 1000 * 1000000;
|
||||||
rc = sys_poll(fds, nfds, timeout_ms);
|
tsp = &ts;
|
||||||
} else {
|
|
||||||
rc = sys_poll_metal(fds, nfds, timeout_ms);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
uint32_t ms = timeout_ms >= 0 ? timeout_ms : -1u;
|
tsp = 0;
|
||||||
rc = sys_poll_nt(fds, nfds, &ms, 0);
|
|
||||||
}
|
}
|
||||||
|
return ppoll(fds, nfds, tsp, 0);
|
||||||
END_CANCELATION_POINT;
|
|
||||||
STRACE("poll(%s, %'zu, %'d) → %d% lm", DescribePollFds(rc, fds, nfds), nfds,
|
|
||||||
timeout_ms, rc);
|
|
||||||
return rc;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
|
#include "libc/calls/calls.h"
|
||||||
#include "libc/calls/cp.internal.h"
|
#include "libc/calls/cp.internal.h"
|
||||||
#include "libc/calls/struct/sigset.h"
|
#include "libc/calls/struct/sigset.h"
|
||||||
#include "libc/calls/struct/sigset.internal.h"
|
#include "libc/calls/struct/sigset.internal.h"
|
||||||
|
@ -24,14 +25,18 @@
|
||||||
#include "libc/dce.h"
|
#include "libc/dce.h"
|
||||||
#include "libc/errno.h"
|
#include "libc/errno.h"
|
||||||
#include "libc/intrin/strace.h"
|
#include "libc/intrin/strace.h"
|
||||||
|
#include "libc/runtime/stack.h"
|
||||||
#include "libc/sock/struct/pollfd.h"
|
#include "libc/sock/struct/pollfd.h"
|
||||||
#include "libc/sock/struct/pollfd.internal.h"
|
#include "libc/sock/struct/pollfd.internal.h"
|
||||||
#include "libc/stdckdint.h"
|
#include "libc/stdckdint.h"
|
||||||
|
#include "libc/str/str.h"
|
||||||
|
#include "libc/sysv/consts/f.h"
|
||||||
|
#include "libc/sysv/consts/poll.h"
|
||||||
#include "libc/sysv/consts/sig.h"
|
#include "libc/sysv/consts/sig.h"
|
||||||
#include "libc/sysv/errfuns.h"
|
#include "libc/sysv/errfuns.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Waits for something to happen on multiple file descriptors at once.
|
* Checks status on multiple file descriptors at once.
|
||||||
*
|
*
|
||||||
* This function is the same as saying:
|
* This function is the same as saying:
|
||||||
*
|
*
|
||||||
|
@ -41,16 +46,51 @@
|
||||||
* sigprocmask(SIG_SETMASK, old, 0);
|
* sigprocmask(SIG_SETMASK, old, 0);
|
||||||
*
|
*
|
||||||
* Except it happens atomically when the kernel supports doing that. On
|
* Except it happens atomically when the kernel supports doing that. On
|
||||||
* kernel such as XNU and NetBSD which don't, this wrapper will fall
|
* kernels such as XNU and NetBSD which don't, this wrapper will fall
|
||||||
* back to using the example above. Consider using pselect() which is
|
* back to using the example above. If you need ironclad assurances of
|
||||||
* atomic on all supported platforms.
|
* signal mask atomicity, then consider using pselect() which Cosmo Libc
|
||||||
|
* guarantees to be atomic on all supported platforms.
|
||||||
*
|
*
|
||||||
* The Linux Kernel modifies the timeout parameter. This wrapper gives
|
* Servers that need to handle an unbounded number of client connections
|
||||||
* it a local variable due to POSIX requiring that `timeout` be const.
|
* should just create a separate thread for each client. poll(), ppoll()
|
||||||
* If you need that information from the Linux Kernel use sys_ppoll().
|
* and select() aren't scalable i/o solutions on any platform.
|
||||||
*
|
*
|
||||||
|
* On Windows it's only possible to poll 64 file descriptors at a time;
|
||||||
|
* it's a limitation imposed by WSAPoll(). Cosmopolitan Libc's ppoll()
|
||||||
|
* polyfill can go higher in some cases; for example, It's possible to
|
||||||
|
* poll 64 sockets and 64 pipes/terminals at the same time. Furthermore,
|
||||||
|
* elements whose fd field is set to a negative number are ignored and
|
||||||
|
* will not count against this limit.
|
||||||
|
*
|
||||||
|
* One of the use cases for poll() is to quickly check if a number of
|
||||||
|
* file descriptors are valid. The canonical way to do this is to set
|
||||||
|
* events to 0 which prevents blocking and causes only the invalid,
|
||||||
|
* hangup, and error statuses to be checked.
|
||||||
|
*
|
||||||
|
* On XNU, the POLLHUP and POLLERR statuses aren't checked unless either
|
||||||
|
* POLLIN, POLLOUT, or POLLPRI are specified in the events field. Cosmo
|
||||||
|
* will however polyfill the checking of POLLNVAL on XNU with the events
|
||||||
|
* doesn't specify any of the above i/o events.
|
||||||
|
*
|
||||||
|
* When XNU and BSD OSes report POLLHUP, they will always set POLLIN too
|
||||||
|
* when POLLIN is requested, even in cases when there isn't unread data.
|
||||||
|
*
|
||||||
|
* @param fds[𝑖].fd should be a socket, input pipe, or conosle input
|
||||||
|
* and if it's a negative number then the entry is ignored, plus
|
||||||
|
* revents will be set to zero
|
||||||
|
* @param fds[𝑖].events flags can have POLLIN, POLLOUT, POLLPRI,
|
||||||
|
* POLLRDNORM, POLLWRNORM, POLLRDBAND, POLLWRBAND as well as
|
||||||
|
* POLLERR, POLLHUP, and POLLNVAL although the latter are
|
||||||
|
* always implied (assuming fd≥0) so they're ignored here
|
||||||
|
* @param timeout_ms if 0 means don't wait and negative waits forever
|
||||||
|
* @return number of `fds` whose revents field has been set to a nonzero
|
||||||
|
* number, 0 if the timeout elapsed without events, or -1 w/ errno
|
||||||
|
* @return fds[𝑖].revents is always zero initializaed and then will
|
||||||
|
* be populated with POLL{IN,OUT,PRI,HUP,ERR,NVAL} if something
|
||||||
|
* was determined about the file descriptor
|
||||||
* @param timeout if null will block indefinitely
|
* @param timeout if null will block indefinitely
|
||||||
* @param sigmask may be null in which case no mask change happens
|
* @param sigmask may be null in which case no mask change happens
|
||||||
|
* @raise E2BIG if we exceeded the 64 socket limit on Windows
|
||||||
* @raise ECANCELED if thread was cancelled in masked mode
|
* @raise ECANCELED if thread was cancelled in masked mode
|
||||||
* @raise EINTR if signal was delivered
|
* @raise EINTR if signal was delivered
|
||||||
* @cancelationpoint
|
* @cancelationpoint
|
||||||
|
@ -59,11 +99,32 @@
|
||||||
*/
|
*/
|
||||||
int ppoll(struct pollfd *fds, size_t nfds, const struct timespec *timeout,
|
int ppoll(struct pollfd *fds, size_t nfds, const struct timespec *timeout,
|
||||||
const sigset_t *sigmask) {
|
const sigset_t *sigmask) {
|
||||||
int e, rc;
|
int e, fdcount;
|
||||||
sigset_t oldmask;
|
sigset_t oldmask;
|
||||||
struct timespec ts, *tsp;
|
struct timespec ts, *tsp;
|
||||||
BEGIN_CANCELATION_POINT;
|
BEGIN_CANCELATION_POINT;
|
||||||
|
|
||||||
|
// The OpenBSD poll() man pages claims it'll ignore POLLERR, POLLHUP,
|
||||||
|
// and POLLNVAL in pollfd::events except it doesn't actually do this.
|
||||||
|
size_t bytes = 0;
|
||||||
|
struct pollfd *fds2 = 0;
|
||||||
|
if (IsOpenbsd()) {
|
||||||
|
if (ckd_mul(&bytes, nfds, sizeof(struct pollfd)))
|
||||||
|
return einval();
|
||||||
|
#pragma GCC push_options
|
||||||
|
#pragma GCC diagnostic ignored "-Walloca-larger-than="
|
||||||
|
#pragma GCC diagnostic ignored "-Wanalyzer-out-of-bounds"
|
||||||
|
fds2 = alloca(bytes);
|
||||||
|
#pragma GCC pop_options
|
||||||
|
CheckLargeStackAllocation(fds2, bytes);
|
||||||
|
memcpy(fds2, fds, bytes);
|
||||||
|
for (size_t i = 0; i < nfds; ++i)
|
||||||
|
fds2[i].events &= ~(POLLERR | POLLHUP | POLLNVAL);
|
||||||
|
struct pollfd *swap = fds;
|
||||||
|
fds = fds2;
|
||||||
|
fds2 = swap;
|
||||||
|
}
|
||||||
|
|
||||||
if (!IsWindows()) {
|
if (!IsWindows()) {
|
||||||
e = errno;
|
e = errno;
|
||||||
if (timeout) {
|
if (timeout) {
|
||||||
|
@ -72,8 +133,8 @@ int ppoll(struct pollfd *fds, size_t nfds, const struct timespec *timeout,
|
||||||
} else {
|
} else {
|
||||||
tsp = 0;
|
tsp = 0;
|
||||||
}
|
}
|
||||||
rc = sys_ppoll(fds, nfds, tsp, sigmask, 8);
|
fdcount = sys_ppoll(fds, nfds, tsp, sigmask, 8);
|
||||||
if (rc == -1 && errno == ENOSYS) {
|
if (fdcount == -1 && errno == ENOSYS) {
|
||||||
int ms;
|
int ms;
|
||||||
errno = e;
|
errno = e;
|
||||||
if (!timeout || ckd_add(&ms, timeout->tv_sec,
|
if (!timeout || ckd_add(&ms, timeout->tv_sec,
|
||||||
|
@ -82,7 +143,7 @@ int ppoll(struct pollfd *fds, size_t nfds, const struct timespec *timeout,
|
||||||
}
|
}
|
||||||
if (sigmask)
|
if (sigmask)
|
||||||
sys_sigprocmask(SIG_SETMASK, sigmask, &oldmask);
|
sys_sigprocmask(SIG_SETMASK, sigmask, &oldmask);
|
||||||
rc = poll(fds, nfds, ms);
|
fdcount = sys_poll(fds, nfds, ms);
|
||||||
if (sigmask)
|
if (sigmask)
|
||||||
sys_sigprocmask(SIG_SETMASK, &oldmask, 0);
|
sys_sigprocmask(SIG_SETMASK, &oldmask, 0);
|
||||||
}
|
}
|
||||||
|
@ -92,11 +153,38 @@ int ppoll(struct pollfd *fds, size_t nfds, const struct timespec *timeout,
|
||||||
ckd_add(&ms, timeout->tv_sec, (timeout->tv_nsec + 999999) / 1000000)) {
|
ckd_add(&ms, timeout->tv_sec, (timeout->tv_nsec + 999999) / 1000000)) {
|
||||||
ms = -1u;
|
ms = -1u;
|
||||||
}
|
}
|
||||||
rc = sys_poll_nt(fds, nfds, &ms, sigmask);
|
fdcount = sys_poll_nt(fds, nfds, &ms, sigmask);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsOpenbsd() && fdcount != -1) {
|
||||||
|
struct pollfd *swap = fds;
|
||||||
|
fds = fds2;
|
||||||
|
fds2 = swap;
|
||||||
|
memcpy(fds, fds2, bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
// One of the use cases for poll() is checking if a large number of
|
||||||
|
// file descriptors exist. However on XNU if none of the meaningful
|
||||||
|
// event flags are specified (e.g. POLLIN, POLLOUT) then it doesn't
|
||||||
|
// perform the POLLNVAL check that's implied on all other platforms
|
||||||
|
if (IsXnu() && fdcount != -1) {
|
||||||
|
for (size_t i = 0; i < nfds; ++i) {
|
||||||
|
if (fds[i].fd >= 0 && //
|
||||||
|
!fds[i].revents && //
|
||||||
|
!(fds[i].events & (POLLIN | POLLOUT | POLLPRI))) {
|
||||||
|
int err = errno;
|
||||||
|
if (fcntl(fds[i].fd, F_GETFL) == -1) {
|
||||||
|
errno = err;
|
||||||
|
fds[i].revents = POLLNVAL;
|
||||||
|
++fdcount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
END_CANCELATION_POINT;
|
END_CANCELATION_POINT;
|
||||||
STRACE("ppoll(%s, %'zu, %s, %s) → %d% lm", DescribePollFds(rc, fds, nfds),
|
STRACE("ppoll(%s, %'zu, %s, %s) → %d% lm",
|
||||||
nfds, DescribeTimespec(0, timeout), DescribeSigset(0, sigmask), rc);
|
DescribePollFds(fdcount, fds, nfds), nfds,
|
||||||
return rc;
|
DescribeTimespec(0, timeout), DescribeSigset(0, sigmask), fdcount);
|
||||||
|
return fdcount;
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,7 +32,7 @@
|
||||||
#include "libc/sysv/errfuns.h"
|
#include "libc/sysv/errfuns.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Does what poll() does except with bitset API.
|
* Checks status on multiple file descriptors at once.
|
||||||
*
|
*
|
||||||
* This function is the same as saying:
|
* This function is the same as saying:
|
||||||
*
|
*
|
||||||
|
@ -41,15 +41,23 @@
|
||||||
* select(nfds, readfds, writefds, exceptfds, timeout);
|
* select(nfds, readfds, writefds, exceptfds, timeout);
|
||||||
* sigprocmask(SIG_SETMASK, old, 0);
|
* sigprocmask(SIG_SETMASK, old, 0);
|
||||||
*
|
*
|
||||||
* Except it happens atomically.
|
* Except it happens atomically. Unlike ppoll() Cosmo guarantees this is
|
||||||
*
|
* atomic on all supported platforms.
|
||||||
* The Linux Kernel modifies the timeout parameter. This wrapper gives
|
|
||||||
* it a local variable due to POSIX requiring that `timeout` be const.
|
|
||||||
* If you need that information from the Linux Kernel use sys_pselect.
|
|
||||||
*
|
|
||||||
* This system call is supported on all platforms. It's like select()
|
|
||||||
* except that it atomically changes the sigprocmask() during the op.
|
|
||||||
*
|
*
|
||||||
|
* @param nfds is the number of the highest file descriptor set in these
|
||||||
|
* bitsets by the caller, plus one; this value can't be greater than
|
||||||
|
* `FD_SETSIZE` which Cosmopolitan currently defines as 1024 because
|
||||||
|
* `fd_set` has a static size
|
||||||
|
* @param readfds may be used to be notified when you can call read() on
|
||||||
|
* a file descriptor without it blocking; this includes when data is
|
||||||
|
* is available to be read as well as eof and error conditions
|
||||||
|
* @param writefds may be used to be notified when write() may be called
|
||||||
|
* on a file descriptor without it blocking
|
||||||
|
* @param exceptfds may be used to be notified of exceptional conditions
|
||||||
|
* such as out-of-band data on a socket; it is equivalent to POLLPRI
|
||||||
|
* in the revents of poll()
|
||||||
|
* @param timeout if null will block indefinitely
|
||||||
|
* @param sigmask may be null in which case no mask change happens
|
||||||
* @raise ECANCELED if thread was cancelled in masked mode
|
* @raise ECANCELED if thread was cancelled in masked mode
|
||||||
* @raise EINTR if signal was delivered
|
* @raise EINTR if signal was delivered
|
||||||
* @cancelationpoint
|
* @cancelationpoint
|
||||||
|
@ -74,7 +82,7 @@ int pselect(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
|
||||||
fd_set *old_exceptfds_ptr = 0;
|
fd_set *old_exceptfds_ptr = 0;
|
||||||
|
|
||||||
BEGIN_CANCELATION_POINT;
|
BEGIN_CANCELATION_POINT;
|
||||||
if (nfds < 0) {
|
if (nfds < 0 || nfds > FD_SETSIZE) {
|
||||||
rc = einval();
|
rc = einval();
|
||||||
} else {
|
} else {
|
||||||
if (readfds) {
|
if (readfds) {
|
||||||
|
|
|
@ -16,7 +16,6 @@
|
||||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/assert.h"
|
|
||||||
#include "libc/calls/internal.h"
|
#include "libc/calls/internal.h"
|
||||||
#include "libc/calls/state.internal.h"
|
#include "libc/calls/state.internal.h"
|
||||||
#include "libc/calls/struct/timeval.h"
|
#include "libc/calls/struct/timeval.h"
|
||||||
|
@ -31,37 +30,48 @@
|
||||||
#include "libc/sysv/errfuns.h"
|
#include "libc/sysv/errfuns.h"
|
||||||
#ifdef __x86_64__
|
#ifdef __x86_64__
|
||||||
|
|
||||||
|
// <sync libc/sysv/consts.sh>
|
||||||
|
#define POLLERR_ 0x0001 // implied in events
|
||||||
|
#define POLLHUP_ 0x0002 // implied in events
|
||||||
|
#define POLLNVAL_ 0x0004 // implied in events
|
||||||
|
#define POLLIN_ 0x0300
|
||||||
|
#define POLLRDNORM_ 0x0100
|
||||||
|
#define POLLRDBAND_ 0x0200
|
||||||
|
#define POLLOUT_ 0x0010
|
||||||
|
#define POLLWRNORM_ 0x0010
|
||||||
|
#define POLLWRBAND_ 0x0020 // MSDN undocumented
|
||||||
|
#define POLLPRI_ 0x0400 // MSDN unsupported
|
||||||
|
// </sync libc/sysv/consts.sh>
|
||||||
|
|
||||||
int sys_select_nt(int nfds, fd_set *readfds, fd_set *writefds,
|
int sys_select_nt(int nfds, fd_set *readfds, fd_set *writefds,
|
||||||
fd_set *exceptfds, struct timeval *timeout,
|
fd_set *exceptfds, struct timeval *timeout,
|
||||||
const sigset_t *sigmask) {
|
const sigset_t *sigmask) {
|
||||||
int i, pfds, events, fdcount;
|
int pfds = 0;
|
||||||
|
|
||||||
// convert bitsets to pollfd
|
// convert bitsets to pollfd
|
||||||
struct pollfd fds[64];
|
struct pollfd fds[128];
|
||||||
for (pfds = i = 0; i < nfds; ++i) {
|
for (int fd = 0; fd < nfds; ++fd) {
|
||||||
events = 0;
|
int events = 0;
|
||||||
if (readfds && FD_ISSET(i, readfds))
|
if (readfds && FD_ISSET(fd, readfds))
|
||||||
events |= POLLIN;
|
events |= POLLIN_;
|
||||||
if (writefds && FD_ISSET(i, writefds))
|
if (writefds && FD_ISSET(fd, writefds))
|
||||||
events |= POLLOUT;
|
events |= POLLOUT_;
|
||||||
if (exceptfds && FD_ISSET(i, exceptfds))
|
if (exceptfds && FD_ISSET(fd, exceptfds))
|
||||||
events |= POLLERR;
|
events |= POLLPRI_;
|
||||||
if (events) {
|
if (events) {
|
||||||
if (pfds < ARRAYLEN(fds)) {
|
if (pfds == ARRAYLEN(fds))
|
||||||
fds[pfds].fd = i;
|
return e2big();
|
||||||
fds[pfds].events = events;
|
fds[pfds].fd = fd;
|
||||||
fds[pfds].revents = 0;
|
fds[pfds].events = events;
|
||||||
pfds += 1;
|
fds[pfds].revents = 0;
|
||||||
} else {
|
++pfds;
|
||||||
return enomem();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// convert the wait time to a word
|
// convert the wait time to a word
|
||||||
uint32_t millis;
|
uint32_t millis;
|
||||||
if (!timeout) {
|
if (!timeout) {
|
||||||
millis = -1;
|
millis = -1u;
|
||||||
} else {
|
} else {
|
||||||
int64_t ms = timeval_tomillis(*timeout);
|
int64_t ms = timeval_tomillis(*timeout);
|
||||||
if (ms < 0 || ms > UINT32_MAX) {
|
if (ms < 0 || ms > UINT32_MAX) {
|
||||||
|
@ -72,9 +82,8 @@ int sys_select_nt(int nfds, fd_set *readfds, fd_set *writefds,
|
||||||
}
|
}
|
||||||
|
|
||||||
// call our nt poll implementation
|
// call our nt poll implementation
|
||||||
fdcount = sys_poll_nt(fds, pfds, &millis, sigmask);
|
int fdcount = sys_poll_nt(fds, pfds, &millis, sigmask);
|
||||||
unassert(fdcount < 64);
|
if (fdcount == -1)
|
||||||
if (fdcount < 0)
|
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
// convert pollfd back to bitsets
|
// convert pollfd back to bitsets
|
||||||
|
@ -85,20 +94,20 @@ int sys_select_nt(int nfds, fd_set *readfds, fd_set *writefds,
|
||||||
if (exceptfds)
|
if (exceptfds)
|
||||||
FD_ZERO(exceptfds);
|
FD_ZERO(exceptfds);
|
||||||
int bits = 0;
|
int bits = 0;
|
||||||
for (i = 0; i < pfds; ++i) {
|
for (int i = 0; i < pfds; ++i) {
|
||||||
if (fds[i].revents & POLLIN) {
|
if (fds[i].revents & (POLLIN_ | POLLHUP_ | POLLERR_ | POLLNVAL_)) {
|
||||||
if (readfds) {
|
if (readfds) {
|
||||||
FD_SET(fds[i].fd, readfds);
|
FD_SET(fds[i].fd, readfds);
|
||||||
++bits;
|
++bits;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (fds[i].revents & POLLOUT) {
|
if (fds[i].revents & POLLOUT_) {
|
||||||
if (writefds) {
|
if (writefds) {
|
||||||
FD_SET(fds[i].fd, writefds);
|
FD_SET(fds[i].fd, writefds);
|
||||||
++bits;
|
++bits;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (fds[i].revents & (POLLERR | POLLNVAL)) {
|
if (fds[i].revents & POLLPRI_) {
|
||||||
if (exceptfds) {
|
if (exceptfds) {
|
||||||
FD_SET(fds[i].fd, exceptfds);
|
FD_SET(fds[i].fd, exceptfds);
|
||||||
++bits;
|
++bits;
|
||||||
|
@ -107,9 +116,8 @@ int sys_select_nt(int nfds, fd_set *readfds, fd_set *writefds,
|
||||||
}
|
}
|
||||||
|
|
||||||
// store remaining time back in caller's timeval
|
// store remaining time back in caller's timeval
|
||||||
if (timeout) {
|
if (timeout)
|
||||||
*timeout = timeval_frommillis(millis);
|
*timeout = timeval_frommillis(millis);
|
||||||
}
|
|
||||||
|
|
||||||
return bits;
|
return bits;
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,26 +17,25 @@
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/sock/select.h"
|
#include "libc/sock/select.h"
|
||||||
#include "libc/calls/cp.internal.h"
|
|
||||||
#include "libc/calls/struct/itimerval.internal.h"
|
|
||||||
#include "libc/calls/struct/timespec.h"
|
|
||||||
#include "libc/calls/struct/timeval.h"
|
#include "libc/calls/struct/timeval.h"
|
||||||
#include "libc/calls/struct/timeval.internal.h"
|
|
||||||
#include "libc/dce.h"
|
|
||||||
#include "libc/intrin/describeflags.h"
|
|
||||||
#include "libc/intrin/strace.h"
|
|
||||||
#include "libc/sock/internal.h"
|
|
||||||
#include "libc/sock/select.h"
|
|
||||||
#include "libc/sock/select.internal.h"
|
|
||||||
#include "libc/sysv/errfuns.h"
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Does what poll() does except with bitset API.
|
* Checks status on multiple file descriptors at once.
|
||||||
*
|
|
||||||
* This system call is supported on all platforms. However, on Windows,
|
|
||||||
* this is polyfilled to translate into poll(). So it's recommended that
|
|
||||||
* poll() be used instead.
|
|
||||||
*
|
*
|
||||||
|
* @param readfds may be used to be notified when you can call read() on
|
||||||
|
* a file descriptor without it blocking; this includes when data is
|
||||||
|
* is available to be read as well as eof and error conditions
|
||||||
|
* @param writefds may be used to be notified when write() may be called
|
||||||
|
* on a file descriptor without it blocking
|
||||||
|
* @param exceptfds may be used to be notified of exceptional conditions
|
||||||
|
* such as out-of-band data on a socket; it is equivalent to POLLPRI
|
||||||
|
* in the revents of poll()
|
||||||
|
* @param timeout may be null which means to block indefinitely; cosmo's
|
||||||
|
* implementation of select() never modifies this parameter which is
|
||||||
|
* how most platforms except Linux work which modifies it to reflect
|
||||||
|
* elapsed time, noting that POSIX permits either behavior therefore
|
||||||
|
* portable code should assume that timeout memory becomes undefined
|
||||||
|
* @raise E2BIG if we exceeded the 64 socket limit on Windows
|
||||||
* @raise ECANCELED if thread was cancelled in masked mode
|
* @raise ECANCELED if thread was cancelled in masked mode
|
||||||
* @raise EINTR if signal was delivered
|
* @raise EINTR if signal was delivered
|
||||||
* @cancelationpoint
|
* @cancelationpoint
|
||||||
|
@ -45,70 +44,13 @@
|
||||||
*/
|
*/
|
||||||
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
|
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
|
||||||
struct timeval *timeout) {
|
struct timeval *timeout) {
|
||||||
|
struct timespec ts;
|
||||||
int rc;
|
struct timespec *tsp;
|
||||||
fd_set old_readfds;
|
if (timeout) {
|
||||||
fd_set *old_readfds_ptr = 0;
|
ts = timeval_totimespec(*timeout);
|
||||||
fd_set old_writefds;
|
tsp = &ts;
|
||||||
fd_set *old_writefds_ptr = 0;
|
|
||||||
fd_set old_exceptfds;
|
|
||||||
fd_set *old_exceptfds_ptr = 0;
|
|
||||||
struct timeval old_timeout;
|
|
||||||
struct timeval *old_timeout_ptr = 0;
|
|
||||||
|
|
||||||
POLLTRACE("select(%d, %p, %p, %p, %s) → ...", nfds, readfds, writefds,
|
|
||||||
exceptfds, DescribeTimeval(0, timeout));
|
|
||||||
|
|
||||||
BEGIN_CANCELATION_POINT;
|
|
||||||
if (nfds < 0) {
|
|
||||||
rc = einval();
|
|
||||||
} else {
|
} else {
|
||||||
if (readfds) {
|
tsp = 0;
|
||||||
old_readfds = *readfds;
|
|
||||||
old_readfds_ptr = &old_readfds;
|
|
||||||
}
|
|
||||||
if (writefds) {
|
|
||||||
old_writefds = *writefds;
|
|
||||||
old_writefds_ptr = &old_writefds;
|
|
||||||
}
|
|
||||||
if (exceptfds) {
|
|
||||||
old_exceptfds = *exceptfds;
|
|
||||||
old_exceptfds_ptr = &old_exceptfds;
|
|
||||||
}
|
|
||||||
if (timeout) {
|
|
||||||
old_timeout = *timeout;
|
|
||||||
old_timeout_ptr = &old_timeout;
|
|
||||||
}
|
|
||||||
if (!IsWindows()) {
|
|
||||||
#ifdef __aarch64__
|
|
||||||
struct timespec ts, *tsp;
|
|
||||||
if (timeout) {
|
|
||||||
ts = timeval_totimespec(*timeout);
|
|
||||||
tsp = &ts;
|
|
||||||
} else {
|
|
||||||
tsp = 0;
|
|
||||||
}
|
|
||||||
rc = sys_pselect(nfds, readfds, writefds, exceptfds, tsp, 0);
|
|
||||||
if (timeout) {
|
|
||||||
*timeout = timespec_totimeval(ts);
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
rc = sys_select(nfds, readfds, writefds, exceptfds, timeout);
|
|
||||||
#endif
|
|
||||||
} else {
|
|
||||||
rc = sys_select_nt(nfds, readfds, writefds, exceptfds, timeout, 0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
END_CANCELATION_POINT;
|
return pselect(nfds, readfds, writefds, exceptfds, tsp, 0);
|
||||||
|
|
||||||
STRACE("select(%d, %s → [%s], %s → [%s], %s → [%s], %s → [%s]) → %d% m", nfds,
|
|
||||||
DescribeFdSet(rc, nfds, old_readfds_ptr),
|
|
||||||
DescribeFdSet(rc, nfds, readfds),
|
|
||||||
DescribeFdSet(rc, nfds, old_writefds_ptr),
|
|
||||||
DescribeFdSet(rc, nfds, writefds),
|
|
||||||
DescribeFdSet(rc, nfds, old_exceptfds_ptr),
|
|
||||||
DescribeFdSet(rc, nfds, exceptfds), //
|
|
||||||
DescribeTimeval(rc, old_timeout_ptr), //
|
|
||||||
DescribeTimeval(rc, timeout), rc);
|
|
||||||
return rc;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,6 @@ COSMOPOLITAN_C_START_
|
||||||
|
|
||||||
bool32 sys_isatty(int);
|
bool32 sys_isatty(int);
|
||||||
int sys_chdir_nt(const char *);
|
int sys_chdir_nt(const char *);
|
||||||
int sys_close_epoll_nt(int);
|
|
||||||
int sys_dup_nt(int, int, int, int);
|
int sys_dup_nt(int, int, int, int);
|
||||||
int sys_execve_nt(const char *, char *const[], char *const[]);
|
int sys_execve_nt(const char *, char *const[], char *const[]);
|
||||||
int sys_faccessat_nt(int, const char *, int, uint32_t);
|
int sys_faccessat_nt(int, const char *, int, uint32_t);
|
||||||
|
|
|
@ -10,7 +10,7 @@ COSMOPOLITAN_C_START_
|
||||||
#define kFdConsole 4
|
#define kFdConsole 4
|
||||||
#define kFdSerial 5
|
#define kFdSerial 5
|
||||||
#define kFdZip 6
|
#define kFdZip 6
|
||||||
#define kFdEpoll 7
|
#define kFdEpoll 7 /* epoll() deleted on 2024-09-01 */
|
||||||
#define kFdReserved 8
|
#define kFdReserved 8
|
||||||
#define kFdDevNull 9
|
#define kFdDevNull 9
|
||||||
#define kFdDevRandom 10
|
#define kFdDevRandom 10
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
#ifndef COSMOPOLITAN_LIBC_ISYSTEM_SYS_POLL_H_
|
#ifndef COSMOPOLITAN_LIBC_ISYSTEM_SYS_POLL_H_
|
||||||
#define COSMOPOLITAN_LIBC_ISYSTEM_SYS_POLL_H_
|
#define COSMOPOLITAN_LIBC_ISYSTEM_SYS_POLL_H_
|
||||||
|
#include "libc/calls/weirdtypes.h"
|
||||||
#include "libc/sock/sock.h"
|
#include "libc/sock/sock.h"
|
||||||
|
#include "libc/sock/struct/pollfd.h"
|
||||||
#include "libc/sysv/consts/poll.h"
|
#include "libc/sysv/consts/poll.h"
|
||||||
#endif /* COSMOPOLITAN_LIBC_ISYSTEM_SYS_POLL_H_ */
|
#endif /* COSMOPOLITAN_LIBC_ISYSTEM_SYS_POLL_H_ */
|
||||||
|
|
|
@ -196,10 +196,21 @@ static textwindows errno_t spawnfds_open(struct SpawnFds *fds, int64_t dirhand,
|
||||||
errno_t err;
|
errno_t err;
|
||||||
char16_t path16[PATH_MAX];
|
char16_t path16[PATH_MAX];
|
||||||
uint32_t perm, share, disp, attr;
|
uint32_t perm, share, disp, attr;
|
||||||
|
if (!strcmp(path, "/dev/null")) {
|
||||||
|
strcpy16(path16, u"NUL");
|
||||||
|
} else if (!strcmp(path, "/dev/stdin")) {
|
||||||
|
return spawnfds_dup2(fds, 0, fildes);
|
||||||
|
} else if (!strcmp(path, "/dev/stdout")) {
|
||||||
|
return spawnfds_dup2(fds, 1, fildes);
|
||||||
|
} else if (!strcmp(path, "/dev/stderr")) {
|
||||||
|
return spawnfds_dup2(fds, 2, fildes);
|
||||||
|
} else {
|
||||||
|
if (__mkntpathath(dirhand, path, 0, path16) == -1)
|
||||||
|
return errno;
|
||||||
|
}
|
||||||
if ((err = spawnfds_ensure(fds, fildes)))
|
if ((err = spawnfds_ensure(fds, fildes)))
|
||||||
return err;
|
return err;
|
||||||
if (__mkntpathath(dirhand, path, 0, path16) != -1 &&
|
if (GetNtOpenFlags(oflag, mode, &perm, &share, &disp, &attr) != -1 &&
|
||||||
GetNtOpenFlags(oflag, mode, &perm, &share, &disp, &attr) != -1 &&
|
|
||||||
(h = CreateFile(path16, perm, share, &kNtIsInheritable, disp, attr, 0))) {
|
(h = CreateFile(path16, perm, share, &kNtIsInheritable, disp, attr, 0))) {
|
||||||
spawnfds_closelater(fds, h);
|
spawnfds_closelater(fds, h);
|
||||||
fds->p[fildes].kind = kFdFile;
|
fds->p[fildes].kind = kFdFile;
|
||||||
|
@ -366,6 +377,19 @@ static textwindows errno_t posix_spawn_nt_impl(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UNC paths break some things when they are not needed.
|
||||||
|
if (lpCurrentDirectory) {
|
||||||
|
size_t n = strlen16(lpCurrentDirectory);
|
||||||
|
if (n > 4 && n < 260 && //
|
||||||
|
lpCurrentDirectory[0] == '\\' && //
|
||||||
|
lpCurrentDirectory[1] == '\\' && //
|
||||||
|
lpCurrentDirectory[2] == '?' && //
|
||||||
|
lpCurrentDirectory[3] == '\\') {
|
||||||
|
memmove(lpCurrentDirectory, lpCurrentDirectory + 4,
|
||||||
|
(n - 4 + 1) * sizeof(char16_t));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// inherit signal mask
|
// inherit signal mask
|
||||||
sigset_t childmask;
|
sigset_t childmask;
|
||||||
char maskvar[6 + 21];
|
char maskvar[6 + 21];
|
||||||
|
|
1655
libc/sock/epoll.c
1655
libc/sock/epoll.c
File diff suppressed because it is too large
Load diff
|
@ -1,25 +0,0 @@
|
||||||
#ifndef COSMOPOLITAN_LIBC_SOCK_WEPOLL_H_
|
|
||||||
#define COSMOPOLITAN_LIBC_SOCK_WEPOLL_H_
|
|
||||||
COSMOPOLITAN_C_START_
|
|
||||||
#include "libc/calls/struct/sigset.h"
|
|
||||||
|
|
||||||
typedef union epoll_data {
|
|
||||||
void *ptr;
|
|
||||||
int fd;
|
|
||||||
uint32_t u32;
|
|
||||||
uint64_t u64;
|
|
||||||
} epoll_data_t;
|
|
||||||
|
|
||||||
struct thatispacked epoll_event {
|
|
||||||
uint32_t events;
|
|
||||||
epoll_data_t data;
|
|
||||||
};
|
|
||||||
|
|
||||||
int epoll_create(int) libcesque;
|
|
||||||
int epoll_create1(int) libcesque;
|
|
||||||
int epoll_ctl(int, int, int, struct epoll_event *) libcesque;
|
|
||||||
int epoll_wait(int, struct epoll_event *, int, int) libcesque;
|
|
||||||
int epoll_pwait(int, struct epoll_event *, int, int, const sigset_t *);
|
|
||||||
|
|
||||||
COSMOPOLITAN_C_END_
|
|
||||||
#endif /* COSMOPOLITAN_LIBC_SOCK_WEPOLL_H_ */
|
|
|
@ -52,11 +52,6 @@ int32_t sys_select(int32_t, fd_set *, fd_set *, fd_set *, struct timeval *);
|
||||||
int sys_pselect(int, fd_set *, fd_set *, fd_set *, struct timespec *,
|
int sys_pselect(int, fd_set *, fd_set *, fd_set *, struct timespec *,
|
||||||
const void *);
|
const void *);
|
||||||
int sys_setsockopt(int, int, int, const void *, uint32_t);
|
int sys_setsockopt(int, int, int, const void *, uint32_t);
|
||||||
int32_t sys_epoll_create(int32_t);
|
|
||||||
int32_t sys_epoll_ctl(int32_t, int32_t, int32_t, void *);
|
|
||||||
int32_t sys_epoll_wait(int32_t, void *, int32_t, int32_t);
|
|
||||||
int32_t sys_epoll_pwait(int32_t, void *, int32_t, int32_t, const sigset_t *,
|
|
||||||
size_t);
|
|
||||||
|
|
||||||
int sys_socket_nt(int, int, int);
|
int sys_socket_nt(int, int, int);
|
||||||
|
|
||||||
|
|
|
@ -1,31 +0,0 @@
|
||||||
#ifndef COSMOPOLITAN_LIBC_SYSV_CONSTS_EPOLL_H_
|
|
||||||
#define COSMOPOLITAN_LIBC_SYSV_CONSTS_EPOLL_H_
|
|
||||||
#include "libc/sysv/consts/o.h"
|
|
||||||
|
|
||||||
#define EPOLL_CTL_ADD 1
|
|
||||||
#define EPOLL_CTL_DEL 2
|
|
||||||
#define EPOLL_CTL_MOD 3
|
|
||||||
|
|
||||||
#define EPOLLIN 1
|
|
||||||
#define EPOLLPRI 2
|
|
||||||
#define EPOLLOUT 4
|
|
||||||
#define EPOLLERR 8
|
|
||||||
#define EPOLLHUP 0x10
|
|
||||||
#define EPOLLRDNORM 0x40
|
|
||||||
#define EPOLLRDBAND 0x80
|
|
||||||
#define EPOLLWRNORM 0x0100
|
|
||||||
#define EPOLLWRBAND 0x0200
|
|
||||||
#define EPOLLMSG 0x0400
|
|
||||||
#define EPOLLRDHUP 0x2000
|
|
||||||
#define EPOLLEXCLUSIVE 0x10000000
|
|
||||||
#define EPOLLWAKEUP 0x20000000
|
|
||||||
#define EPOLLONESHOT 0x40000000
|
|
||||||
#define EPOLLET 0x80000000
|
|
||||||
|
|
||||||
COSMOPOLITAN_C_START_
|
|
||||||
|
|
||||||
extern const int EPOLL_CLOEXEC;
|
|
||||||
#define EPOLL_CLOEXEC O_CLOEXEC
|
|
||||||
|
|
||||||
COSMOPOLITAN_C_END_
|
|
||||||
#endif /* COSMOPOLITAN_LIBC_SYSV_CONSTS_EPOLL_H_ */
|
|
|
@ -188,7 +188,6 @@ static errno_t _pthread_cancel_everyone(void) {
|
||||||
* - `connect`
|
* - `connect`
|
||||||
* - `copy_file_range`
|
* - `copy_file_range`
|
||||||
* - `creat`
|
* - `creat`
|
||||||
* - `epoll_wait`
|
|
||||||
* - `fcntl(F_OFD_SETLKW)`
|
* - `fcntl(F_OFD_SETLKW)`
|
||||||
* - `fcntl(F_SETLKW)`
|
* - `fcntl(F_SETLKW)`
|
||||||
* - `fdatasync`
|
* - `fdatasync`
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
#include "libc/calls/struct/sigaction.h"
|
#include "libc/calls/struct/sigaction.h"
|
||||||
#include "libc/dce.h"
|
#include "libc/dce.h"
|
||||||
#include "libc/errno.h"
|
#include "libc/errno.h"
|
||||||
|
#include "libc/intrin/describeflags.h"
|
||||||
#include "libc/log/libfatal.internal.h"
|
#include "libc/log/libfatal.internal.h"
|
||||||
#include "libc/mem/gc.h"
|
#include "libc/mem/gc.h"
|
||||||
#include "libc/nexgen32e/rdtsc.h"
|
#include "libc/nexgen32e/rdtsc.h"
|
||||||
|
@ -55,12 +56,6 @@ void OnSig(int sig) {
|
||||||
gotsig = true;
|
gotsig = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
__wur char *FormatPollFd(struct pollfd p[2]) {
|
|
||||||
return xasprintf("fd:%d revents:%s\n"
|
|
||||||
"fd:%d revents:%s\n",
|
|
||||||
p[0].fd, "<TODO:kPollNames>", p[1].fd, "<TODO:kPollNames>");
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(poll, allZero_doesNothingPrettyMuch) {
|
TEST(poll, allZero_doesNothingPrettyMuch) {
|
||||||
EXPECT_SYS(0, 0, poll(0, 0, 0));
|
EXPECT_SYS(0, 0, poll(0, 0, 0));
|
||||||
}
|
}
|
||||||
|
@ -94,14 +89,52 @@ TEST(poll, testNegativeOneFd_isIgnored) {
|
||||||
struct sockaddr_in addr = {AF_INET, 0, {htonl(INADDR_LOOPBACK)}};
|
struct sockaddr_in addr = {AF_INET, 0, {htonl(INADDR_LOOPBACK)}};
|
||||||
ASSERT_SYS(0, 0, bind(3, (struct sockaddr *)&addr, sizeof(addr)));
|
ASSERT_SYS(0, 0, bind(3, (struct sockaddr *)&addr, sizeof(addr)));
|
||||||
ASSERT_SYS(0, 0, listen(3, 10));
|
ASSERT_SYS(0, 0, listen(3, 10));
|
||||||
struct pollfd fds[] = {{-1}, {3}};
|
struct pollfd fds[] = {{-1, 0, -1}, {3, 0, -1}};
|
||||||
EXPECT_SYS(0, 0, poll(fds, ARRAYLEN(fds), 1));
|
EXPECT_SYS(0, 0, poll(fds, ARRAYLEN(fds), 1));
|
||||||
EXPECT_STREQ("fd:-1 revents:<TODO:kPollNames>\n"
|
EXPECT_EQ(-1, fds[0].fd);
|
||||||
"fd:3 revents:<TODO:kPollNames>\n",
|
EXPECT_EQ(0, fds[0].revents);
|
||||||
gc(FormatPollFd(&fds[0])));
|
EXPECT_EQ(3, fds[1].fd);
|
||||||
|
EXPECT_EQ(0, fds[1].revents);
|
||||||
ASSERT_SYS(0, 0, close(3));
|
ASSERT_SYS(0, 0, close(3));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(poll, testInvalidFd_POLLIN_isChecked) {
|
||||||
|
struct pollfd fds[] = {{77, POLLIN, -1}};
|
||||||
|
EXPECT_SYS(0, 1, poll(fds, ARRAYLEN(fds), 1));
|
||||||
|
EXPECT_EQ(77, fds[0].fd);
|
||||||
|
EXPECT_EQ(POLLNVAL, fds[0].revents);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(poll, testInvalidFd_POLLOUT_isChecked) {
|
||||||
|
struct pollfd fds[] = {{77, POLLOUT, -1}};
|
||||||
|
EXPECT_SYS(0, 1, poll(fds, ARRAYLEN(fds), 1));
|
||||||
|
EXPECT_EQ(77, fds[0].fd);
|
||||||
|
EXPECT_EQ(POLLNVAL, fds[0].revents);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(poll, testInvalidFd_POLLPRI_isChecked) {
|
||||||
|
struct pollfd fds[] = {{77, POLLPRI, -1}};
|
||||||
|
EXPECT_SYS(0, 1, poll(fds, ARRAYLEN(fds), 1));
|
||||||
|
EXPECT_EQ(77, fds[0].fd);
|
||||||
|
EXPECT_EQ(POLLNVAL, fds[0].revents);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(poll, testInvalidFd_POLLHUP_isChecked) {
|
||||||
|
// this behavior has to be polyfilled on xnu
|
||||||
|
struct pollfd fds[] = {{77, POLLHUP, -1}};
|
||||||
|
EXPECT_SYS(0, 1, poll(fds, ARRAYLEN(fds), 1));
|
||||||
|
EXPECT_EQ(77, fds[0].fd);
|
||||||
|
EXPECT_EQ(POLLNVAL, fds[0].revents);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(poll, testInvalidFd_ZERO_isChecked) {
|
||||||
|
// this behavior has to be polyfilled on xnu
|
||||||
|
struct pollfd fds[] = {{77, 0, -1}};
|
||||||
|
EXPECT_SYS(0, 1, poll(fds, ARRAYLEN(fds), 1));
|
||||||
|
EXPECT_EQ(77, fds[0].fd);
|
||||||
|
EXPECT_EQ(POLLNVAL, fds[0].revents);
|
||||||
|
}
|
||||||
|
|
||||||
TEST(poll, pipe_noInput) {
|
TEST(poll, pipe_noInput) {
|
||||||
// we can't test stdin here since
|
// we can't test stdin here since
|
||||||
// we can't assume it isn't /dev/null
|
// we can't assume it isn't /dev/null
|
||||||
|
@ -115,6 +148,17 @@ TEST(poll, pipe_noInput) {
|
||||||
EXPECT_SYS(0, 0, close(pipefds[1]));
|
EXPECT_SYS(0, 0, close(pipefds[1]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(poll, pipe_broken) {
|
||||||
|
int pipefds[2];
|
||||||
|
EXPECT_SYS(0, 0, pipe(pipefds));
|
||||||
|
EXPECT_SYS(0, 0, close(pipefds[1]));
|
||||||
|
struct pollfd fds[] = {{pipefds[0], POLLIN}};
|
||||||
|
EXPECT_SYS(0, 1, poll(fds, 1, 0));
|
||||||
|
// BSDs also set POLLIN here too even though that's wrong
|
||||||
|
EXPECT_TRUE(!!(fds[0].revents & POLLHUP));
|
||||||
|
EXPECT_SYS(0, 0, close(pipefds[0]));
|
||||||
|
}
|
||||||
|
|
||||||
TEST(poll, pipe_hasInputFromSameProcess) {
|
TEST(poll, pipe_hasInputFromSameProcess) {
|
||||||
char buf[2];
|
char buf[2];
|
||||||
int pipefds[2];
|
int pipefds[2];
|
||||||
|
@ -122,7 +166,7 @@ TEST(poll, pipe_hasInputFromSameProcess) {
|
||||||
struct pollfd fds[] = {{pipefds[0], POLLIN}};
|
struct pollfd fds[] = {{pipefds[0], POLLIN}};
|
||||||
EXPECT_SYS(0, 2, write(pipefds[1], "hi", 2));
|
EXPECT_SYS(0, 2, write(pipefds[1], "hi", 2));
|
||||||
EXPECT_SYS(0, 1, poll(fds, 1, 1000)); // flake nt!
|
EXPECT_SYS(0, 1, poll(fds, 1, 1000)); // flake nt!
|
||||||
EXPECT_EQ(POLLIN, fds[0].revents);
|
EXPECT_TRUE(!!(fds[0].revents & POLLIN));
|
||||||
EXPECT_SYS(0, 2, read(pipefds[0], buf, 2));
|
EXPECT_SYS(0, 2, read(pipefds[0], buf, 2));
|
||||||
EXPECT_SYS(0, 0, poll(fds, 1, 0));
|
EXPECT_SYS(0, 0, poll(fds, 1, 0));
|
||||||
EXPECT_SYS(0, 0, close(pipefds[0]));
|
EXPECT_SYS(0, 0, close(pipefds[0]));
|
||||||
|
@ -150,7 +194,7 @@ TEST(poll, pipe_hasInput) {
|
||||||
EXPECT_SYS(0, 2, read(pipefds[0], buf, 2));
|
EXPECT_SYS(0, 2, read(pipefds[0], buf, 2));
|
||||||
struct pollfd fds[] = {{pipefds[0], POLLIN}};
|
struct pollfd fds[] = {{pipefds[0], POLLIN}};
|
||||||
EXPECT_SYS(0, 1, poll(fds, 1, -1));
|
EXPECT_SYS(0, 1, poll(fds, 1, -1));
|
||||||
EXPECT_EQ(POLLIN, fds[0].revents & POLLIN);
|
EXPECT_TRUE(!!(fds[0].revents & POLLIN));
|
||||||
EXPECT_SYS(0, 2, read(pipefds[0], buf, 2));
|
EXPECT_SYS(0, 2, read(pipefds[0], buf, 2));
|
||||||
EXPECT_SYS(0, 0, close(pipefds[0]));
|
EXPECT_SYS(0, 0, close(pipefds[0]));
|
||||||
ASSERT_NE(-1, wait(&ws));
|
ASSERT_NE(-1, wait(&ws));
|
||||||
|
|
|
@ -40,6 +40,7 @@ TEST_LIBC_STDIO_DIRECTDEPS = \
|
||||||
LIBC_THREAD \
|
LIBC_THREAD \
|
||||||
LIBC_LOG \
|
LIBC_LOG \
|
||||||
LIBC_X \
|
LIBC_X \
|
||||||
|
THIRD_PARTY_COMPILER_RT \
|
||||||
THIRD_PARTY_GDTOA \
|
THIRD_PARTY_GDTOA \
|
||||||
THIRD_PARTY_MBEDTLS \
|
THIRD_PARTY_MBEDTLS \
|
||||||
THIRD_PARTY_MUSL \
|
THIRD_PARTY_MUSL \
|
||||||
|
|
6
third_party/python/BUILD.mk
vendored
6
third_party/python/BUILD.mk
vendored
|
@ -1844,7 +1844,6 @@ THIRD_PARTY_PYTHON_PYTEST_PYMAINS = \
|
||||||
third_party/python/Lib/test/test_enum.py \
|
third_party/python/Lib/test/test_enum.py \
|
||||||
third_party/python/Lib/test/test_enumerate.py \
|
third_party/python/Lib/test/test_enumerate.py \
|
||||||
third_party/python/Lib/test/test_eof.py \
|
third_party/python/Lib/test/test_eof.py \
|
||||||
third_party/python/Lib/test/test_epoll.py \
|
|
||||||
third_party/python/Lib/test/test_errno.py \
|
third_party/python/Lib/test/test_errno.py \
|
||||||
third_party/python/Lib/test/test_exception_hierarchy.py \
|
third_party/python/Lib/test/test_exception_hierarchy.py \
|
||||||
third_party/python/Lib/test/test_exception_variations.py \
|
third_party/python/Lib/test/test_exception_variations.py \
|
||||||
|
@ -2148,8 +2147,6 @@ o/$(MODE)/third_party/python/Lib/test/test_wsgiref.py.runs: private \
|
||||||
/usr/local/etc/httpd/conf/mime.types \
|
/usr/local/etc/httpd/conf/mime.types \
|
||||||
/usr/local/etc/mime.types
|
/usr/local/etc/mime.types
|
||||||
|
|
||||||
o/$(MODE)/third_party/python/Lib/test/test_epoll.py.runs: \
|
|
||||||
private .PLEDGE = stdio rpath wpath cpath fattr proc inet
|
|
||||||
o/$(MODE)/third_party/python/Lib/test/test_wsgiref.py.runs: \
|
o/$(MODE)/third_party/python/Lib/test/test_wsgiref.py.runs: \
|
||||||
private .PLEDGE = stdio rpath wpath cpath fattr proc inet
|
private .PLEDGE = stdio rpath wpath cpath fattr proc inet
|
||||||
o/$(MODE)/third_party/python/Lib/test/test_fcntl.py.runs: \
|
o/$(MODE)/third_party/python/Lib/test/test_fcntl.py.runs: \
|
||||||
|
@ -2787,9 +2784,6 @@ o/$(MODE)/third_party/python/Lib/test/test_dis.py.runs: $(PYTHONTESTER)
|
||||||
o/$(MODE)/third_party/python/Lib/test/test_asyncore.py.runs: $(PYTHONTESTER)
|
o/$(MODE)/third_party/python/Lib/test/test_asyncore.py.runs: $(PYTHONTESTER)
|
||||||
@$(COMPILE) -ACHECK -wtT$@ $(PYHARNESSARGS) $(PYTHONTESTER) -m test.test_asyncore $(PYTESTARGS)
|
@$(COMPILE) -ACHECK -wtT$@ $(PYHARNESSARGS) $(PYTHONTESTER) -m test.test_asyncore $(PYTESTARGS)
|
||||||
|
|
||||||
o/$(MODE)/third_party/python/Lib/test/test_epoll.py.runs: $(PYTHONTESTER)
|
|
||||||
@$(COMPILE) -ACHECK -wtT$@ $(PYHARNESSARGS) $(PYTHONTESTER) -m test.test_epoll $(PYTESTARGS)
|
|
||||||
|
|
||||||
o/$(MODE)/third_party/python/Lib/test/test_cmd_line.py.runs: $(PYTHONTESTER)
|
o/$(MODE)/third_party/python/Lib/test/test_cmd_line.py.runs: $(PYTHONTESTER)
|
||||||
@$(COMPILE) -ACHECK -wtT$@ $(PYHARNESSARGS) $(PYTHONTESTER) -m test.test_cmd_line $(PYTESTARGS)
|
@$(COMPILE) -ACHECK -wtT$@ $(PYHARNESSARGS) $(PYTHONTESTER) -m test.test_cmd_line $(PYTESTARGS)
|
||||||
|
|
||||||
|
|
|
@ -486,7 +486,7 @@ build_time_vars = {'ABIFLAGS': 'm',
|
||||||
'HAVE_SYS_DEVPOLL_H': 0,
|
'HAVE_SYS_DEVPOLL_H': 0,
|
||||||
'HAVE_SYS_DIR_H': 1,
|
'HAVE_SYS_DIR_H': 1,
|
||||||
'HAVE_SYS_ENDIAN_H': 0,
|
'HAVE_SYS_ENDIAN_H': 0,
|
||||||
'HAVE_SYS_EPOLL_H': 1,
|
'HAVE_SYS_EPOLL_H': 0,
|
||||||
'HAVE_SYS_EVENT_H': 0,
|
'HAVE_SYS_EVENT_H': 0,
|
||||||
'HAVE_SYS_FILE_H': 1,
|
'HAVE_SYS_FILE_H': 1,
|
||||||
'HAVE_SYS_IOCTL_H': 1,
|
'HAVE_SYS_IOCTL_H': 1,
|
||||||
|
|
18
third_party/python/Modules/selectmodule.c
vendored
18
third_party/python/Modules/selectmodule.c
vendored
|
@ -10,11 +10,9 @@
|
||||||
#include "libc/mem/gc.h"
|
#include "libc/mem/gc.h"
|
||||||
#include "libc/mem/mem.h"
|
#include "libc/mem/mem.h"
|
||||||
#include "libc/nt/efi.h"
|
#include "libc/nt/efi.h"
|
||||||
#include "libc/sock/epoll.h"
|
|
||||||
#include "libc/sock/select.h"
|
#include "libc/sock/select.h"
|
||||||
#include "libc/sock/sock.h"
|
#include "libc/sock/sock.h"
|
||||||
#include "libc/sock/struct/pollfd.h"
|
#include "libc/sock/struct/pollfd.h"
|
||||||
#include "libc/sysv/consts/epoll.h"
|
|
||||||
#include "libc/sysv/consts/poll.h"
|
#include "libc/sysv/consts/poll.h"
|
||||||
#include "third_party/python/Include/abstract.h"
|
#include "third_party/python/Include/abstract.h"
|
||||||
#include "third_party/python/Include/boolobject.h"
|
#include "third_party/python/Include/boolobject.h"
|
||||||
|
@ -35,21 +33,6 @@
|
||||||
#include "third_party/python/pyconfig.h"
|
#include "third_party/python/pyconfig.h"
|
||||||
|
|
||||||
PYTHON_PROVIDE("select");
|
PYTHON_PROVIDE("select");
|
||||||
PYTHON_PROVIDE("select.EPOLLERR");
|
|
||||||
PYTHON_PROVIDE("select.EPOLLET");
|
|
||||||
PYTHON_PROVIDE("select.EPOLLEXCLUSIVE");
|
|
||||||
PYTHON_PROVIDE("select.EPOLLHUP");
|
|
||||||
PYTHON_PROVIDE("select.EPOLLIN");
|
|
||||||
PYTHON_PROVIDE("select.EPOLLMSG");
|
|
||||||
PYTHON_PROVIDE("select.EPOLLONESHOT");
|
|
||||||
PYTHON_PROVIDE("select.EPOLLOUT");
|
|
||||||
PYTHON_PROVIDE("select.EPOLLPRI");
|
|
||||||
PYTHON_PROVIDE("select.EPOLLRDBAND");
|
|
||||||
PYTHON_PROVIDE("select.EPOLLRDHUP");
|
|
||||||
PYTHON_PROVIDE("select.EPOLLRDNORM");
|
|
||||||
PYTHON_PROVIDE("select.EPOLLWRBAND");
|
|
||||||
PYTHON_PROVIDE("select.EPOLLWRNORM");
|
|
||||||
PYTHON_PROVIDE("select.EPOLL_CLOEXEC");
|
|
||||||
PYTHON_PROVIDE("select.POLLERR");
|
PYTHON_PROVIDE("select.POLLERR");
|
||||||
PYTHON_PROVIDE("select.POLLHUP");
|
PYTHON_PROVIDE("select.POLLHUP");
|
||||||
PYTHON_PROVIDE("select.POLLIN");
|
PYTHON_PROVIDE("select.POLLIN");
|
||||||
|
@ -61,7 +44,6 @@ PYTHON_PROVIDE("select.POLLRDHUP");
|
||||||
PYTHON_PROVIDE("select.POLLRDNORM");
|
PYTHON_PROVIDE("select.POLLRDNORM");
|
||||||
PYTHON_PROVIDE("select.POLLWRBAND");
|
PYTHON_PROVIDE("select.POLLWRBAND");
|
||||||
PYTHON_PROVIDE("select.POLLWRNORM");
|
PYTHON_PROVIDE("select.POLLWRNORM");
|
||||||
PYTHON_PROVIDE("select.epoll");
|
|
||||||
PYTHON_PROVIDE("select.error");
|
PYTHON_PROVIDE("select.error");
|
||||||
PYTHON_PROVIDE("select.poll");
|
PYTHON_PROVIDE("select.poll");
|
||||||
PYTHON_PROVIDE("select.select");
|
PYTHON_PROVIDE("select.select");
|
||||||
|
|
4
third_party/python/pyconfig.h
vendored
4
third_party/python/pyconfig.h
vendored
|
@ -122,8 +122,8 @@
|
||||||
#define HAVE_DIRENT_D_TYPE 1
|
#define HAVE_DIRENT_D_TYPE 1
|
||||||
#define HAVE_DUP2 1
|
#define HAVE_DUP2 1
|
||||||
#define HAVE_DUP3 1
|
#define HAVE_DUP3 1
|
||||||
#define HAVE_EPOLL 1
|
// #define HAVE_EPOLL 1
|
||||||
#define HAVE_EPOLL_CREATE1 1
|
// #define HAVE_EPOLL_CREATE1 1
|
||||||
#define HAVE_ERF 1
|
#define HAVE_ERF 1
|
||||||
#define HAVE_ERFC 1
|
#define HAVE_ERFC 1
|
||||||
#define HAVE_EXECV 1
|
#define HAVE_EXECV 1
|
||||||
|
|
Loading…
Add table
Reference in a new issue