From 2db2f40a98565eb82df1a50cd44daa02fb28350a Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Sat, 14 Oct 2023 01:06:00 -0700 Subject: [PATCH] Rewrite special file handling on Windows This change gets GNU grep working. What caused it to not work, is it wouldn't write to an output file descriptor when its dev/ino equaled /dev/null's. So now we invent special dev/ino values for these files --- libc/calls/abort.c | 4 +- libc/calls/fcntl-nt.c | 55 ++----------- libc/calls/fstat-nt.c | 43 +++++++++- libc/calls/fstat.c | 2 +- libc/calls/fstatat-nt.c | 44 ++++++++-- libc/calls/getloadavg-nt.c | 3 + libc/calls/getuid-nt.c | 12 +-- libc/calls/ioctl.c | 3 + libc/calls/ischardev.c | 2 +- libc/calls/lseek-nt.c | 4 +- libc/calls/mkntpath.c | 20 ----- libc/calls/ntmagicpaths.inc | 7 -- libc/calls/ntmagicpaths.internal.h | 16 ---- libc/calls/open-nt.c | 72 ++++++++++++---- libc/calls/openat-sysv.c | 8 +- libc/calls/park.c | 10 +-- libc/calls/pause-nt.c | 1 - libc/calls/poll-nt.c | 27 +++--- libc/calls/pread.c | 2 +- libc/calls/printfds.c | 2 + libc/calls/pwrite.c | 2 +- libc/calls/read-nt.c | 10 ++- libc/calls/readv-nt.c | 1 + libc/calls/readwrite-nt.c | 4 +- libc/calls/setfl.c | 71 ++++++++++++++++ libc/calls/sig.c | 11 ++- libc/calls/sigignore.c | 5 +- libc/calls/sigpending.c | 4 +- libc/calls/state.internal.h | 1 + libc/calls/struct/fd.internal.h | 1 + libc/calls/struct/stat.internal.h | 4 +- libc/calls/syscall_support-nt.internal.h | 1 + libc/calls/writev-nt.c | 1 + libc/intrin/describestat.c | 4 +- libc/{calls/ntmagicpaths.c => intrin/fnv.c} | 19 +++-- .../pthread_unref.c} | 43 ++++------ libc/intrin/reopenfile.c | 2 - libc/log/libfatal.internal.h | 14 ---- libc/proc/cocmd.c | 12 ++- libc/proc/fork-nt.c | 4 +- libc/proc/getrusage-nt.c | 3 +- libc/runtime/mprotect-nt.greg.c | 3 + libc/runtime/zipos-inode.c | 12 +-- libc/str/str.h | 1 + libc/thread/posixthread.internal.h | 21 +++-- libc/thread/pthread_create.c | 23 +++++- libc/thread/pthread_timedjoin_np.c | 3 +- test/libc/calls/specialfile_test.c | 82 +++++++++++++++++++ test/libc/proc/system_test.c | 9 ++ third_party/nsync/futex.c | 7 +- tool/build/runitd.c | 5 +- tool/net/redbean.c | 8 +- tool/net/winbench.c | 54 +++++------- 53 files changed, 484 insertions(+), 298 deletions(-) delete mode 100644 libc/calls/ntmagicpaths.inc delete mode 100644 libc/calls/ntmagicpaths.internal.h create mode 100644 libc/calls/setfl.c rename libc/{calls/ntmagicpaths.c => intrin/fnv.c} (84%) rename libc/{thread/pthread_decimate.c => intrin/pthread_unref.c} (66%) create mode 100644 test/libc/calls/specialfile_test.c diff --git a/libc/calls/abort.c b/libc/calls/abort.c index cbe973e801d..ce8150e9284 100644 --- a/libc/calls/abort.c +++ b/libc/calls/abort.c @@ -33,9 +33,7 @@ * @noreturn */ wontreturn void abort(void) { - sigset_t m; - sigemptyset(&m); - sigaddset(&m, SIGABRT); + sigset_t m = 1ull << (SIGABRT - 1); sigprocmask(SIG_UNBLOCK, &m, 0); raise(SIGABRT); signal(SIGABRT, SIG_DFL); diff --git a/libc/calls/fcntl-nt.c b/libc/calls/fcntl-nt.c index eb5fe2036b7..51e44d64039 100644 --- a/libc/calls/fcntl-nt.c +++ b/libc/calls/fcntl-nt.c @@ -321,63 +321,18 @@ static textwindows int sys_fcntl_nt_dupfd(int fd, int cmd, int start) { return sys_dup_nt(fd, -1, (cmd == F_DUPFD_CLOEXEC ? O_CLOEXEC : 0), start); } -static textwindows int sys_fcntl_nt_setfl(int fd, unsigned *flags, - unsigned mode, unsigned arg, - intptr_t *handle) { - - // you may change the following: - // - // - O_NONBLOCK make read() raise EAGAIN - // - O_APPEND for toggling append mode - // - O_RANDOM alt. for posix_fadvise() - // - O_SEQUENTIAL alt. for posix_fadvise() - // - O_DIRECT works but haven't tested - // - // the other bits are ignored. - unsigned allowed = O_APPEND | O_SEQUENTIAL | O_RANDOM | O_DIRECT | O_NONBLOCK; - unsigned needreo = O_APPEND | O_SEQUENTIAL | O_RANDOM | O_DIRECT; - unsigned newflag = (*flags & ~allowed) | (arg & allowed); - - if ((*flags & needreo) ^ (arg & needreo)) { - unsigned perm, share, attr; - if (GetNtOpenFlags(newflag, mode, &perm, &share, 0, &attr) == -1) { - return -1; - } - // MSDN says only these are allowed, otherwise it returns EINVAL. - attr &= kNtFileFlagBackupSemantics | kNtFileFlagDeleteOnClose | - kNtFileFlagNoBuffering | kNtFileFlagOpenNoRecall | - kNtFileFlagOpenReparsePoint | kNtFileFlagOverlapped | - kNtFileFlagPosixSemantics | kNtFileFlagRandomAccess | - kNtFileFlagSequentialScan | kNtFileFlagWriteThrough; - intptr_t hand; - if ((hand = ReOpenFile(*handle, perm, share, attr)) != -1) { - if (hand != *handle) { - CloseHandle(*handle); - *handle = hand; - } - } else { - return __winerr(); - } - } - - // 1. ignore flags that aren't access mode flags - // 2. return zero if nothing's changed - *flags = newflag; - return 0; -} - textwindows int sys_fcntl_nt(int fd, int cmd, uintptr_t arg) { int rc; BLOCK_SIGNALS; - if (__isfdkind(fd, kFdFile) || // - __isfdkind(fd, kFdSocket) || // - __isfdkind(fd, kFdConsole)) { + if (__isfdkind(fd, kFdFile) || // + __isfdkind(fd, kFdSocket) || // + __isfdkind(fd, kFdConsole) || // + __isfdkind(fd, kFdDevNull)) { if (cmd == F_GETFL) { rc = g_fds.p[fd].flags & (O_ACCMODE | O_APPEND | O_DIRECT | O_NONBLOCK | O_RANDOM | O_SEQUENTIAL); } else if (cmd == F_SETFL) { - rc = sys_fcntl_nt_setfl(fd, &g_fds.p[fd].flags, g_fds.p[fd].mode, arg, - &g_fds.p[fd].handle); + rc = sys_fcntl_nt_setfl(fd, arg); } else if (cmd == F_GETFD) { if (g_fds.p[fd].flags & O_CLOEXEC) { rc = FD_CLOEXEC; diff --git a/libc/calls/fstat-nt.c b/libc/calls/fstat-nt.c index bf563c98824..b71f17ac146 100644 --- a/libc/calls/fstat-nt.c +++ b/libc/calls/fstat-nt.c @@ -19,7 +19,9 @@ #include "libc/assert.h" #include "libc/calls/calls.h" #include "libc/calls/internal.h" +#include "libc/calls/struct/fd.internal.h" #include "libc/calls/struct/stat.h" +#include "libc/calls/struct/stat.internal.h" #include "libc/calls/syscall_support-nt.internal.h" #include "libc/fmt/wintime.internal.h" #include "libc/intrin/atomic.h" @@ -76,13 +78,47 @@ static textwindows long GetSizeOfReparsePoint(int64_t fh) { return z; } -textwindows int sys_fstat_nt(int64_t handle, struct stat *out_st) { +static textwindows int sys_fstat_nt_socket(int kind, struct stat *st) { + bzero(st, sizeof(*st)); + st->st_blksize = 512; + st->st_mode = S_IFSOCK | 0666; + st->st_dev = 0x44444444; + st->st_ino = kind; + return 0; +} + +textwindows int sys_fstat_nt_special(int kind, struct stat *st) { + bzero(st, sizeof(*st)); + st->st_blksize = 512; + st->st_mode = S_IFCHR | 0666; + st->st_dev = 0x77777777; + st->st_ino = kind; + return 0; +} + +textwindows int sys_fstat_nt(int fd, struct stat *st) { + if (fd + 0u >= g_fds.n) return ebadf(); + switch (g_fds.p[fd].kind) { + case kFdEmpty: + return ebadf(); + case kFdConsole: + case kFdDevNull: + return sys_fstat_nt_special(g_fds.p[fd].kind, st); + case kFdSocket: + return sys_fstat_nt_socket(g_fds.p[fd].kind, st); + default: + return sys_fstat_nt_handle(g_fds.p[fd].handle, st); + } +} + +textwindows int sys_fstat_nt_handle(int64_t handle, struct stat *out_st) { struct stat st = {0}; // Always set st_blksize to avoid divide by zero issues. // The Linux kernel sets this for /dev/tty and similar too. // TODO(jart): GetVolumeInformationByHandle? st.st_blksize = 4096; + st.st_gid = st.st_uid = sys_getuid_nt(); // We'll use the "umask" to fake out the mode bits. uint32_t umask = atomic_load_explicit(&__umask, memory_order_acquire); @@ -92,9 +128,13 @@ textwindows int sys_fstat_nt(int64_t handle, struct stat *out_st) { break; case kNtFileTypeChar: st.st_mode = S_IFCHR | (0666 & ~umask); + st.st_dev = 0x66666666; + st.st_ino = handle; break; case kNtFileTypePipe: st.st_mode = S_IFIFO | (0666 & ~umask); + st.st_dev = 0x55555555; + st.st_ino = handle; break; case kNtFileTypeDisk: { struct NtByHandleFileInformation wst; @@ -126,7 +166,6 @@ textwindows int sys_fstat_nt(int64_t handle, struct stat *out_st) { } else { st.st_ctim = st.st_mtim; } - st.st_gid = st.st_uid = sys_getuid_nt(); st.st_size = (wst.nFileSizeHigh + 0ull) << 32 | wst.nFileSizeLow; st.st_dev = wst.dwVolumeSerialNumber; st.st_ino = (wst.nFileIndexHigh + 0ull) << 32 | wst.nFileIndexLow; diff --git a/libc/calls/fstat.c b/libc/calls/fstat.c index 6355de08a72..017d3c2d754 100644 --- a/libc/calls/fstat.c +++ b/libc/calls/fstat.c @@ -52,7 +52,7 @@ int fstat(int fd, struct stat *st) { } else if (IsMetal()) { rc = sys_fstat_metal(fd, st); } else if (IsWindows()) { - rc = sys_fstat_nt(__getfdhandleactual(fd), st); + rc = sys_fstat_nt(fd, st); } else { rc = enosys(); } diff --git a/libc/calls/fstatat-nt.c b/libc/calls/fstatat-nt.c index 8a3b7f93d7f..720110ae7ff 100644 --- a/libc/calls/fstatat-nt.c +++ b/libc/calls/fstatat-nt.c @@ -16,9 +16,8 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/sig.internal.h" +#include "libc/calls/struct/fd.internal.h" #include "libc/calls/struct/sigset.internal.h" -#include "libc/calls/struct/stat.h" #include "libc/calls/struct/stat.internal.h" #include "libc/calls/syscall_support-nt.internal.h" #include "libc/errno.h" @@ -29,18 +28,43 @@ #include "libc/nt/enum/filesharemode.h" #include "libc/nt/errors.h" #include "libc/nt/runtime.h" +#include "libc/str/str.h" #include "libc/sysv/consts/at.h" +#include "libc/sysv/consts/fileno.h" +#include "libc/sysv/errfuns.h" textwindows int sys_fstatat_nt(int dirfd, const char *path, struct stat *st, int flags) { - int rc, e; - int64_t fh; - uint32_t dwDesiredAccess; + + // handle special files + if (startswith(path, "/dev/")) { + if (!strcmp(path + 5, "tty")) { + return sys_fstat_nt_special(kFdConsole, st); + } else if (!strcmp(path + 5, "null")) { + return sys_fstat_nt_special(kFdDevNull, st); + } else if (!strcmp(path + 5, "stdin")) { + return sys_fstat_nt(STDIN_FILENO, st); + } else if (!strcmp(path + 5, "stdout")) { + return sys_fstat_nt(STDOUT_FILENO, st); + } else if (!strcmp(path + 5, "stderr")) { + return sys_fstat_nt(STDERR_FILENO, st); + } else { + return enoent(); + } + } + + // convert path from utf-8 to utf-16 uint16_t path16[PATH_MAX]; - if (__mkntpathat(dirfd, path, 0, path16) == -1) return -1; + if (__mkntpathat(dirfd, path, 0, path16) == -1) { + return -1; + } + + // open an actual file + int rc; + int64_t fh; + int e = errno; + uint32_t dwDesiredAccess = kNtFileGenericRead; BLOCK_SIGNALS; - e = errno; - dwDesiredAccess = kNtFileGenericRead; TryAgain: if ((fh = CreateFile( path16, dwDesiredAccess, @@ -50,7 +74,7 @@ textwindows int sys_fstatat_nt(int dirfd, const char *path, struct stat *st, ((flags & AT_SYMLINK_NOFOLLOW) ? kNtFileFlagOpenReparsePoint : 0), 0)) != -1) { - rc = st ? sys_fstat_nt(fh, st) : 0; + rc = st ? sys_fstat_nt_handle(fh, st) : 0; CloseHandle(fh); } else if (dwDesiredAccess == kNtFileGenericRead && GetLastError() == kNtErrorSharingViolation) { @@ -61,5 +85,7 @@ textwindows int sys_fstatat_nt(int dirfd, const char *path, struct stat *st, rc = __winerr(); } ALLOW_SIGNALS; + + // mop up errors return __fix_enotdir(rc, path16); } diff --git a/libc/calls/getloadavg-nt.c b/libc/calls/getloadavg-nt.c index 6e90db64baf..fd48d1bd91c 100644 --- a/libc/calls/getloadavg-nt.c +++ b/libc/calls/getloadavg-nt.c @@ -17,6 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" +#include "libc/calls/struct/sigset.internal.h" #include "libc/calls/syscall_support-nt.internal.h" #include "libc/dce.h" #include "libc/fmt/conv.h" @@ -36,6 +37,7 @@ textwindows int sys_getloadavg_nt(double *a, int n) { int i, rc; uint64_t elapsed, used; struct NtFileTime idle, kern, user; + BLOCK_SIGNALS; pthread_spin_lock(&lock); if (GetSystemTimes(&idle, &kern, &user)) { elapsed = (FT(kern) - FT(kern1)) + (FT(user) - FT(user1)); @@ -53,6 +55,7 @@ textwindows int sys_getloadavg_nt(double *a, int n) { rc = __winerr(); } pthread_spin_unlock(&lock); + ALLOW_SIGNALS; return rc; } diff --git a/libc/calls/getuid-nt.c b/libc/calls/getuid-nt.c index 0de3eba66db..1cb4ad292ca 100644 --- a/libc/calls/getuid-nt.c +++ b/libc/calls/getuid-nt.c @@ -20,15 +20,7 @@ #include "libc/intrin/atomic.h" #include "libc/macros.internal.h" #include "libc/nt/accounting.h" - -static textwindows uint32_t __kmp32(const void *buf, size_t size) { - size_t i; - uint32_t h; - const uint32_t kPhiPrime = 0x9e3779b1; - const unsigned char *p = (const unsigned char *)buf; - for (h = i = 0; i < size; i++) h = (p[i] + h) * kPhiPrime; - return h; -} +#include "libc/str/str.h" textwindows uint32_t sys_getuid_nt(void) { char16_t buf[257]; @@ -36,7 +28,7 @@ textwindows uint32_t sys_getuid_nt(void) { uint32_t tmp, size = ARRAYLEN(buf); if (!(tmp = atomic_load_explicit(&uid, memory_order_acquire))) { GetUserName(&buf, &size); - tmp = __kmp32(buf, size >> 1) & 32767; + tmp = __fnv(buf, size >> 1) & 32767; if (!tmp) ++tmp; atomic_store_explicit(&uid, tmp, memory_order_release); } diff --git a/libc/calls/ioctl.c b/libc/calls/ioctl.c index 8bd31b81467..aca879bdbd7 100644 --- a/libc/calls/ioctl.c +++ b/libc/calls/ioctl.c @@ -106,6 +106,9 @@ static int ioctl_fionread(int fd, uint32_t *arg) { int bytes = CountConsoleInputBytes(); *arg = MAX(0, bytes); return 0; + } else if (g_fds.p[fd].kind == kFdDevNull) { + *arg = 1; + return 0; } else if (GetFileType(handle) == kNtFileTypePipe) { uint32_t avail; if (PeekNamedPipe(handle, 0, 0, 0, &avail, 0)) { diff --git a/libc/calls/ischardev.c b/libc/calls/ischardev.c index 6d460d7f4a5..06e02653126 100644 --- a/libc/calls/ischardev.c +++ b/libc/calls/ischardev.c @@ -54,7 +54,7 @@ bool32 ischardev(int fd) { return false; } } else { - return __isfdkind(fd, kFdConsole) || + return __isfdkind(fd, kFdConsole) || __isfdkind(fd, kFdDevNull) || (__isfdkind(fd, kFdFile) && GetFileType(__getfdhandleactual(fd)) == kNtFileTypeChar); } diff --git a/libc/calls/lseek-nt.c b/libc/calls/lseek-nt.c index 27b83312760..82e0f2b58d7 100644 --- a/libc/calls/lseek-nt.c +++ b/libc/calls/lseek-nt.c @@ -62,7 +62,9 @@ static textwindows int64_t Seek(struct Fd *f, int64_t offset, int whence) { } textwindows int64_t sys_lseek_nt(int fd, int64_t offset, int whence) { - if (__isfdkind(fd, kFdFile)) { + if (__isfdkind(fd, kFdDevNull)) { + return offset; + } else if (__isfdkind(fd, kFdFile)) { struct Fd *f = g_fds.p + fd; int filetype = GetFileType(f->handle); if (filetype != kNtFileTypePipe && filetype != kNtFileTypeChar) { diff --git a/libc/calls/mkntpath.c b/libc/calls/mkntpath.c index b5dff02894b..add261725e0 100644 --- a/libc/calls/mkntpath.c +++ b/libc/calls/mkntpath.c @@ -16,7 +16,6 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/ntmagicpaths.internal.h" #include "libc/calls/syscall_support-nt.internal.h" #include "libc/dce.h" #include "libc/intrin/asan.internal.h" @@ -36,24 +35,6 @@ static inline int IsAlpha(int c) { return ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z'); } -textwindows static const char *FixNtMagicPath(const char *path, - unsigned flags) { - const struct NtMagicPaths *mp = &kNtMagicPaths; - asm("" : "+r"(mp)); - if (!IsSlash(path[0])) return path; - if (strcmp(path, mp->devtty) == 0) { - if ((flags & O_ACCMODE) == O_RDONLY) { - return mp->conin; - } else if ((flags & O_ACCMODE) == O_WRONLY) { - return mp->conout; - } - } - if (strcmp(path, mp->devnull) == 0) return mp->nul; - if (strcmp(path, mp->devstdin) == 0) return mp->conin; - if (strcmp(path, mp->devstdout) == 0) return mp->conout; - return path; -} - textwindows size_t __normntpath(char16_t *p, size_t n) { size_t i, j; for (j = i = 0; i < n; ++i) { @@ -116,7 +97,6 @@ textwindows int __mkntpath2(const char *path, if (!path || (IsAsan() && !__asan_is_valid_str(path))) { return efault(); } - path = FixNtMagicPath(path, flags); size_t x, z; char16_t *p = path16; diff --git a/libc/calls/ntmagicpaths.inc b/libc/calls/ntmagicpaths.inc deleted file mode 100644 index e052ccf0570..00000000000 --- a/libc/calls/ntmagicpaths.inc +++ /dev/null @@ -1,7 +0,0 @@ -TAB(devtty, "/dev/tty") -TAB(devnull, "/dev/null") -TAB(devstdin, "/dev/stdin") -TAB(devstdout, "/dev/stdout") -TAB(nul, "NUL") -TAB(conin, "CONIN$") -TAB(conout, "CONOUT$") diff --git a/libc/calls/ntmagicpaths.internal.h b/libc/calls/ntmagicpaths.internal.h deleted file mode 100644 index 20b2be0fd22..00000000000 --- a/libc/calls/ntmagicpaths.internal.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef COSMOPOLITAN_LIBC_CALLS_NTMAGICPATHS_H_ -#define COSMOPOLITAN_LIBC_CALLS_NTMAGICPATHS_H_ -#if !(__ASSEMBLER__ + __LINKER__ + 0) -COSMOPOLITAN_C_START_ - -struct NtMagicPaths { -#define TAB(NAME, STRING) char NAME[sizeof(STRING)]; -#include "libc/calls/ntmagicpaths.inc" -#undef TAB -}; - -extern const struct NtMagicPaths kNtMagicPaths; - -COSMOPOLITAN_C_END_ -#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ -#endif /* COSMOPOLITAN_LIBC_CALLS_NTMAGICPATHS_H_ */ diff --git a/libc/calls/open-nt.c b/libc/calls/open-nt.c index ba3d70b2133..570ff6d59f4 100644 --- a/libc/calls/open-nt.c +++ b/libc/calls/open-nt.c @@ -18,8 +18,8 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/assert.h" #include "libc/calls/internal.h" -#include "libc/calls/ntmagicpaths.internal.h" #include "libc/calls/state.internal.h" +#include "libc/calls/struct/sigset.internal.h" #include "libc/calls/syscall-nt.internal.h" #include "libc/calls/syscall_support-nt.internal.h" #include "libc/errno.h" @@ -131,24 +131,13 @@ static textwindows int64_t sys_open_nt_impl(int dirfd, const char *path, return __fix_enotdir(hand, path16); } -static textwindows int sys_open_nt_console(int dirfd, - const struct NtMagicPaths *mp, - uint32_t flags, int32_t mode, - size_t fd) { - g_fds.p[fd].kind = kFdConsole; - g_fds.p[fd].flags = flags; - g_fds.p[fd].mode = mode; - g_fds.p[fd].handle = - CreateFile(u"CONIN$", kNtGenericRead | kNtGenericWrite, kNtFileShareRead, - &kNtIsInheritable, kNtOpenExisting, 0, 0); - return fd; -} - static textwindows int sys_open_nt_file(int dirfd, const char *file, uint32_t flags, int32_t mode, size_t fd) { - if ((g_fds.p[fd].handle = sys_open_nt_impl(dirfd, file, flags, mode, - kNtFileFlagOverlapped)) != -1) { + int64_t handle; + if ((handle = sys_open_nt_impl(dirfd, file, flags, mode, + kNtFileFlagOverlapped)) != -1) { + g_fds.p[fd].handle = handle; g_fds.p[fd].kind = kFdFile; g_fds.p[fd].flags = flags; g_fds.p[fd].mode = mode; @@ -158,15 +147,61 @@ static textwindows int sys_open_nt_file(int dirfd, const char *file, } } +static textwindows int sys_open_nt_special(int fd, int flags, int mode, + int kind, const char16_t *name) { + g_fds.p[fd].kind = kind; + g_fds.p[fd].mode = mode; + g_fds.p[fd].flags = flags; + g_fds.p[fd].handle = CreateFile(name, kNtGenericRead | kNtGenericWrite, + kNtFileShareRead | kNtFileShareWrite, + &kNtIsInheritable, kNtOpenExisting, 0, 0); + return fd; +} + +static textwindows int sys_open_nt_dup(int fd, int flags, int mode, int oldfd) { + int64_t handle; + if (!__isfdopen(oldfd)) { + return enoent(); + } + if (DuplicateHandle(GetCurrentProcess(), g_fds.p[oldfd].handle, + GetCurrentProcess(), &handle, 0, true, + kNtDuplicateSameAccess)) { + g_fds.p[fd] = g_fds.p[oldfd]; + g_fds.p[fd].handle = handle; + g_fds.p[fd].mode = mode; + if (!sys_fcntl_nt_setfl(fd, flags)) { + return fd; + } else { + CloseHandle(handle); + return -1; + } + } else { + return __winerr(); + } +} + textwindows int sys_open_nt(int dirfd, const char *file, uint32_t flags, int32_t mode) { int fd; ssize_t rc; + BLOCK_SIGNALS; __fds_lock(); if (!(flags & O_CREAT)) mode = 0; if ((rc = fd = __reservefd_unlocked(-1)) != -1) { - if (!strcmp(file, kNtMagicPaths.devtty)) { - rc = sys_open_nt_console(dirfd, &kNtMagicPaths, flags, mode, fd); + if (startswith(file, "/dev/")) { + if (!strcmp(file + 5, "tty")) { + rc = sys_open_nt_special(fd, flags, mode, kFdConsole, u"CONIN$"); + } else if (!strcmp(file + 5, "null")) { + rc = sys_open_nt_special(fd, flags, mode, kFdDevNull, u"NUL"); + } else if (!strcmp(file + 5, "stdin")) { + rc = sys_open_nt_dup(fd, flags, mode, STDIN_FILENO); + } else if (!strcmp(file + 5, "stdout")) { + rc = sys_open_nt_dup(fd, flags, mode, STDOUT_FILENO); + } else if (!strcmp(file + 5, "stderr")) { + rc = sys_open_nt_dup(fd, flags, mode, STDERR_FILENO); + } else { + rc = enoent(); + } } else { rc = sys_open_nt_file(dirfd, file, flags, mode, fd); } @@ -175,5 +210,6 @@ textwindows int sys_open_nt(int dirfd, const char *file, uint32_t flags, } __fds_unlock(); } + ALLOW_SIGNALS; return rc; } diff --git a/libc/calls/openat-sysv.c b/libc/calls/openat-sysv.c index 8088d5e59b7..cf1699aa52e 100644 --- a/libc/calls/openat-sysv.c +++ b/libc/calls/openat-sysv.c @@ -27,11 +27,9 @@ int sys_openat(int dirfd, const char *file, int flags, unsigned mode) { static bool once, modernize; int d, e, f; - /* - * RHEL5 doesn't support O_CLOEXEC. It's hard to test for this. - * Sometimes the system call succeeds and it just doesn't set the - * flag. Other times, it return -530 which makes no sense. - */ + // RHEL5 doesn't support O_CLOEXEC. It's hard to test for this. + // Sometimes the system call succeeds and it just doesn't set the + // flag. Other times, it return -530 which makes no sense. if (!IsLinux() || !(flags & O_CLOEXEC) || modernize) { d = __sys_openat(dirfd, file, flags, mode); } else if (once) { diff --git a/libc/calls/park.c b/libc/calls/park.c index 06f1f4233fe..119e29854aa 100644 --- a/libc/calls/park.c +++ b/libc/calls/park.c @@ -47,13 +47,9 @@ static textwindows int _park_thread(uint32_t msdelay, sigset_t waitmask, om = __sig_beginwait(waitmask); if ((rc = _check_cancel()) != -1 && (rc = _check_signal(restartable)) != -1) { unassert((wi = WaitForSingleObject(sem, msdelay)) != -1u); - if (wi != kNtWaitTimeout) { - _check_signal(false); - rc = eintr(); - _check_cancel(); - } else if ((rc = _check_signal(restartable))) { - _check_cancel(); - } + if (restartable && !(pt->pt_flags & PT_RESTARTABLE)) rc = eintr(); + rc |= _check_signal(restartable); + if (rc == -1 && errno == EINTR) _check_cancel(); } __sig_finishwait(om); atomic_store_explicit(&pt->pt_blocker, PT_BLOCKER_CPU, memory_order_release); diff --git a/libc/calls/pause-nt.c b/libc/calls/pause-nt.c index f9f2c23acbc..61a809ec44f 100644 --- a/libc/calls/pause-nt.c +++ b/libc/calls/pause-nt.c @@ -17,7 +17,6 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/internal.h" -#include "libc/calls/sig.internal.h" #include "libc/calls/syscall_support-nt.internal.h" #ifdef __x86_64__ diff --git a/libc/calls/poll-nt.c b/libc/calls/poll-nt.c index 40898f116ae..3e37c8da446 100644 --- a/libc/calls/poll-nt.c +++ b/libc/calls/poll-nt.c @@ -58,10 +58,10 @@ // Polls on the New Technology. // // This function is used to implement poll() and select(). You may poll -// on both sockets and files at the same time. We also poll for signals -// while poll is polling. -textwindows int sys_poll_nt(struct pollfd *fds, uint64_t nfds, uint32_t *ms, - const sigset_t *sigmask) { +// on sockets, files and the console at the same time. We also poll for +// both signals and posix thread cancelation, while the poll is polling +static textwindows int sys_poll_nt_impl(struct pollfd *fds, uint64_t nfds, + uint32_t *ms, sigset_t sigmask) { bool ok; uint64_t millis; uint32_t cm, avail, waitfor; @@ -72,7 +72,6 @@ textwindows int sys_poll_nt(struct pollfd *fds, uint64_t nfds, uint32_t *ms, struct timespec started, deadline, remain, now; int i, rc, sn, pn, gotinvals, gotpipes, gotsocks; - BLOCK_SIGNALS; started = timespec_real(); deadline = timespec_add(started, timespec_frommillis(ms ? *ms : -1u)); @@ -128,7 +127,7 @@ textwindows int sys_poll_nt(struct pollfd *fds, uint64_t nfds, uint32_t *ms, __fds_unlock(); if (rc) { // failed to create a polling solution - goto Finished; + return rc; } // perform the i/o and sleeping and looping @@ -170,8 +169,7 @@ textwindows int sys_poll_nt(struct pollfd *fds, uint64_t nfds, uint32_t *ms, // compute a small time slice we don't mind sleeping for if (sn) { if ((gotsocks = WSAPoll(sockfds, sn, 0)) == -1) { - rc = __winsockerr(); - goto Finished; + return __winsockerr(); } } else { gotsocks = 0; @@ -190,8 +188,8 @@ textwindows int sys_poll_nt(struct pollfd *fds, uint64_t nfds, uint32_t *ms, if (waitfor) { POLLTRACE("poll() sleeping for %'d out of %'lu ms", waitfor, timespec_tomillis(remain)); - if ((rc = _park_norestart(waitfor, sigmask ? *sigmask : 0)) == -1) { - goto Finished; // eintr, ecanceled, etc. + if ((rc = _park_norestart(waitfor, sigmask)) == -1) { + return -1; // eintr, ecanceled, etc. } } } @@ -221,9 +219,14 @@ textwindows int sys_poll_nt(struct pollfd *fds, uint64_t nfds, uint32_t *ms, } // and finally return - rc = gotinvals + gotpipes + gotsocks; + return gotinvals + gotpipes + gotsocks; +} -Finished: +textwindows int sys_poll_nt(struct pollfd *fds, uint64_t nfds, uint32_t *ms, + const sigset_t *sigmask) { + int rc; + BLOCK_SIGNALS; + rc = sys_poll_nt_impl(fds, nfds, ms, sigmask ? *sigmask : 0); ALLOW_SIGNALS; return rc; } diff --git a/libc/calls/pread.c b/libc/calls/pread.c index d274ac1572a..99054957185 100644 --- a/libc/calls/pread.c +++ b/libc/calls/pread.c @@ -72,7 +72,7 @@ ssize_t pread(int fd, void *buf, size_t size, int64_t offset) { rc = sys_pread(fd, buf, size, offset, offset); } else if (__isfdkind(fd, kFdSocket)) { rc = espipe(); - } else if (__isfdkind(fd, kFdFile)) { + } else if (__isfdkind(fd, kFdFile) || __isfdkind(fd, kFdDevNull)) { rc = sys_read_nt(fd, (struct iovec[]){{buf, size}}, 1, offset); } else { rc = ebadf(); diff --git a/libc/calls/printfds.c b/libc/calls/printfds.c index d37d6078414..a8602a59a43 100644 --- a/libc/calls/printfds.c +++ b/libc/calls/printfds.c @@ -32,6 +32,8 @@ static const char *__fdkind2str(int x) { return "kFdSocket"; case kFdConsole: return "kFdConsole"; + case kFdDevNull: + return "kFdDevNull"; case kFdSerial: return "kFdSerial"; case kFdZip: diff --git a/libc/calls/pwrite.c b/libc/calls/pwrite.c index 2696dc553a4..2ae7252f6ce 100644 --- a/libc/calls/pwrite.c +++ b/libc/calls/pwrite.c @@ -65,7 +65,7 @@ ssize_t pwrite(int fd, const void *buf, size_t size, int64_t offset) { rc = sys_pwrite(fd, buf, size, offset, offset); } else if (__isfdkind(fd, kFdSocket)) { rc = espipe(); - } else if (__isfdkind(fd, kFdFile)) { + } else if (__isfdkind(fd, kFdFile) || __isfdkind(fd, kFdDevNull)) { rc = sys_write_nt(fd, (struct iovec[]){{(void *)buf, size}}, 1, offset); } else { return ebadf(); diff --git a/libc/calls/read-nt.c b/libc/calls/read-nt.c index 245e2e2b75d..2894af20f9d 100644 --- a/libc/calls/read-nt.c +++ b/libc/calls/read-nt.c @@ -16,6 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/assert.h" #include "libc/atomic.h" #include "libc/calls/internal.h" #include "libc/calls/sig.internal.h" @@ -733,8 +734,10 @@ static textwindows int WaitForConsole(struct Fd *f, sigset_t waitmask) { atomic_store_explicit(&pt->pt_blocker, PT_BLOCKER_SEM, memory_order_release); m = __sig_beginwait(waitmask); if ((rc = _check_cancel()) != -1 && (rc = _check_signal(true)) != -1) { - WaitForMultipleObjects(2, (int64_t[2]){sem, __keystroke.cin}, 0, ms); + int64_t hands[2] = {sem, __keystroke.cin}; + unassert(WaitForMultipleObjects(2, hands, 0, ms) != -1u); if (~pt->pt_flags & PT_RESTARTABLE) rc = eintr(); + rc |= _check_signal(true); if (rc == -1 && errno == EINTR) _check_cancel(); } __sig_finishwait(m); @@ -763,6 +766,11 @@ textwindows ssize_t sys_read_nt_impl(int fd, void *data, size_t size, // switch to terminal polyfill if reading from win32 console struct Fd *f = g_fds.p + fd; + + if (f->kind == kFdDevNull) { + return 0; + } + if (f->kind == kFdConsole) { return ReadFromConsole(f, data, size, waitmask); } diff --git a/libc/calls/readv-nt.c b/libc/calls/readv-nt.c index 261ceeb5b7a..11cd46cad8f 100644 --- a/libc/calls/readv-nt.c +++ b/libc/calls/readv-nt.c @@ -28,6 +28,7 @@ textwindows ssize_t sys_readv_nt(int fd, const struct iovec *iov, int iovlen) { switch (g_fds.p[fd].kind) { case kFdFile: case kFdConsole: + case kFdDevNull: return sys_read_nt(fd, iov, iovlen, -1); case kFdSocket: return _weaken(sys_recv_nt)(fd, iov, iovlen, 0); diff --git a/libc/calls/readwrite-nt.c b/libc/calls/readwrite-nt.c index 9f6e4884f26..06ba2b37bcb 100644 --- a/libc/calls/readwrite-nt.c +++ b/libc/calls/readwrite-nt.c @@ -83,7 +83,9 @@ sys_readwrite_nt(int fd, void *data, size_t size, ssize_t offset, // similar to the lseek() system call, they too raise ESPIPE when // operating on a non-seekable file. bool pwriting = offset != -1; - bool seekable = f->kind == kFdFile && GetFileType(handle) == kNtFileTypeDisk; + bool seekable = + (f->kind == kFdFile && GetFileType(handle) == kNtFileTypeDisk) || + f->kind == kFdDevNull; if (pwriting && !seekable) { return espipe(); } diff --git a/libc/calls/setfl.c b/libc/calls/setfl.c new file mode 100644 index 00000000000..e1fb8841ad3 --- /dev/null +++ b/libc/calls/setfl.c @@ -0,0 +1,71 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2023 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/internal.h" +#include "libc/calls/struct/fd.internal.h" +#include "libc/calls/syscall_support-nt.internal.h" +#include "libc/nt/createfile.h" +#include "libc/nt/enum/fileflagandattributes.h" +#include "libc/nt/files.h" +#include "libc/nt/runtime.h" +#include "libc/sysv/consts/o.h" + +textwindows int sys_fcntl_nt_setfl(int fd, unsigned flags) { + + // you may change the following: + // + // - O_NONBLOCK make read() raise EAGAIN + // - O_APPEND for toggling append mode + // - O_RANDOM alt. for posix_fadvise() + // - O_SEQUENTIAL alt. for posix_fadvise() + // - O_DIRECT works but haven't tested + // + // the other bits are ignored. + unsigned allowed = O_APPEND | O_SEQUENTIAL | O_RANDOM | O_DIRECT | O_NONBLOCK; + unsigned needreo = O_APPEND | O_SEQUENTIAL | O_RANDOM | O_DIRECT; + unsigned newflag = (g_fds.p[fd].flags & ~allowed) | (flags & allowed); + + if (g_fds.p[fd].kind == kFdFile && + ((g_fds.p[fd].flags & needreo) ^ (flags & needreo))) { + unsigned perm, share, attr; + if (GetNtOpenFlags(newflag, g_fds.p[fd].mode, &perm, &share, 0, &attr) == + -1) { + return -1; + } + // MSDN says only these are allowed, otherwise it returns EINVAL. + attr &= kNtFileFlagBackupSemantics | kNtFileFlagDeleteOnClose | + kNtFileFlagNoBuffering | kNtFileFlagOpenNoRecall | + kNtFileFlagOpenReparsePoint | kNtFileFlagOverlapped | + kNtFileFlagPosixSemantics | kNtFileFlagRandomAccess | + kNtFileFlagSequentialScan | kNtFileFlagWriteThrough; + intptr_t hand; + if ((hand = ReOpenFile(g_fds.p[fd].handle, perm, share, attr)) != -1) { + if (hand != g_fds.p[fd].handle) { + CloseHandle(g_fds.p[fd].handle); + g_fds.p[fd].handle = hand; + } + } else { + return __winerr(); + } + } + + // 1. ignore flags that aren't access mode flags + // 2. return zero if nothing's changed + g_fds.p[fd].flags = newflag; + return 0; +} diff --git a/libc/calls/sig.c b/libc/calls/sig.c index c59d7dbd07a..9c0c6a8daef 100644 --- a/libc/calls/sig.c +++ b/libc/calls/sig.c @@ -88,11 +88,13 @@ textwindows bool __sig_ignored(int sig) { textwindows void __sig_delete(int sig) { struct Dll *e; __sig.pending &= ~(1ull << (sig - 1)); + BLOCK_SIGNALS; _pthread_lock(); for (e = dll_last(_pthread_list); e; e = dll_prev(_pthread_list, e)) { POSIXTHREAD_CONTAINER(e)->tib->tib_sigpending &= ~(1ull << (sig - 1)); } _pthread_unlock(); + ALLOW_SIGNALS; } static textwindows bool __sig_should_use_altstack(unsigned flags, @@ -219,7 +221,7 @@ textwindows void __sig_cancel(struct PosixThread *pt, int sig, unsigned flags) { } static textwindows wontreturn void __sig_tramp(struct SignalFrame *sf) { - ++__sig.count; + atomic_fetch_add_explicit(&__sig.count, 1, memory_order_relaxed); int sig = sf->si.si_signo; sigset_t blocksigs = __sighandmask[sig]; if (!(sf->flags & SA_NODEFER)) blocksigs |= 1ull << (sig - 1); @@ -309,7 +311,9 @@ static int __sig_killer(struct PosixThread *pt, int sig, int sic) { textwindows int __sig_kill(struct PosixThread *pt, int sig, int sic) { int rc; BLOCK_SIGNALS; + _pthread_ref(pt); rc = __sig_killer(pt, sig, sic); + _pthread_unref(pt); ALLOW_SIGNALS; return rc; } @@ -333,7 +337,7 @@ textwindows void __sig_generate(int sig, int sic) { atomic_load_explicit(&pt->pt_status, memory_order_acquire) < kPosixThreadTerminated && !(pt->tib->tib_sigmask & (1ull << (sig - 1)))) { - mark = pt; + _pthread_ref((mark = pt)); break; } } @@ -345,6 +349,7 @@ textwindows void __sig_generate(int sig, int sic) { STRACE("all threads block %G so adding to pending signals of process", sig); __sig.pending |= 1ull << (sig - 1); } + _pthread_unref(mark); ALLOW_SIGNALS; } @@ -411,7 +416,7 @@ static void __sig_unmaskable(struct NtExceptionPointers *ep, int code, int sig, struct CosmoTib *tib) { // increment the signal count for getrusage() - ++__sig.count; + atomic_fetch_add_explicit(&__sig.count, 1, memory_order_relaxed); // log vital crash information reliably for --strace before doing much // we don't print this without the flag since raw numbers scare people diff --git a/libc/calls/sigignore.c b/libc/calls/sigignore.c index c8a7b3ee4bf..16498d20ea3 100644 --- a/libc/calls/sigignore.c +++ b/libc/calls/sigignore.c @@ -18,14 +18,11 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" #include "libc/calls/struct/sigaction.h" -#include "libc/str/str.h" /** * Configures process to ignore signal. */ int sigignore(int sig) { - struct sigaction sa; - bzero(&sa, sizeof(sa)); - sa.sa_handler = SIG_IGN; + struct sigaction sa = {.sa_handler = SIG_IGN}; return sigaction(sig, &sa, 0); } diff --git a/libc/calls/sigpending.c b/libc/calls/sigpending.c index 7dd73d38b2f..d9400b92d17 100644 --- a/libc/calls/sigpending.c +++ b/libc/calls/sigpending.c @@ -21,6 +21,7 @@ #include "libc/calls/struct/sigset.internal.h" #include "libc/dce.h" #include "libc/intrin/asan.internal.h" +#include "libc/intrin/atomic.h" #include "libc/intrin/describeflags.internal.h" #include "libc/intrin/strace.internal.h" #include "libc/sysv/errfuns.h" @@ -53,7 +54,8 @@ int sigpending(sigset_t *pending) { } rc = 0; } else if (IsWindows()) { - *pending = __sig.pending | __get_tls()->tib_sigpending; + *pending = atomic_load_explicit(&__sig.pending, memory_order_acquire) | + __get_tls()->tib_sigpending; rc = 0; } else { rc = enosys(); diff --git a/libc/calls/state.internal.h b/libc/calls/state.internal.h index 81fc2dc094c..a37a780bc16 100644 --- a/libc/calls/state.internal.h +++ b/libc/calls/state.internal.h @@ -1,6 +1,7 @@ #ifndef COSMOPOLITAN_LIBC_CALLS_STATE_INTERNAL_H_ #define COSMOPOLITAN_LIBC_CALLS_STATE_INTERNAL_H_ #include "libc/calls/calls.h" +#include "libc/nt/struct/securityattributes.h" #include "libc/thread/thread.h" #include "libc/thread/tls.h" #if !(__ASSEMBLER__ + __LINKER__ + 0) diff --git a/libc/calls/struct/fd.internal.h b/libc/calls/struct/fd.internal.h index 2e7405c55ab..5c54a97d457 100644 --- a/libc/calls/struct/fd.internal.h +++ b/libc/calls/struct/fd.internal.h @@ -11,6 +11,7 @@ COSMOPOLITAN_C_START_ #define kFdZip 6 #define kFdEpoll 7 #define kFdReserved 8 +#define kFdDevNull 9 struct Fd { char kind; diff --git a/libc/calls/struct/stat.internal.h b/libc/calls/struct/stat.internal.h index 29861e85b1a..4e4fdcbfadf 100644 --- a/libc/calls/struct/stat.internal.h +++ b/libc/calls/struct/stat.internal.h @@ -7,7 +7,9 @@ COSMOPOLITAN_C_START_ int sys_fstat(int, struct stat *); int sys_fstatat(int, const char *, struct stat *, int); -int sys_fstat_nt(int64_t, struct stat *); +int sys_fstat_nt(int, struct stat *); +int sys_fstat_nt_special(int, struct stat *); +int sys_fstat_nt_handle(int64_t, struct stat *); int sys_fstatat_nt(int, const char *, struct stat *, int); int sys_lstat_nt(const char *, struct stat *); int sys_fstat_metal(int, struct stat *); diff --git a/libc/calls/syscall_support-nt.internal.h b/libc/calls/syscall_support-nt.internal.h index 9fdaa7d8021..2e79b21f8e5 100644 --- a/libc/calls/syscall_support-nt.internal.h +++ b/libc/calls/syscall_support-nt.internal.h @@ -16,6 +16,7 @@ int __mkntpath2(const char *, char16_t[hasatleast PATH_MAX], int); int __mkntpathat(int, const char *, int, char16_t[hasatleast PATH_MAX]); int __mkntpathath(int64_t, const char *, int, char16_t[hasatleast PATH_MAX]); int ntaccesscheck(const char16_t *, uint32_t) paramsnonnull(); +int sys_fcntl_nt_setfl(int, unsigned); int sys_pause_nt(void); int64_t __fix_enotdir(int64_t, char16_t *); int64_t __fix_enotdir3(int64_t, char16_t *, char16_t *); diff --git a/libc/calls/writev-nt.c b/libc/calls/writev-nt.c index 0e78534302b..c630e031e9b 100644 --- a/libc/calls/writev-nt.c +++ b/libc/calls/writev-nt.c @@ -27,6 +27,7 @@ textwindows ssize_t sys_writev_nt(int fd, const struct iovec *iov, int iovlen) { switch (g_fds.p[fd].kind) { case kFdFile: case kFdConsole: + case kFdDevNull: return sys_write_nt(fd, iov, iovlen, -1); case kFdSocket: return _weaken(sys_send_nt)(fd, iov, iovlen, 0); diff --git a/libc/intrin/describestat.c b/libc/intrin/describestat.c index 80ad0db058c..97e2b247c91 100644 --- a/libc/intrin/describestat.c +++ b/libc/intrin/describestat.c @@ -60,11 +60,11 @@ const char *(DescribeStat)(char buf[N], int rc, const struct stat *st) { } if (st->st_dev) { - append(", .st_%s=%lu", "dev", st->st_dev); + append(", .st_%s=%#lx", "dev", st->st_dev); } if (st->st_ino) { - append(", .st_%s=%lu", "ino", st->st_ino); + append(", .st_%s=%#lx", "ino", st->st_ino); } if (st->st_gen) { diff --git a/libc/calls/ntmagicpaths.c b/libc/intrin/fnv.c similarity index 84% rename from libc/calls/ntmagicpaths.c rename to libc/intrin/fnv.c index 30269b7c9a9..3f605ffbe46 100644 --- a/libc/calls/ntmagicpaths.c +++ b/libc/intrin/fnv.c @@ -1,7 +1,7 @@ /*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ │vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ ╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ Copyright 2023 Justine Alexandra Roberts Tunney │ │ │ │ Permission to use, copy, modify, and/or distribute this software for │ │ any purpose with or without fee is hereby granted, provided that the │ @@ -16,10 +16,15 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/ntmagicpaths.internal.h" +#include "libc/str/str.h" -const struct NtMagicPaths kNtMagicPaths = { -#define TAB(NAME, STRING) STRING, -#include "libc/calls/ntmagicpaths.inc" -#undef TAB -}; +uint64_t __fnv(const void *data, size_t size) { + const unsigned char *p = data; + const unsigned char *pe = p + size; + uint64_t hash = 0xcbf29ce484222325; + while (p < pe) { + hash *= 0x100000001b3; + hash ^= *p++; + } + return hash; +} diff --git a/libc/thread/pthread_decimate.c b/libc/intrin/pthread_unref.c similarity index 66% rename from libc/thread/pthread_decimate.c rename to libc/intrin/pthread_unref.c index df13fd35cf9..20c40740d61 100644 --- a/libc/thread/pthread_decimate.c +++ b/libc/intrin/pthread_unref.c @@ -1,7 +1,7 @@ /*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ │vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ ╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2022 Justine Alexandra Roberts Tunney │ +│ Copyright 2023 Justine Alexandra Roberts Tunney │ │ │ │ Permission to use, copy, modify, and/or distribute this software for │ │ any purpose with or without fee is hereby granted, provided that the │ @@ -16,35 +16,22 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/atomic.h" +#include "libc/assert.h" #include "libc/intrin/atomic.h" -#include "libc/intrin/dll.h" -#include "libc/intrin/strace.internal.h" -#include "libc/runtime/runtime.h" +#include "libc/intrin/weaken.h" #include "libc/thread/posixthread.internal.h" -#include "libc/thread/thread.h" -#include "libc/thread/tls.h" -/** - * Releases memory of detached threads that have terminated. - */ -void _pthread_decimate(void) { - struct Dll *e; - struct PosixThread *pt; - enum PosixThreadStatus status; -StartOver: - _pthread_lock(); - for (e = dll_last(_pthread_list); e; e = dll_prev(_pthread_list, e)) { - pt = POSIXTHREAD_CONTAINER(e); - if (pt->tib == __get_tls()) continue; - status = atomic_load_explicit(&pt->pt_status, memory_order_acquire); - if (status != kPosixThreadZombie) break; - if (!atomic_load_explicit(&pt->tib->tib_tid, memory_order_acquire)) { - dll_remove(&_pthread_list, e); - _pthread_unlock(); - _pthread_free(pt, false); - goto StartOver; - } +static bool _pthread_deref(struct PosixThread *pt) { + int refs = atomic_load_explicit(&pt->pt_refs, memory_order_acquire); + if (!refs) return true; + unassert(refs > 0); + return !atomic_fetch_sub(&pt->pt_refs, 1); +} + +void _pthread_unref(struct PosixThread *pt) { + if (_pthread_deref(pt)) { + unassert(_weaken(_pthread_free)); + _weaken(_pthread_free)(pt, false); + _weaken(_pthread_decimate)(); } - _pthread_unlock(); } diff --git a/libc/intrin/reopenfile.c b/libc/intrin/reopenfile.c index bb81cc056ef..759e06274c4 100644 --- a/libc/intrin/reopenfile.c +++ b/libc/intrin/reopenfile.c @@ -28,14 +28,12 @@ __msabi extern typeof(ReOpenFile) *const __imp_ReOpenFile; * Reopens file on the New Technology. * * @return handle, or -1 on failure - * @note this wrapper takes care of ABI, STRACE(), and __winerr() */ int64_t ReOpenFile(int64_t hOriginalFile, uint32_t dwDesiredAccess, uint32_t dwShareMode, uint32_t dwFlagsAndAttributes) { int64_t hHandle; hHandle = __imp_ReOpenFile(hOriginalFile, dwDesiredAccess, dwShareMode, dwFlagsAndAttributes); - if (hHandle == -1) __winerr(); NTTRACE("ReOpenFile(%ld, %s, %s, %s) → %ld% m", hOriginalFile, DescribeNtFileAccessFlags(dwDesiredAccess), DescribeNtFileShareFlags(dwShareMode), diff --git a/libc/log/libfatal.internal.h b/libc/log/libfatal.internal.h index a837e7ea602..c214e979e97 100644 --- a/libc/log/libfatal.internal.h +++ b/libc/log/libfatal.internal.h @@ -1,11 +1,6 @@ #ifndef COSMOPOLITAN_LIBC_LOG_LIBFATAL_INTERNAL_H_ #define COSMOPOLITAN_LIBC_LOG_LIBFATAL_INTERNAL_H_ -#include "libc/calls/calls.h" -#include "libc/calls/syscall-sysv.internal.h" -#include "libc/dce.h" #include "libc/macros.internal.h" -#include "libc/nt/runtime.h" -#include "libc/sysv/consts/nr.h" #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ @@ -30,15 +25,6 @@ __funline char *__stpcpy(char *d, const char *s) { } } -__funline long __write_linux(int fd, const void *p, long n) { - long ax = 1; - asm volatile("syscall" - : "+a"(ax) - : "D"(fd), "S"(p), "d"(n) - : "rcx", "r11", "memory"); - return ax; -} - __funline void *__repstosb(void *di, char al, size_t cx) { #if defined(__x86__) && defined(__GNUC__) && !defined(__STRICT_ANSI__) asm("rep stosb" diff --git a/libc/proc/cocmd.c b/libc/proc/cocmd.c index 583d27d22df..4fafca4b06e 100644 --- a/libc/proc/cocmd.c +++ b/libc/proc/cocmd.c @@ -96,11 +96,19 @@ static wontreturn void UnsupportedSyntax(unsigned char c) { } static void Open(const char *path, int fd, int flags) { - close(fd); - if (open(path, flags, 0644) == -1) { + int tmpfd; + // use open+dup2 to support things like >/dev/stdout + if ((tmpfd = open(path, flags, 0644)) == -1) { perror(path); _Exit(1); } + if (tmpfd != fd) { + if (dup2(tmpfd, fd) == -1) { + perror("dup2"); + _Exit(1); + } + close(tmpfd); + } } static int SystemExec(void) { diff --git a/libc/proc/fork-nt.c b/libc/proc/fork-nt.c index 6174035b4e9..d0e7a7fa6a0 100644 --- a/libc/proc/fork-nt.c +++ b/libc/proc/fork-nt.c @@ -67,10 +67,8 @@ #include "libc/thread/itimer.internal.h" #include "libc/thread/posixthread.internal.h" #include "libc/thread/tls.h" - #ifdef __x86_64__ -extern int64_t __wincrashearly; void __keystroke_wipe(void); static textwindows wontreturn void AbortFork(const char *func) { @@ -399,7 +397,7 @@ textwindows int sys_fork_nt(uint32_t dwCreationFlags) { __tls_enabled_set(true); // clear pending signals tib->tib_sigpending = 0; - __sig.pending = 0; + atomic_store_explicit(&__sig.pending, 0, memory_order_relaxed); // re-enable threads __enable_threads(); // re-apply code morphing for function tracing diff --git a/libc/proc/getrusage-nt.c b/libc/proc/getrusage-nt.c index b7e56a5fed2..a5b4a8645c4 100644 --- a/libc/proc/getrusage-nt.c +++ b/libc/proc/getrusage-nt.c @@ -21,6 +21,7 @@ #include "libc/calls/struct/rusage.internal.h" #include "libc/calls/syscall_support-nt.internal.h" #include "libc/fmt/wintime.internal.h" +#include "libc/intrin/atomic.h" #include "libc/nt/accounting.h" #include "libc/nt/process.h" #include "libc/nt/runtime.h" @@ -75,7 +76,7 @@ textwindows int sys_getrusage_nt(int who, struct rusage *usage) { .ru_majflt = memcount.PageFaultCount, .ru_inblock = iocount.ReadOperationCount, .ru_oublock = iocount.WriteOperationCount, - .ru_nsignals = __sig.count, + .ru_nsignals = atomic_load_explicit(&__sig.count, memory_order_acquire), }; if (who == RUSAGE_BOTH) { diff --git a/libc/runtime/mprotect-nt.greg.c b/libc/runtime/mprotect-nt.greg.c index 24f5073814b..8289fe3d9a5 100644 --- a/libc/runtime/mprotect-nt.greg.c +++ b/libc/runtime/mprotect-nt.greg.c @@ -16,6 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/struct/sigset.internal.h" #include "libc/intrin/directmap.internal.h" #include "libc/nt/memory.h" #include "libc/runtime/internal.h" @@ -26,6 +27,7 @@ textwindows int sys_mprotect_nt(void *addr, size_t size, int prot) { unsigned i; uint32_t op; char *a, *b, *x, *y, *p; + BLOCK_SIGNALS; __mmi_lock(); size = (size + 4095) & -4096; p = addr; @@ -62,5 +64,6 @@ textwindows int sys_mprotect_nt(void *addr, size_t size, int prot) { } } __mmi_unlock(); + ALLOW_SIGNALS; return rc; } diff --git a/libc/runtime/zipos-inode.c b/libc/runtime/zipos-inode.c index 0dbb7fcfdf5..1b5f1fea4d8 100644 --- a/libc/runtime/zipos-inode.c +++ b/libc/runtime/zipos-inode.c @@ -20,23 +20,15 @@ #include "libc/limits.h" #include "libc/runtime/zipos.internal.h" #include "libc/stdio/stdio.h" +#include "libc/str/str.h" #include "libc/zip.internal.h" -static uint64_t __zipos_fnv(const char *s, int len) { - uint64_t hash = 0xcbf29ce484222325; - for (int i = 0; i < len; i++) { - hash *= 0x100000001b3; - hash ^= (unsigned char)s[i]; - } - return hash; -} - uint64_t __zipos_inode(struct Zipos *zipos, int64_t cfile, // const void *name, size_t namelen) { unassert(cfile >= 0); if (cfile == ZIPOS_SYNTHETIC_DIRECTORY) { if (namelen && ((char *)name)[namelen - 1] == '/') --namelen; - cfile = INT64_MIN | __zipos_fnv(name, namelen); + cfile = INT64_MIN | __fnv(name, namelen); } return cfile; } diff --git a/libc/str/str.h b/libc/str/str.h index ec2d8f1097c..732e938f92b 100644 --- a/libc/str/str.h +++ b/libc/str/str.h @@ -186,6 +186,7 @@ dontthrow nocallback; uint64_t tpenc(uint32_t) pureconst; char *chomp(char *) libcesque; wchar_t *wchomp(wchar_t *) libcesque; +uint64_t __fnv(const void *, size_t) strlenesque; bool startswith(const char *, const char *) strlenesque; bool startswithi(const char *, const char *) strlenesque; bool endswith(const char *, const char *) strlenesque; diff --git a/libc/thread/posixthread.internal.h b/libc/thread/posixthread.internal.h index 7a252ac97c0..8107b081aa0 100644 --- a/libc/thread/posixthread.internal.h +++ b/libc/thread/posixthread.internal.h @@ -2,6 +2,7 @@ #define COSMOPOLITAN_LIBC_THREAD_POSIXTHREAD_INTERNAL_H_ #include "libc/calls/struct/sched_param.h" #include "libc/calls/struct/sigaltstack.h" +#include "libc/intrin/atomic.h" #include "libc/intrin/dll.h" #include "libc/runtime/runtime.h" #include "libc/thread/thread.h" @@ -16,9 +17,9 @@ #define PT_RESTARTABLE 64 #define PT_OPENBSD_KLUDGE 128 -#define PT_BLOCKER_CPU ((_Atomic(int) *)-0) -#define PT_BLOCKER_SEM ((_Atomic(int) *)-1) -#define PT_BLOCKER_IO ((_Atomic(int) *)-2) +#define PT_BLOCKER_CPU ((atomic_int *)-0) +#define PT_BLOCKER_SEM ((atomic_int *)-1) +#define PT_BLOCKER_IO ((atomic_int *)-2) #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ @@ -79,10 +80,11 @@ enum PosixThreadStatus { #define POSIXTHREAD_CONTAINER(e) DLL_CONTAINER(struct PosixThread, list, e) struct PosixThread { - int pt_flags; // 0x00: see PT_* constants - _Atomic(int) pt_canceled; // 0x04: thread has bad beliefs + int pt_flags; // 0x00: see PT_* constants + atomic_int pt_canceled; // 0x04: thread has bad beliefs _Atomic(enum PosixThreadStatus) pt_status; - _Atomic(int) ptid; // transitions 0 → tid + atomic_int ptid; // transitions 0 → tid + atomic_int pt_refs; // negative means free void *(*pt_start)(void *); // creation callback void *pt_arg; // start's parameter void *pt_rc; // start's return value @@ -90,7 +92,7 @@ struct PosixThread { struct CosmoTib *tib; // middle of tls allocation struct Dll list; // list of threads struct _pthread_cleanup_buffer *pt_cleanup; - _Atomic(_Atomic(int) *) pt_blocker; + _Atomic(atomic_int *) pt_blocker; int64_t pt_semaphore; intptr_t pt_iohandle; void *pt_ioverlap; @@ -119,6 +121,7 @@ void _pthread_onfork_prepare(void); void _pthread_ungarbage(void); void _pthread_unkey(struct CosmoTib *); void _pthread_unlock(void); +void _pthread_unref(struct PosixThread *); void _pthread_unwind(struct PosixThread *); void _pthread_zombify(struct PosixThread *); @@ -126,6 +129,10 @@ __funline pureconst struct PosixThread *_pthread_self(void) { return (struct PosixThread *)__get_tls()->tib_pthread; } +__funline void _pthread_ref(struct PosixThread *pt) { + atomic_fetch_add_explicit(&pt->pt_refs, 1, memory_order_relaxed); +} + COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ #endif /* COSMOPOLITAN_LIBC_THREAD_POSIXTHREAD_INTERNAL_H_ */ diff --git a/libc/thread/pthread_create.c b/libc/thread/pthread_create.c index 794cb02e5ba..8ac15524178 100644 --- a/libc/thread/pthread_create.c +++ b/libc/thread/pthread_create.c @@ -17,7 +17,6 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/assert.h" -#include "libc/atomic.h" #include "libc/calls/calls.h" #include "libc/calls/struct/sigset.h" #include "libc/calls/struct/sigset.internal.h" @@ -26,7 +25,6 @@ #include "libc/errno.h" #include "libc/fmt/itoa.h" #include "libc/intrin/asan.internal.h" -#include "libc/intrin/atomic.h" #include "libc/intrin/bits.h" #include "libc/intrin/bsr.h" #include "libc/intrin/describeflags.internal.h" @@ -67,6 +65,7 @@ __static_yoink("_pthread_atfork"); #define MAP_STACK_OPENBSD 0x4000 void _pthread_free(struct PosixThread *pt, bool isfork) { + unassert(dll_is_alone(&pt->list) && &pt->list != _pthread_list); if (pt->pt_flags & PT_STATIC) return; if (pt->pt_flags & PT_OWNSTACK) { unassert(!munmap(pt->pt_attr.__stackaddr, pt->pt_attr.__stacksize)); @@ -86,6 +85,26 @@ void _pthread_free(struct PosixThread *pt, bool isfork) { free(pt); } +void _pthread_decimate(void) { + struct Dll *e; + struct PosixThread *pt; + enum PosixThreadStatus status; +StartOver: + _pthread_lock(); + for (e = dll_last(_pthread_list); e; e = dll_prev(_pthread_list, e)) { + pt = POSIXTHREAD_CONTAINER(e); + status = atomic_load_explicit(&pt->pt_status, memory_order_acquire); + if (status != kPosixThreadZombie) break; + if (!atomic_load_explicit(&pt->tib->tib_tid, memory_order_acquire)) { + dll_remove(&_pthread_list, e); + _pthread_unlock(); + _pthread_unref(pt); + goto StartOver; + } + } + _pthread_unlock(); +} + static int PosixThread(void *arg, int tid) { void *rc; struct PosixThread *pt = arg; diff --git a/libc/thread/pthread_timedjoin_np.c b/libc/thread/pthread_timedjoin_np.c index 5fea5416ab9..719ea9e3f3f 100644 --- a/libc/thread/pthread_timedjoin_np.c +++ b/libc/thread/pthread_timedjoin_np.c @@ -116,8 +116,7 @@ errno_t pthread_timedjoin_np(pthread_t thread, void **value_ptr, if (value_ptr) { *value_ptr = pt->pt_rc; } - _pthread_free(pt, false); - _pthread_decimate(); + _pthread_unref(pt); } STRACE("pthread_timedjoin_np(%d, %s, %s) → %s", _pthread_tid(pt), DescribeReturnValue(alloca(30), err, value_ptr), diff --git a/test/libc/calls/specialfile_test.c b/test/libc/calls/specialfile_test.c new file mode 100644 index 00000000000..ebc750789fd --- /dev/null +++ b/test/libc/calls/specialfile_test.c @@ -0,0 +1,82 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2023 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/calls.h" +#include "libc/dce.h" +#include "libc/errno.h" +#include "libc/nt/files.h" +#include "libc/sysv/consts/f.h" +#include "libc/sysv/consts/o.h" +#include "libc/testlib/testlib.h" + +int pipefd[2]; +int stdoutBack; + +void SetUpOnce(void) { + testlib_enable_tmp_setup_teardown(); +} + +void CaptureStdout(void) { + ASSERT_NE(-1, (stdoutBack = dup(1))); + ASSERT_SYS(0, 0, pipe(pipefd)); + ASSERT_NE(-1, dup2(pipefd[1], 1)); +} + +void RestoreStdout(void) { + ASSERT_SYS(0, 1, dup2(stdoutBack, 1)); + ASSERT_SYS(0, 0, close(stdoutBack)); + ASSERT_SYS(0, 0, close(pipefd[1])); + ASSERT_SYS(0, 0, close(pipefd[0])); +} + +TEST(specialfile, devNull) { + ASSERT_SYS(0, 3, creat("/dev/null", 0644)); + ASSERT_EQ(O_WRONLY, fcntl(3, F_GETFL) & ~O_LARGEFILE); + ASSERT_SYS(0, 2, write(3, "hi", 2)); + ASSERT_SYS(0, 2, pwrite(3, "hi", 2, 0)); + ASSERT_SYS(0, 2, pwrite(3, "hi", 2, 2)); + ASSERT_SYS(0, 0, lseek(3, 0, SEEK_END)); + ASSERT_SYS(0, 0, lseek(3, 0, SEEK_CUR)); + if (!IsLinux()) { + // rhel7 doesn't have this behavior + ASSERT_SYS(0, 2, lseek(3, 2, SEEK_CUR)); + ASSERT_SYS(0, 2, lseek(3, 2, SEEK_END)); + } + ASSERT_SYS(0, 0, close(3)); +} + +TEST(specialfile, devNullRead) { + char buf[8] = {0}; + ASSERT_SYS(0, 3, open("/dev/null", O_RDONLY)); + ASSERT_EQ(O_RDONLY, fcntl(3, F_GETFL) & ~O_LARGEFILE); + ASSERT_SYS(0, 0, read(3, buf, 8)); + ASSERT_SYS(0, 0, close(3)); +} + +TEST(specialfile, devStdout) { + char buf[8] = {8}; + CaptureStdout(); + ASSERT_SYS(0, 6, creat("/dev/stdout", 0644)); + ASSERT_SYS(0, 2, write(6, "hi", 2)); + ASSERT_EQ(2, read(pipefd[0], buf, 8)); + ASSERT_STREQ("hi", buf); + ASSERT_SYS(ESPIPE, -1, pwrite(6, "hi", 2, 0)); + ASSERT_SYS(ESPIPE, -1, lseek(6, 0, SEEK_END)); + ASSERT_SYS(0, 0, close(6)); + RestoreStdout(); +} diff --git a/test/libc/proc/system_test.c b/test/libc/proc/system_test.c index a7cdf6cc26b..f40e075c891 100644 --- a/test/libc/proc/system_test.c +++ b/test/libc/proc/system_test.c @@ -195,6 +195,15 @@ TEST(system, exitStatusPreservedAfterSemiColon) { RestoreStdout(); } +TEST(system, devStdout) { + CaptureStdout(); + ASSERT_EQ(0, GETEXITSTATUS(system("echo sup >/dev/stdout"))); + char buf[16] = {0}; + ASSERT_EQ(4, read(pipefd[0], buf, 16)); + ASSERT_STREQ("sup\n", buf); + RestoreStdout(); +} + TEST(system, globio) { char buf[9] = {0}; CaptureStdout(); diff --git a/third_party/nsync/futex.c b/third_party/nsync/futex.c index 0bef7bcc883..7d651341859 100644 --- a/third_party/nsync/futex.c +++ b/third_party/nsync/futex.c @@ -169,10 +169,11 @@ static int nsync_futex_wait_win32_ (atomic_int *w, int expect, char pshare, return 0; } if (pt) atomic_store_explicit (&pt->pt_blocker, w, memory_order_release); - if (_check_cancel() == -1) return -1; - if (_check_signal(false) == -1) return -1; + if (_check_cancel () == -1) return -1; + if (_check_signal (false) == -1) return -1; ok = WaitOnAddress (w, &expect, sizeof(int), timespec_tomillis (wait)); - if (_check_cancel() == -1) return -1; + if (_check_signal (false) == -1) { _check_cancel (); return -1; } + if (_check_cancel () == -1) return -1; if (ok) { return 0; } else { diff --git a/tool/build/runitd.c b/tool/build/runitd.c index 87269f8f34d..e0e14f12218 100644 --- a/tool/build/runitd.c +++ b/tool/build/runitd.c @@ -620,9 +620,8 @@ void *ClientWorker(void *arg) { goto TerminateJob; } if (received > 0) { - WARNF("%s client sent %d unexpected bytes so killing job", origname, - received); - goto HangupClientAndTerminateJob; + WARNF("%s client sent %d unexpected bytes", origname, received); + continue; } if (received == MBEDTLS_ERR_SSL_WANT_READ) { // EAGAIN SO_RCVTIMEO WARNF("%s (pid %d) is taking a really long time", origname, diff --git a/tool/net/redbean.c b/tool/net/redbean.c index 37bd88be521..d6e330a10a8 100644 --- a/tool/net/redbean.c +++ b/tool/net/redbean.c @@ -178,14 +178,18 @@ __static_yoink("blink_xnu_aarch64"); // is apple silicon #define MONITOR_MICROS 150000 #define READ(F, P, N) readv(F, &(struct iovec){P, N}, 1) #define WRITE(F, P, N) writev(F, &(struct iovec){P, N}, 1) -#define LockInc(P) (*(_Atomic(typeof(*(P))) *)(P))++ -#define LockDec(P) (*(_Atomic(typeof(*(P))) *)(P))-- #define AppendCrlf(P) mempcpy(P, "\r\n", 2) #define HasHeader(H) (!!cpm.msg.headers[H].a) #define HeaderData(H) (inbuf.p + cpm.msg.headers[H].a) #define HeaderLength(H) (cpm.msg.headers[H].b - cpm.msg.headers[H].a) #define HeaderEqualCase(H, S) \ SlicesEqualCase(S, strlen(S), HeaderData(H), HeaderLength(H)) +#define LockInc(P) \ + atomic_fetch_add_explicit((_Atomic(typeof(*(P))) *)(P), +1, \ + memory_order_relaxed) +#define LockDec(P) \ + atomic_fetch_add_explicit((_Atomic(typeof(*(P))) *)(P), -1, \ + memory_order_relaxed) #define TRACE_BEGIN \ do { \ diff --git a/tool/net/winbench.c b/tool/net/winbench.c index a7bed81d58b..fb3979a1e0d 100644 --- a/tool/net/winbench.c +++ b/tool/net/winbench.c @@ -60,7 +60,6 @@ static char msg[128]; static uint32_t msglen; static const char *prog; static atomic_int a_termsig; -static atomic_long a_errors; static atomic_long a_requests; static atomic_bool a_finished; @@ -125,23 +124,21 @@ static void NewClient(struct Client *client, const struct sockaddr_in *addr) { static void *Worker(void *arg) { while (!a_finished) { - bool32 ok; + uint32_t i; uint32_t dwFlags; - uint32_t dwBytes; - struct Client *client; - uint64_t CompletionKey; - struct NtOverlapped *lpOverlapped; - if (!(ok = GetQueuedCompletionStatus(iocp, &dwBytes, &CompletionKey, - &lpOverlapped, -1u)) && - !lpOverlapped) { + uint32_t dwRecordCount; + struct NtOverlappedEntry records[8]; + if (!GetQueuedCompletionStatusEx(iocp, records, ARRAYLEN(records), + &dwRecordCount, -1u, false)) { fprintf(stderr, "GetQueuedCompletionStatus() failed w/ %d\n", GetLastError()); exit(1); } - client = (struct Client *)CompletionKey; - switch (client->state) { - case SENDING: - if (ok) { + for (i = 0; i < dwRecordCount; ++i) { + uint32_t dwBytes = records[i].dwNumberOfBytesTransferred; + struct Client *client = (struct Client *)records[i].lpCompletionKey; + switch (client->state) { + case SENDING: dwFlags = 0; client->state = RECEIVING; client->iov.buf = client->buf; @@ -153,15 +150,8 @@ static void *Worker(void *arg) { fprintf(stderr, "WSARecv() failed w/ %d\n", WSAGetLastError()); exit(1); } - } else { - fprintf(stderr, "WSAConnect() or WSASend() failed w/ %d\n", - WSAGetLastError()); - __imp_closesocket(client->handle); - ++a_errors; - } - break; - case RECEIVING: - if (ok) { + break; + case RECEIVING: if (!dwBytes) { fprintf(stderr, "got disconnect\n"); break; @@ -183,14 +173,10 @@ static void *Worker(void *arg) { fprintf(stderr, "WSASend() failed w/ %d\n", WSAGetLastError()); exit(1); } - } else { - fprintf(stderr, "WSARecv() failed w/ %d\n", WSAGetLastError()); - __imp_closesocket(client->handle); - ++a_errors; - } - break; - default: - __builtin_unreachable(); + break; + default: + __builtin_unreachable(); + } } } return 0; @@ -209,12 +195,12 @@ int main(int argc, char *argv[]) { prog = argv[0]; if (!prog) { - prog = "ab"; + prog = "winbench"; } int opt; - int nclients = 20; - int nthreads = GetMaximumProcessorCount(0xffff) * 2; + int nclients = 1000; + int nthreads = GetMaximumProcessorCount(0xffff); struct sockaddr_in destaddr = {AF_INET, htons(8080), {htonl(0x7f000001)}}; while ((opt = getopt(argc, argv, "hH:P:")) != -1) { switch (opt) { @@ -280,7 +266,7 @@ int main(int argc, char *argv[]) { NewClient(clients + i, &destaddr); } - sleep(5); + sleep(10); a_finished = true; long request_count = a_requests;