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
This commit is contained in:
Justine Tunney 2023-10-14 01:06:00 -07:00
parent aca2261cda
commit 2db2f40a98
No known key found for this signature in database
GPG key ID: BE714B4575D6E328
53 changed files with 485 additions and 299 deletions

View file

@ -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);

View file

@ -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;

View file

@ -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;

View file

@ -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();
}

View file

@ -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 @@ TryAgain:
((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 @@ TryAgain:
rc = __winerr();
}
ALLOW_SIGNALS;
// mop up errors
return __fix_enotdir(rc, path16);
}

View file

@ -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;
}

View file

@ -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);
}

View file

@ -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)) {

View file

@ -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);
}

View file

@ -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) {

View file

@ -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;

View file

@ -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$")

View file

@ -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_ */

View file

@ -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;
}

View file

@ -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) {

View file

@ -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);

View file

@ -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__

View file

@ -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;
}

View file

@ -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();

View file

@ -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:

View file

@ -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();

View file

@ -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);
}

View file

@ -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);

View file

@ -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();
}

71
libc/calls/setfl.c Normal file
View file

@ -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;
}

View file

@ -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

View file

@ -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);
}

View file

@ -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();

View file

@ -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)

View file

@ -11,6 +11,7 @@ COSMOPOLITAN_C_START_
#define kFdZip 6
#define kFdEpoll 7
#define kFdReserved 8
#define kFdDevNull 9
struct Fd {
char kind;

View file

@ -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 *);

View file

@ -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 *);

View file

@ -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);

View file

@ -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) {

View file

@ -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;
}

View file

@ -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;
}
}
_pthread_unlock();
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)();
}
}

View file

@ -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),

View file

@ -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"

View file

@ -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) {

View file

@ -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

View file

@ -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) {

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;

View file

@ -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_ */

View file

@ -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;

View file

@ -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),

View file

@ -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();
}

View file

@ -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();

View file

@ -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 {

View file

@ -620,9 +620,8 @@ RetryOnEtxtbsyRaceCondition:
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,

View file

@ -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 { \

View file

@ -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;