Make improvements

- Improved async signal safety of read() particularly for longjmp()
- Started adding cancel cleanup handlers for locks / etc on Windows
- Make /dev/tty work better particularly for uses like `foo | less`
- Eagerly read console input into a linked list, so poll can signal
- Fix some libc definitional bugs, which configure scripts detected
This commit is contained in:
Justine Tunney 2023-09-21 07:30:39 -07:00
parent d6c2830850
commit 0c5dd7b342
No known key found for this signature in database
GPG key ID: BE714B4575D6E328
85 changed files with 1062 additions and 671 deletions

View file

@ -28,6 +28,7 @@
#include "libc/intrin/strace.internal.h"
#include "libc/sock/internal.h"
#include "libc/sock/select.h"
#include "libc/sock/select.internal.h"
#include "libc/sysv/consts/nrlinux.h"
#include "libc/sysv/errfuns.h"
@ -68,6 +69,15 @@ int pselect(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
} ss;
BEGIN_CANCELLATION_POINT;
#ifdef SYSDEBUG
fd_set old_readfds;
fd_set *old_readfds_ptr = 0;
fd_set old_writefds;
fd_set *old_writefds_ptr = 0;
fd_set old_exceptfds;
fd_set *old_exceptfds_ptr = 0;
#endif
if (nfds < 0) {
rc = einval();
} else if (IsAsan() &&
@ -77,33 +87,55 @@ int pselect(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
(timeout && !__asan_is_valid(timeout, sizeof(*timeout))) ||
(sigmask && !__asan_is_valid(sigmask, sizeof(*sigmask))))) {
rc = efault();
} else if (IsLinux()) {
if (timeout) {
ts = *timeout;
tsp = &ts;
} else {
tsp = 0;
}
ss.s = sigmask;
ss.n = 8;
rc = sys_pselect(nfds, readfds, writefds, exceptfds, tsp, &ss);
} else if (!IsWindows()) {
rc = sys_pselect(nfds, readfds, writefds, exceptfds,
(struct timespec *)timeout, sigmask);
} else {
if (timeout) {
tv.tv_sec = timeout->tv_sec;
tv.tv_usec = timeout->tv_nsec / 1000;
tvp = &tv;
} else {
tvp = 0;
#ifdef SYSDEBUG
if (readfds) {
old_readfds = *readfds;
old_readfds_ptr = &old_readfds;
}
if (writefds) {
old_writefds = *writefds;
old_writefds_ptr = &old_writefds;
}
if (exceptfds) {
old_exceptfds = *exceptfds;
old_exceptfds_ptr = &old_exceptfds;
}
#endif
if (IsLinux()) {
if (timeout) {
ts = *timeout;
tsp = &ts;
} else {
tsp = 0;
}
ss.s = sigmask;
ss.n = 8;
rc = sys_pselect(nfds, readfds, writefds, exceptfds, tsp, &ss);
} else if (!IsWindows()) {
rc = sys_pselect(nfds, readfds, writefds, exceptfds,
(struct timespec *)timeout, sigmask);
} else {
if (timeout) {
tv.tv_sec = timeout->tv_sec;
tv.tv_usec = timeout->tv_nsec / 1000;
tvp = &tv;
} else {
tvp = 0;
}
rc = sys_select_nt(nfds, readfds, writefds, exceptfds, tvp, sigmask);
}
rc = sys_select_nt(nfds, readfds, writefds, exceptfds, tvp, sigmask);
}
END_CANCELLATION_POINT;
POLLTRACE("pselect(%d, %p, %p, %p, %s, %s) → %d% m", nfds, readfds, writefds,
exceptfds, DescribeTimespec(0, timeout), DescribeSigset(0, sigmask),
rc);
STRACE("pselect(%d, %s → [%s], %s → [%s], %s → [%s], %s, %s) → %d% m", nfds,
DescribeFdSet(rc, nfds, old_readfds_ptr),
DescribeFdSet(rc, nfds, readfds),
DescribeFdSet(rc, nfds, old_writefds_ptr),
DescribeFdSet(rc, nfds, writefds),
DescribeFdSet(rc, nfds, old_exceptfds_ptr),
DescribeFdSet(rc, nfds, exceptfds), //
DescribeTimespec(0, timeout), //
DescribeSigset(0, sigmask), rc);
return rc;
}

View file

@ -16,10 +16,12 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/assert.h"
#include "libc/calls/bo.internal.h"
#include "libc/calls/internal.h"
#include "libc/calls/state.internal.h"
#include "libc/calls/struct/timeval.h"
#include "libc/limits.h"
#include "libc/macros.internal.h"
#include "libc/sock/select.h"
#include "libc/sock/sock.h"
@ -34,11 +36,10 @@
int sys_select_nt(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout,
const sigset_t *sigmask) {
uint64_t millis;
int i, pfds, events, fdcount;
struct pollfd fds[64];
// convert bitsets to pollfd
struct pollfd fds[64];
for (pfds = i = 0; i < nfds; ++i) {
events = 0;
if (readfds && FD_ISSET(i, readfds)) events |= POLLIN;
@ -57,26 +58,49 @@ int sys_select_nt(int nfds, fd_set *readfds, fd_set *writefds,
}
// convert the wait time to a word
uint32_t millis;
if (!timeout) {
millis = -1;
} else {
millis = timeval_tomillis(*timeout);
int64_t ms = timeval_tomillis(*timeout);
if (ms < 0 || ms > UINT32_MAX) {
millis = -1u;
} else {
millis = ms;
}
}
// call our nt poll implementation
BEGIN_BLOCKING_OPERATION;
fdcount = sys_poll_nt(fds, pfds, &millis, sigmask);
unassert(fdcount < 64);
END_BLOCKING_OPERATION;
if (fdcount == -1) return -1;
if (fdcount < 0) return -1;
// convert pollfd back to bitsets
if (readfds) FD_ZERO(readfds);
if (writefds) FD_ZERO(writefds);
if (exceptfds) FD_ZERO(exceptfds);
for (i = 0; i < fdcount; ++i) {
if (fds[i].revents & POLLIN) FD_SET(fds[i].fd, readfds);
if (fds[i].revents & POLLOUT) FD_SET(fds[i].fd, writefds);
if (fds[i].revents & (POLLERR | POLLNVAL)) FD_SET(fds[i].fd, exceptfds);
int bits = 0;
for (i = 0; i < pfds; ++i) {
if (fds[i].revents & POLLIN) {
if (readfds) {
FD_SET(fds[i].fd, readfds);
++bits;
}
}
if (fds[i].revents & POLLOUT) {
if (writefds) {
FD_SET(fds[i].fd, writefds);
++bits;
}
}
if (fds[i].revents & (POLLERR | POLLNVAL)) {
if (exceptfds) {
FD_SET(fds[i].fd, exceptfds);
++bits;
}
}
}
// store remaining time back in caller's timeval
@ -84,7 +108,7 @@ int sys_select_nt(int nfds, fd_set *readfds, fd_set *writefds,
*timeout = timeval_frommillis(millis);
}
return fdcount;
return bits;
}
#endif /* __x86_64__ */

View file

@ -27,6 +27,8 @@
#include "libc/intrin/describeflags.internal.h"
#include "libc/intrin/strace.internal.h"
#include "libc/sock/internal.h"
#include "libc/sock/select.h"
#include "libc/sock/select.internal.h"
#include "libc/sysv/errfuns.h"
/**
@ -88,7 +90,8 @@ int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
}
END_CANCELLATION_POINT;
POLLTRACE("select(%d, %p, %p, %p, [%s]) → %d% m", nfds, readfds, writefds,
exceptfds, DescribeTimeval(rc, tvp), rc);
STRACE("select(%d, [%s], [%s], [%s], [%s]) → %d% m", nfds,
DescribeFdSet(rc, nfds, readfds), DescribeFdSet(rc, nfds, writefds),
DescribeFdSet(rc, nfds, exceptfds), DescribeTimeval(rc, tvp), rc);
return rc;
}

View file

@ -0,0 +1,13 @@
#ifndef COSMOPOLITAN_LIBC_SOCK_SELECT_INTERNAL_H_
#define COSMOPOLITAN_LIBC_SOCK_SELECT_INTERNAL_H_
#include "libc/mem/alloca.h"
#include "libc/sock/select.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
const char *DescribeFdSet(char[100], ssize_t, int, fd_set *);
#define DescribeFdSet(x, y, z) DescribeFdSet(alloca(100), x, y, z)
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_SOCK_SELECT_INTERNAL_H_ */

View file

@ -219,3 +219,5 @@ ssize_t sendfile(int outfd, int infd, int64_t *opt_in_out_inoffset,
DescribeInOutInt64(rc, opt_in_out_inoffset), uptobytes, rc);
return rc;
}
__weak_reference(sendfile, sendfile64);

View file

@ -12,7 +12,7 @@ int32_t __sys_poll(struct pollfd *, uint64_t, signed);
int sys_ppoll(struct pollfd *, size_t, const struct timespec *,
const sigset_t *, size_t);
int sys_poll_metal(struct pollfd *, size_t, unsigned);
int sys_poll_nt(struct pollfd *, uint64_t, uint64_t *, const sigset_t *);
int sys_poll_nt(struct pollfd *, uint64_t, uint32_t *, const sigset_t *);
const char *DescribePollFds(char[300], ssize_t, struct pollfd *, size_t);
#define DescribePollFds(x, y, z) DescribePollFds(alloca(300), x, y, z)

View file

@ -22,6 +22,7 @@
#include "libc/calls/sig.internal.h"
#include "libc/calls/struct/timespec.h"
#include "libc/errno.h"
#include "libc/intrin/weaken.h"
#include "libc/nt/enum/wait.h"
#include "libc/nt/enum/wsa.h"
#include "libc/nt/errors.h"
@ -113,6 +114,10 @@ BlockingOperation:
if (WSAGetOverlappedResult(f->handle, overlapped, &got, nonblock, flags)) {
rc = got;
} else {
if (_weaken(pthread_testcancel_np) &&
(err = _weaken(pthread_testcancel_np)())) {
return ecanceled();
}
rc = -1;
err = WSAGetLastError();
if (err == kNtErrorOperationAborted) {