mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-05-17 11:00:08 +00:00
Rewrite Windows accept()
This change should fix the Windows issues Qt Creator has been having, by ensuring accept() and accept4() work in O_NONBLOCK mode. I switched away from AcceptEx() which is buggy, back to using WSAAccept(). This requires making a tradeoff where we have to accept a busy loop. However it is low latency in nature, just like our new and improved Windows poll() code. I was furthermore able to eliminate a bunch of Windows-related test todos.
This commit is contained in:
parent
6f868fe1de
commit
acd6c32184
20 changed files with 622 additions and 209 deletions
|
@ -21,7 +21,7 @@
|
||||||
#include "libc/intrin/weaken.h"
|
#include "libc/intrin/weaken.h"
|
||||||
#include "libc/thread/posixthread.internal.h"
|
#include "libc/thread/posixthread.internal.h"
|
||||||
|
|
||||||
int _check_cancel(void) {
|
textwindows int _check_cancel(void) {
|
||||||
if (_weaken(_pthread_cancel_ack) && //
|
if (_weaken(_pthread_cancel_ack) && //
|
||||||
_pthread_self() && !(_pthread_self()->pt_flags & PT_NOCANCEL) &&
|
_pthread_self() && !(_pthread_self()->pt_flags & PT_NOCANCEL) &&
|
||||||
atomic_load_explicit(&_pthread_self()->pt_canceled,
|
atomic_load_explicit(&_pthread_self()->pt_canceled,
|
||||||
|
|
|
@ -25,6 +25,7 @@ int __ensurefds(int);
|
||||||
uint32_t sys_getuid_nt(void);
|
uint32_t sys_getuid_nt(void);
|
||||||
int __ensurefds_unlocked(int);
|
int __ensurefds_unlocked(int);
|
||||||
void __printfds(struct Fd *, size_t);
|
void __printfds(struct Fd *, size_t);
|
||||||
|
int __sigcheck(sigset_t, bool);
|
||||||
int CountConsoleInputBytes(void);
|
int CountConsoleInputBytes(void);
|
||||||
int FlushConsoleInputBytes(void);
|
int FlushConsoleInputBytes(void);
|
||||||
int64_t GetConsoleInputHandle(void);
|
int64_t GetConsoleInputHandle(void);
|
||||||
|
|
|
@ -17,25 +17,19 @@
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/calls/internal.h"
|
#include "libc/calls/internal.h"
|
||||||
#include "libc/calls/sig.internal.h"
|
#include "libc/calls/struct/sigset.h"
|
||||||
#include "libc/intrin/atomic.h"
|
#include "libc/intrin/atomic.h"
|
||||||
#include "libc/intrin/weaken.h"
|
|
||||||
#include "libc/nt/synchronization.h"
|
#include "libc/nt/synchronization.h"
|
||||||
#include "libc/sysv/consts/sicode.h"
|
|
||||||
#include "libc/sysv/errfuns.h"
|
|
||||||
#include "libc/thread/posixthread.internal.h"
|
#include "libc/thread/posixthread.internal.h"
|
||||||
#ifdef __x86_64__
|
#ifdef __x86_64__
|
||||||
|
|
||||||
// returns 0 on timeout or spurious wakeup
|
// returns 0 on timeout or spurious wakeup
|
||||||
// raises EINTR if a signal delivery interrupted wait operation
|
// raises EINTR if a signal delivery interrupted wait operation
|
||||||
// raises ECANCELED if this POSIX thread was canceled in masked mode
|
// raises ECANCELED if this POSIX thread was canceled in masked mode
|
||||||
static textwindows int _park_thread(uint32_t msdelay, sigset_t waitmask,
|
textwindows static int _park_thread(uint32_t msdelay, sigset_t waitmask,
|
||||||
bool restartable) {
|
bool restartable) {
|
||||||
int sig, handler_was_called;
|
if (__sigcheck(waitmask, restartable) == -1)
|
||||||
if (_check_cancel() == -1)
|
|
||||||
return -1;
|
return -1;
|
||||||
if (_weaken(__sig_get) && (sig = _weaken(__sig_get)(waitmask)))
|
|
||||||
goto HandleSignal;
|
|
||||||
int expect = 0;
|
int expect = 0;
|
||||||
atomic_int futex = 0;
|
atomic_int futex = 0;
|
||||||
struct PosixThread *pt = _pthread_self();
|
struct PosixThread *pt = _pthread_self();
|
||||||
|
@ -43,17 +37,8 @@ static textwindows int _park_thread(uint32_t msdelay, sigset_t waitmask,
|
||||||
atomic_store_explicit(&pt->pt_blocker, &futex, memory_order_release);
|
atomic_store_explicit(&pt->pt_blocker, &futex, memory_order_release);
|
||||||
bool32 ok = WaitOnAddress(&futex, &expect, sizeof(int), msdelay);
|
bool32 ok = WaitOnAddress(&futex, &expect, sizeof(int), msdelay);
|
||||||
atomic_store_explicit(&pt->pt_blocker, 0, memory_order_release);
|
atomic_store_explicit(&pt->pt_blocker, 0, memory_order_release);
|
||||||
if (ok && _weaken(__sig_get) && (sig = _weaken(__sig_get)(waitmask))) {
|
if (ok && __sigcheck(waitmask, restartable) == -1)
|
||||||
HandleSignal:
|
return -1;
|
||||||
handler_was_called = _weaken(__sig_relay)(sig, SI_KERNEL, waitmask);
|
|
||||||
if (_check_cancel() == -1)
|
|
||||||
return -1;
|
|
||||||
if (handler_was_called & SIG_HANDLED_NO_RESTART)
|
|
||||||
return eintr();
|
|
||||||
if (handler_was_called & SIG_HANDLED_SA_RESTART)
|
|
||||||
if (!restartable)
|
|
||||||
return eintr();
|
|
||||||
}
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,23 +16,15 @@
|
||||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/assert.h"
|
|
||||||
#include "libc/calls/calls.h"
|
|
||||||
#include "libc/calls/internal.h"
|
#include "libc/calls/internal.h"
|
||||||
#include "libc/calls/sig.internal.h"
|
|
||||||
#include "libc/calls/state.internal.h"
|
#include "libc/calls/state.internal.h"
|
||||||
#include "libc/calls/struct/sigaction.h"
|
#include "libc/calls/struct/sigset.h"
|
||||||
#include "libc/calls/struct/sigset.internal.h"
|
#include "libc/calls/struct/sigset.internal.h"
|
||||||
#include "libc/calls/struct/timespec.h"
|
#include "libc/calls/struct/timespec.h"
|
||||||
#include "libc/calls/syscall_support-nt.internal.h"
|
#include "libc/calls/syscall_support-nt.internal.h"
|
||||||
#include "libc/dce.h"
|
|
||||||
#include "libc/errno.h"
|
|
||||||
#include "libc/intrin/atomic.h"
|
#include "libc/intrin/atomic.h"
|
||||||
#include "libc/intrin/fds.h"
|
#include "libc/intrin/fds.h"
|
||||||
#include "libc/intrin/strace.h"
|
|
||||||
#include "libc/intrin/weaken.h"
|
|
||||||
#include "libc/macros.h"
|
#include "libc/macros.h"
|
||||||
#include "libc/mem/mem.h"
|
|
||||||
#include "libc/nt/console.h"
|
#include "libc/nt/console.h"
|
||||||
#include "libc/nt/enum/filetype.h"
|
#include "libc/nt/enum/filetype.h"
|
||||||
#include "libc/nt/enum/wait.h"
|
#include "libc/nt/enum/wait.h"
|
||||||
|
@ -42,22 +34,13 @@
|
||||||
#include "libc/nt/runtime.h"
|
#include "libc/nt/runtime.h"
|
||||||
#include "libc/nt/struct/pollfd.h"
|
#include "libc/nt/struct/pollfd.h"
|
||||||
#include "libc/nt/synchronization.h"
|
#include "libc/nt/synchronization.h"
|
||||||
#include "libc/nt/thread.h"
|
|
||||||
#include "libc/nt/thunk/msabi.h"
|
|
||||||
#include "libc/nt/time.h"
|
#include "libc/nt/time.h"
|
||||||
#include "libc/nt/winsock.h"
|
#include "libc/nt/winsock.h"
|
||||||
#include "libc/runtime/runtime.h"
|
|
||||||
#include "libc/sock/internal.h"
|
#include "libc/sock/internal.h"
|
||||||
#include "libc/sock/struct/pollfd.h"
|
#include "libc/sock/struct/pollfd.h"
|
||||||
#include "libc/sock/struct/pollfd.internal.h"
|
|
||||||
#include "libc/stdio/sysparam.h"
|
|
||||||
#include "libc/sysv/consts/o.h"
|
#include "libc/sysv/consts/o.h"
|
||||||
#include "libc/sysv/consts/poll.h"
|
|
||||||
#include "libc/sysv/consts/sicode.h"
|
|
||||||
#include "libc/sysv/consts/sig.h"
|
|
||||||
#include "libc/sysv/errfuns.h"
|
#include "libc/sysv/errfuns.h"
|
||||||
#include "libc/thread/posixthread.internal.h"
|
#include "libc/thread/posixthread.internal.h"
|
||||||
#include "libc/thread/tls.h"
|
|
||||||
#ifdef __x86_64__
|
#ifdef __x86_64__
|
||||||
|
|
||||||
#define POLL_INTERVAL_MS 10
|
#define POLL_INTERVAL_MS 10
|
||||||
|
@ -75,24 +58,22 @@
|
||||||
#define POLLPRI_ 0x0400 // MSDN unsupported
|
#define POLLPRI_ 0x0400 // MSDN unsupported
|
||||||
// </sync libc/sysv/consts.sh>
|
// </sync libc/sysv/consts.sh>
|
||||||
|
|
||||||
textwindows static dontinline struct timespec sys_poll_nt_now(void) {
|
textwindows dontinline static struct timespec sys_poll_nt_now(void) {
|
||||||
uint64_t hectons;
|
uint64_t hectons;
|
||||||
QueryUnbiasedInterruptTimePrecise(&hectons);
|
QueryUnbiasedInterruptTimePrecise(&hectons);
|
||||||
return timespec_fromnanos(hectons * 100);
|
return timespec_fromnanos(hectons * 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
textwindows static int sys_poll_nt_sigcheck(sigset_t sigmask) {
|
textwindows static uint32_t sys_poll_nt_waitms(struct timespec deadline) {
|
||||||
int sig, handler_was_called;
|
struct timespec now = sys_poll_nt_now();
|
||||||
if (_check_cancel() == -1)
|
if (timespec_cmp(now, deadline) < 0) {
|
||||||
return -1;
|
struct timespec remain = timespec_sub(deadline, now);
|
||||||
if (_weaken(__sig_get) && (sig = _weaken(__sig_get)(sigmask))) {
|
int64_t millis = timespec_tomillis(remain);
|
||||||
handler_was_called = _weaken(__sig_relay)(sig, SI_KERNEL, sigmask);
|
uint32_t waitfor = MIN(millis, 0xffffffffu);
|
||||||
if (_check_cancel() == -1)
|
return MIN(waitfor, POLL_INTERVAL_MS);
|
||||||
return -1;
|
} else {
|
||||||
if (handler_was_called)
|
return 0; // we timed out
|
||||||
return eintr();
|
|
||||||
}
|
}
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Polls on the New Technology.
|
// Polls on the New Technology.
|
||||||
|
@ -100,22 +81,17 @@ textwindows static int sys_poll_nt_sigcheck(sigset_t sigmask) {
|
||||||
// This function is used to implement poll() and select(). You may poll
|
// This function is used to implement poll() and select(). You may poll
|
||||||
// on sockets, files and the console at the same time. We also poll for
|
// 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
|
// both signals and posix thread cancelation, while the poll is polling
|
||||||
textwindows static int sys_poll_nt_impl(struct pollfd *fds, uint64_t nfds,
|
textwindows static int sys_poll_nt_actual(struct pollfd *fds, uint64_t nfds,
|
||||||
uint32_t *ms, sigset_t sigmask) {
|
struct timespec deadline,
|
||||||
bool ok;
|
sigset_t waitmask) {
|
||||||
uint64_t millis;
|
|
||||||
int fileindices[64];
|
int fileindices[64];
|
||||||
int sockindices[64];
|
int sockindices[64];
|
||||||
int64_t filehands[64];
|
int64_t filehands[64];
|
||||||
struct PosixThread *pt;
|
struct PosixThread *pt;
|
||||||
int i, rc, ev, kind, gotsocks;
|
int i, rc, ev, kind, gotsocks;
|
||||||
struct sys_pollfd_nt sockfds[64];
|
struct sys_pollfd_nt sockfds[64];
|
||||||
struct timespec deadline, remain, now;
|
|
||||||
uint32_t cm, fi, wi, sn, pn, avail, waitfor, already_slept;
|
uint32_t cm, fi, wi, sn, pn, avail, waitfor, already_slept;
|
||||||
|
|
||||||
waitfor = ms ? *ms : -1u;
|
|
||||||
deadline = timespec_add(sys_poll_nt_now(), timespec_frommillis(waitfor));
|
|
||||||
|
|
||||||
// ensure revents is cleared
|
// ensure revents is cleared
|
||||||
for (i = 0; i < nfds; ++i)
|
for (i = 0; i < nfds; ++i)
|
||||||
fds[i].revents = 0;
|
fds[i].revents = 0;
|
||||||
|
@ -171,7 +147,7 @@ textwindows static int sys_poll_nt_impl(struct pollfd *fds, uint64_t nfds,
|
||||||
rc += !!fds[i].revents;
|
rc += !!fds[i].revents;
|
||||||
}
|
}
|
||||||
__fds_unlock();
|
__fds_unlock();
|
||||||
if (rc)
|
if (rc == -1)
|
||||||
return rc;
|
return rc;
|
||||||
|
|
||||||
// perform poll operation
|
// perform poll operation
|
||||||
|
@ -191,10 +167,7 @@ textwindows static int sys_poll_nt_impl(struct pollfd *fds, uint64_t nfds,
|
||||||
if ((ev & POLLWRNORM_) && !(ev & POLLRDNORM_)) {
|
if ((ev & POLLWRNORM_) && !(ev & POLLRDNORM_)) {
|
||||||
fds[fi].revents = fds[fi].events & (POLLRDNORM_ | POLLWRNORM_);
|
fds[fi].revents = fds[fi].events & (POLLRDNORM_ | POLLWRNORM_);
|
||||||
} else if (GetFileType(filehands[i]) == kNtFileTypePipe) {
|
} else if (GetFileType(filehands[i]) == kNtFileTypePipe) {
|
||||||
ok = PeekNamedPipe(filehands[i], 0, 0, 0, &avail, 0);
|
if (PeekNamedPipe(filehands[i], 0, 0, 0, &avail, 0)) {
|
||||||
POLLTRACE("PeekNamedPipe(%ld, 0, 0, 0, [%'u], 0) → {%hhhd, %d}",
|
|
||||||
filehands[i], avail, ok, GetLastError());
|
|
||||||
if (ok) {
|
|
||||||
if (avail)
|
if (avail)
|
||||||
fds[fi].revents = POLLRDNORM_;
|
fds[fi].revents = POLLRDNORM_;
|
||||||
} else if (GetLastError() == kNtErrorHandleEof ||
|
} else if (GetLastError() == kNtErrorHandleEof ||
|
||||||
|
@ -222,15 +195,7 @@ textwindows static int sys_poll_nt_impl(struct pollfd *fds, uint64_t nfds,
|
||||||
}
|
}
|
||||||
|
|
||||||
// determine how long to wait
|
// determine how long to wait
|
||||||
now = sys_poll_nt_now();
|
waitfor = sys_poll_nt_waitms(deadline);
|
||||||
if (timespec_cmp(now, deadline) < 0) {
|
|
||||||
remain = timespec_sub(deadline, now);
|
|
||||||
millis = timespec_tomillis(remain);
|
|
||||||
waitfor = MIN(millis, 0xffffffffu);
|
|
||||||
waitfor = MIN(waitfor, POLL_INTERVAL_MS);
|
|
||||||
} else {
|
|
||||||
waitfor = 0; // we timed out
|
|
||||||
}
|
|
||||||
|
|
||||||
// check for events and/or readiness on sockets
|
// check for events and/or readiness on sockets
|
||||||
// we always do this due to issues with POLLOUT
|
// we always do this due to issues with POLLOUT
|
||||||
|
@ -238,7 +203,7 @@ textwindows static int sys_poll_nt_impl(struct pollfd *fds, uint64_t nfds,
|
||||||
// if we need to wait, then we prefer to wait inside WSAPoll()
|
// if we need to wait, then we prefer to wait inside WSAPoll()
|
||||||
// this ensures network events are received in ~10µs not ~10ms
|
// this ensures network events are received in ~10µs not ~10ms
|
||||||
if (!rc && waitfor) {
|
if (!rc && waitfor) {
|
||||||
if (sys_poll_nt_sigcheck(sigmask))
|
if (__sigcheck(waitmask, false))
|
||||||
return -1;
|
return -1;
|
||||||
already_slept = waitfor;
|
already_slept = waitfor;
|
||||||
} else {
|
} else {
|
||||||
|
@ -253,7 +218,7 @@ textwindows static int sys_poll_nt_impl(struct pollfd *fds, uint64_t nfds,
|
||||||
++rc;
|
++rc;
|
||||||
}
|
}
|
||||||
} else if (already_slept) {
|
} else if (already_slept) {
|
||||||
if (sys_poll_nt_sigcheck(sigmask))
|
if (__sigcheck(waitmask, false))
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -269,7 +234,7 @@ textwindows static int sys_poll_nt_impl(struct pollfd *fds, uint64_t nfds,
|
||||||
// this ensures low latency for apps like emacs which with no sock
|
// this ensures low latency for apps like emacs which with no sock
|
||||||
// here we shall actually report that something can be written too
|
// here we shall actually report that something can be written too
|
||||||
if (!already_slept) {
|
if (!already_slept) {
|
||||||
if (sys_poll_nt_sigcheck(sigmask))
|
if (__sigcheck(waitmask, false))
|
||||||
return -1;
|
return -1;
|
||||||
pt = _pthread_self();
|
pt = _pthread_self();
|
||||||
filehands[pn] = pt->pt_semaphore = CreateSemaphore(0, 0, 1, 0);
|
filehands[pn] = pt->pt_semaphore = CreateSemaphore(0, 0, 1, 0);
|
||||||
|
@ -283,7 +248,7 @@ textwindows static int sys_poll_nt_impl(struct pollfd *fds, uint64_t nfds,
|
||||||
return __winerr();
|
return __winerr();
|
||||||
} else if (wi == pn) {
|
} else if (wi == pn) {
|
||||||
// our semaphore was signalled
|
// our semaphore was signalled
|
||||||
if (sys_poll_nt_sigcheck(sigmask))
|
if (__sigcheck(waitmask, false))
|
||||||
return -1;
|
return -1;
|
||||||
} else if ((wi ^ kNtWaitAbandoned) < pn) {
|
} else if ((wi ^ kNtWaitAbandoned) < pn) {
|
||||||
// this is possibly because a process or thread was killed
|
// this is possibly because a process or thread was killed
|
||||||
|
@ -328,7 +293,7 @@ textwindows static int sys_poll_nt_impl(struct pollfd *fds, uint64_t nfds,
|
||||||
} else {
|
} else {
|
||||||
// should only be possible on kNtWaitTimeout or semaphore abandoned
|
// should only be possible on kNtWaitTimeout or semaphore abandoned
|
||||||
// keep looping for events and we'll catch timeout when appropriate
|
// keep looping for events and we'll catch timeout when appropriate
|
||||||
if (sys_poll_nt_sigcheck(sigmask))
|
if (__sigcheck(waitmask, false))
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -341,11 +306,44 @@ textwindows static int sys_poll_nt_impl(struct pollfd *fds, uint64_t nfds,
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
textwindows static int sys_poll_nt_impl(struct pollfd *fds, uint64_t nfds,
|
||||||
|
struct timespec deadline,
|
||||||
|
const sigset_t waitmask) {
|
||||||
|
uint32_t waitms;
|
||||||
|
int i, n, rc, got = 0;
|
||||||
|
|
||||||
|
// fast path
|
||||||
|
if (nfds <= 63)
|
||||||
|
return sys_poll_nt_actual(fds, nfds, deadline, waitmask);
|
||||||
|
|
||||||
|
// clumsy path
|
||||||
|
for (;;) {
|
||||||
|
for (i = 0; i < nfds; i += 64) {
|
||||||
|
n = nfds - i;
|
||||||
|
n = n > 64 ? 64 : n;
|
||||||
|
rc = sys_poll_nt_actual(fds + i, n, timespec_zero, waitmask);
|
||||||
|
if (rc == -1)
|
||||||
|
return -1;
|
||||||
|
got += rc;
|
||||||
|
}
|
||||||
|
if (got)
|
||||||
|
return got;
|
||||||
|
if (!(waitms = sys_poll_nt_waitms(deadline)))
|
||||||
|
return 0;
|
||||||
|
if (_park_norestart(waitms, waitmask) == -1)
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
textwindows int sys_poll_nt(struct pollfd *fds, uint64_t nfds, uint32_t *ms,
|
textwindows int sys_poll_nt(struct pollfd *fds, uint64_t nfds, uint32_t *ms,
|
||||||
const sigset_t *sigmask) {
|
const sigset_t *sigmask) {
|
||||||
int rc;
|
int rc;
|
||||||
|
struct timespec now, timeout, deadline;
|
||||||
BLOCK_SIGNALS;
|
BLOCK_SIGNALS;
|
||||||
rc = sys_poll_nt_impl(fds, nfds, ms, sigmask ? *sigmask : 0);
|
now = ms ? sys_poll_nt_now() : timespec_zero;
|
||||||
|
timeout = ms ? timespec_frommillis(*ms) : timespec_max;
|
||||||
|
deadline = timespec_add(now, timeout);
|
||||||
|
rc = sys_poll_nt_impl(fds, nfds, deadline, sigmask ? *sigmask : _SigMask);
|
||||||
ALLOW_SIGNALS;
|
ALLOW_SIGNALS;
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,13 +26,6 @@
|
||||||
* should just create a separate thread for each client. poll() isn't a
|
* should just create a separate thread for each client. poll() isn't a
|
||||||
* scalable i/o solution on any platform.
|
* scalable i/o solution on any platform.
|
||||||
*
|
*
|
||||||
* On Windows it's only possible to poll 64 file descriptors at a time.
|
|
||||||
* This is a limitation imposed by WSAPoll(). Cosmopolitan Libc's poll()
|
|
||||||
* polyfill can go higher in some cases. For example, you can actually
|
|
||||||
* poll 64 sockets and 63 non-sockets at the same time. Furthermore,
|
|
||||||
* elements whose fd field is set to a negative number are ignored and
|
|
||||||
* will not count against this limit.
|
|
||||||
*
|
|
||||||
* One of the use cases for poll() is to quickly check if a number of
|
* One of the use cases for poll() is to quickly check if a number of
|
||||||
* file descriptors are valid. The canonical way to do this is to set
|
* file descriptors are valid. The canonical way to do this is to set
|
||||||
* events to 0 which prevents blocking and causes only the invalid,
|
* events to 0 which prevents blocking and causes only the invalid,
|
||||||
|
@ -46,6 +39,12 @@
|
||||||
* When XNU and BSD OSes report POLLHUP, they will always set POLLIN too
|
* When XNU and BSD OSes report POLLHUP, they will always set POLLIN too
|
||||||
* when POLLIN is requested, even in cases when there isn't unread data.
|
* when POLLIN is requested, even in cases when there isn't unread data.
|
||||||
*
|
*
|
||||||
|
* Your poll() function will check the status of all file descriptors
|
||||||
|
* before returning. This function won't block unless none of the fds
|
||||||
|
* had had any reportable status.
|
||||||
|
*
|
||||||
|
* The impact shutdown() will have on poll() is a dice roll across OSes.
|
||||||
|
*
|
||||||
* @param fds[𝑖].fd should be a socket, input pipe, or conosle input
|
* @param fds[𝑖].fd should be a socket, input pipe, or conosle input
|
||||||
* and if it's a negative number then the entry is ignored, plus
|
* and if it's a negative number then the entry is ignored, plus
|
||||||
* revents will be set to zero
|
* revents will be set to zero
|
||||||
|
|
|
@ -156,11 +156,14 @@ int ppoll(struct pollfd *fds, size_t nfds, const struct timespec *timeout,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
uint32_t ms;
|
uint32_t ms;
|
||||||
if (!timeout ||
|
uint32_t *msp;
|
||||||
ckd_add(&ms, timeout->tv_sec, (timeout->tv_nsec + 999999) / 1000000)) {
|
if (timeout &&
|
||||||
ms = -1u;
|
!ckd_add(&ms, timeout->tv_sec, (timeout->tv_nsec + 999999) / 1000000)) {
|
||||||
|
msp = &ms;
|
||||||
|
} else {
|
||||||
|
msp = 0;
|
||||||
}
|
}
|
||||||
fdcount = sys_poll_nt(fds, nfds, &ms, sigmask);
|
fdcount = sys_poll_nt(fds, nfds, msp, sigmask);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsOpenbsd() && fdcount != -1) {
|
if (IsOpenbsd() && fdcount != -1) {
|
||||||
|
|
|
@ -384,12 +384,14 @@ textwindows static int ProcessMouseEvent(const struct NtInputRecord *r,
|
||||||
kNtLeftAltPressed | kNtRightAltPressed))) {
|
kNtLeftAltPressed | kNtRightAltPressed))) {
|
||||||
// we disable mouse highlighting when the tty is put in raw mode
|
// we disable mouse highlighting when the tty is put in raw mode
|
||||||
// to mouse wheel events with widely understood vt100 arrow keys
|
// to mouse wheel events with widely understood vt100 arrow keys
|
||||||
*p++ = 033;
|
for (int i = 0; i < 3; ++i) {
|
||||||
*p++ = !__keystroke.ohno_decckm ? '[' : 'O';
|
*p++ = 033;
|
||||||
if (isup) {
|
*p++ = !__keystroke.ohno_decckm ? '[' : 'O';
|
||||||
*p++ = 'A';
|
if (isup) {
|
||||||
} else {
|
*p++ = 'A';
|
||||||
*p++ = 'B';
|
} else {
|
||||||
|
*p++ = 'B';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if ((bs || currentbs) && (__ttyconf.magic & kTtyXtMouse)) {
|
} else if ((bs || currentbs) && (__ttyconf.magic & kTtyXtMouse)) {
|
||||||
|
|
40
libc/calls/sigcheck.c
Normal file
40
libc/calls/sigcheck.c
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||||
|
│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │
|
||||||
|
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||||
|
│ Copyright 2024 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/sig.internal.h"
|
||||||
|
#include "libc/intrin/weaken.h"
|
||||||
|
#include "libc/sysv/consts/sicode.h"
|
||||||
|
#include "libc/sysv/errfuns.h"
|
||||||
|
|
||||||
|
textwindows int __sigcheck(sigset_t waitmask, bool restartable) {
|
||||||
|
int sig, handler_was_called;
|
||||||
|
if (_check_cancel() == -1)
|
||||||
|
return -1;
|
||||||
|
if (_weaken(__sig_get) && (sig = _weaken(__sig_get)(waitmask))) {
|
||||||
|
handler_was_called = _weaken(__sig_relay)(sig, SI_KERNEL, waitmask);
|
||||||
|
if (_check_cancel() == -1)
|
||||||
|
return -1;
|
||||||
|
if (handler_was_called & SIG_HANDLED_NO_RESTART)
|
||||||
|
return eintr();
|
||||||
|
if (handler_was_called & SIG_HANDLED_SA_RESTART)
|
||||||
|
if (!restartable)
|
||||||
|
return eintr();
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -16,115 +16,87 @@
|
||||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/assert.h"
|
|
||||||
#include "libc/atomic.h"
|
|
||||||
#include "libc/calls/internal.h"
|
#include "libc/calls/internal.h"
|
||||||
#include "libc/intrin/fds.h"
|
|
||||||
#include "libc/calls/struct/sigset.internal.h"
|
#include "libc/calls/struct/sigset.internal.h"
|
||||||
#include "libc/cosmo.h"
|
#include "libc/calls/syscall_support-nt.internal.h"
|
||||||
#include "libc/errno.h"
|
#include "libc/errno.h"
|
||||||
#include "libc/nt/enum/wsaid.h"
|
#include "libc/intrin/kprintf.h"
|
||||||
|
#include "libc/nt/errors.h"
|
||||||
|
#include "libc/nt/struct/pollfd.h"
|
||||||
#include "libc/nt/thunk/msabi.h"
|
#include "libc/nt/thunk/msabi.h"
|
||||||
#include "libc/nt/winsock.h"
|
#include "libc/nt/winsock.h"
|
||||||
#include "libc/sock/internal.h"
|
#include "libc/sock/internal.h"
|
||||||
#include "libc/sock/struct/sockaddr.h"
|
#include "libc/sock/struct/sockaddr.h"
|
||||||
#include "libc/sock/wsaid.internal.h"
|
#include "libc/sock/syscall_fd.internal.h"
|
||||||
#include "libc/str/str.h"
|
#include "libc/sysv/consts/fio.h"
|
||||||
#include "libc/sysv/consts/o.h"
|
#include "libc/sysv/consts/o.h"
|
||||||
|
#include "libc/sysv/consts/poll.h"
|
||||||
#include "libc/sysv/consts/sock.h"
|
#include "libc/sysv/consts/sock.h"
|
||||||
#include "libc/sysv/consts/sol.h"
|
#include "libc/sysv/consts/sol.h"
|
||||||
#include "libc/thread/thread.h"
|
#include "libc/sysv/errfuns.h"
|
||||||
#ifdef __x86_64__
|
#ifdef __x86_64__
|
||||||
|
|
||||||
|
#define POLL_INTERVAL_MS 10
|
||||||
|
|
||||||
__msabi extern typeof(__sys_setsockopt_nt) *const __imp_setsockopt;
|
__msabi extern typeof(__sys_setsockopt_nt) *const __imp_setsockopt;
|
||||||
__msabi extern typeof(__sys_closesocket_nt) *const __imp_closesocket;
|
__msabi extern typeof(__sys_closesocket_nt) *const __imp_closesocket;
|
||||||
|
__msabi extern typeof(__sys_ioctlsocket_nt) *const __imp_ioctlsocket;
|
||||||
|
|
||||||
union AcceptExAddr {
|
textwindows static int sys_accept_nt_impl(struct Fd *f,
|
||||||
struct sockaddr_storage addr;
|
struct sockaddr_storage *addr,
|
||||||
char buf[sizeof(struct sockaddr_storage) + 16];
|
int accept4_flags,
|
||||||
};
|
sigset_t waitmask) {
|
||||||
|
|
||||||
struct AcceptExBuffer {
|
|
||||||
union AcceptExAddr local;
|
|
||||||
union AcceptExAddr remote;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct AcceptResources {
|
|
||||||
int64_t handle;
|
int64_t handle;
|
||||||
};
|
|
||||||
|
|
||||||
struct AcceptArgs {
|
|
||||||
int64_t listensock;
|
|
||||||
struct AcceptExBuffer *buffer;
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct {
|
|
||||||
atomic_uint once;
|
|
||||||
bool32 (*__msabi lpAcceptEx)(
|
|
||||||
int64_t sListenSocket, int64_t sAcceptSocket,
|
|
||||||
void *out_lpOutputBuffer /*[recvlen+local+remoteaddrlen]*/,
|
|
||||||
uint32_t dwReceiveDataLength, uint32_t dwLocalAddressLength,
|
|
||||||
uint32_t dwRemoteAddressLength, uint32_t *out_lpdwBytesReceived,
|
|
||||||
struct NtOverlapped *inout_lpOverlapped);
|
|
||||||
} g_acceptex;
|
|
||||||
|
|
||||||
static void acceptex_init(void) {
|
|
||||||
static struct NtGuid AcceptExGuid = WSAID_ACCEPTEX;
|
|
||||||
g_acceptex.lpAcceptEx = __get_wsaid(&AcceptExGuid);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void sys_accept_nt_unwind(void *arg) {
|
|
||||||
struct AcceptResources *resources = arg;
|
|
||||||
if (resources->handle != -1) {
|
|
||||||
__imp_closesocket(resources->handle);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int sys_accept_nt_start(int64_t handle, struct NtOverlapped *overlap,
|
|
||||||
uint32_t *flags, void *arg) {
|
|
||||||
struct AcceptArgs *args = arg;
|
|
||||||
cosmo_once(&g_acceptex.once, acceptex_init);
|
|
||||||
if (g_acceptex.lpAcceptEx(args->listensock, handle, args->buffer, 0,
|
|
||||||
sizeof(args->buffer->local),
|
|
||||||
sizeof(args->buffer->remote), 0, overlap)) {
|
|
||||||
return 0;
|
|
||||||
} else {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
textwindows int sys_accept_nt(struct Fd *f, struct sockaddr_storage *addr,
|
|
||||||
int accept4_flags) {
|
|
||||||
int client = -1;
|
int client = -1;
|
||||||
sigset_t m = __sig_block();
|
|
||||||
struct AcceptResources resources = {-1};
|
|
||||||
pthread_cleanup_push(sys_accept_nt_unwind, &resources);
|
|
||||||
|
|
||||||
// creates resources for child socket
|
// accepting sockets must always be non-blocking at the os level. this
|
||||||
// inherit the listener configuration
|
// is because WSAAccept doesn't support overlapped i/o operations. the
|
||||||
if ((resources.handle = WSASocket(f->family, f->type, f->protocol, 0, 0,
|
// AcceptEx function claims to support overlapped i/o however it can't
|
||||||
kNtWsaFlagOverlapped)) == -1) {
|
// be canceled by CancelIoEx, which makes it quite useless to us sadly
|
||||||
client = __winsockerr();
|
// this can't be called in listen(), because then fork() will break it
|
||||||
goto Finish;
|
uint32_t mode = 1;
|
||||||
}
|
if (__imp_ioctlsocket(f->handle, FIONBIO, &mode))
|
||||||
|
return __winsockerr();
|
||||||
|
|
||||||
// accept network connection
|
for (;;) {
|
||||||
// this operation can re-enter, interrupt, cancel, block, timeout, etc.
|
|
||||||
struct AcceptExBuffer buffer;
|
// perform non-blocking accept
|
||||||
ssize_t bytes_received = __winsock_block(
|
// we assume listen() put f->handle in non-blocking mode
|
||||||
resources.handle, 0, !!(f->flags & O_NONBLOCK), f->rcvtimeo, m,
|
int32_t addrsize = sizeof(*addr);
|
||||||
sys_accept_nt_start, &(struct AcceptArgs){f->handle, &buffer});
|
struct sockaddr *paddr = (struct sockaddr *)addr;
|
||||||
if (bytes_received == -1) {
|
if ((handle = WSAAccept(f->handle, paddr, &addrsize, 0, 0)) != -1)
|
||||||
__imp_closesocket(resources.handle);
|
break;
|
||||||
goto Finish;
|
|
||||||
|
// return on genuine errors
|
||||||
|
uint32_t err = WSAGetLastError();
|
||||||
|
if (err != WSAEWOULDBLOCK) {
|
||||||
|
errno = __dos2errno(err);
|
||||||
|
if (errno == ECONNRESET)
|
||||||
|
errno = ECONNABORTED;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// we're done if user wants non-blocking
|
||||||
|
if (f->flags & O_NONBLOCK)
|
||||||
|
return eagain();
|
||||||
|
|
||||||
|
// check for signals and thread cancelation
|
||||||
|
// accept() will restart if SA_RESTART is used
|
||||||
|
if (__sigcheck(waitmask, true) == -1)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
// time to block
|
||||||
|
struct sys_pollfd_nt fds[1] = {{f->handle, POLLIN}};
|
||||||
|
if (WSAPoll(fds, 1, POLL_INTERVAL_MS) == -1)
|
||||||
|
return __winsockerr();
|
||||||
}
|
}
|
||||||
|
|
||||||
// inherit properties of listening socket
|
// inherit properties of listening socket
|
||||||
// errors ignored as if f->handle was created before forking
|
// errors ignored as if f->handle was created before forking
|
||||||
// this fails with WSAENOTSOCK, see
|
// this fails with WSAENOTSOCK, see
|
||||||
// https://github.com/jart/cosmopolitan/issues/1174
|
// https://github.com/jart/cosmopolitan/issues/1174
|
||||||
__imp_setsockopt(resources.handle, SOL_SOCKET, kNtSoUpdateAcceptContext,
|
__imp_setsockopt(handle, SOL_SOCKET, kNtSoUpdateAcceptContext, &f->handle,
|
||||||
&f->handle, sizeof(f->handle));
|
sizeof(f->handle));
|
||||||
|
|
||||||
// create file descriptor for new socket
|
// create file descriptor for new socket
|
||||||
// don't inherit the file open mode bits
|
// don't inherit the file open mode bits
|
||||||
|
@ -141,18 +113,18 @@ textwindows int sys_accept_nt(struct Fd *f, struct sockaddr_storage *addr,
|
||||||
g_fds.p[client].protocol = f->protocol;
|
g_fds.p[client].protocol = f->protocol;
|
||||||
g_fds.p[client].sndtimeo = f->sndtimeo;
|
g_fds.p[client].sndtimeo = f->sndtimeo;
|
||||||
g_fds.p[client].rcvtimeo = f->rcvtimeo;
|
g_fds.p[client].rcvtimeo = f->rcvtimeo;
|
||||||
g_fds.p[client].handle = resources.handle;
|
g_fds.p[client].handle = handle;
|
||||||
resources.handle = -1;
|
|
||||||
memcpy(addr, &buffer.remote.addr, sizeof(*addr));
|
|
||||||
g_fds.p[client].kind = kFdSocket;
|
g_fds.p[client].kind = kFdSocket;
|
||||||
|
|
||||||
Finish:
|
|
||||||
pthread_cleanup_pop(false);
|
|
||||||
__sig_unblock(m);
|
|
||||||
if (client == -1 && errno == ECONNRESET) {
|
|
||||||
errno = ECONNABORTED;
|
|
||||||
}
|
|
||||||
return client;
|
return client;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
textwindows int sys_accept_nt(struct Fd *f, struct sockaddr_storage *addr,
|
||||||
|
int accept4_flags) {
|
||||||
|
int rc;
|
||||||
|
BLOCK_SIGNALS;
|
||||||
|
rc = sys_accept_nt_impl(f, addr, accept4_flags, _SigMask);
|
||||||
|
ALLOW_SIGNALS;
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
#endif /* __x86_64__ */
|
#endif /* __x86_64__ */
|
||||||
|
|
|
@ -20,18 +20,28 @@
|
||||||
#include "libc/nt/thunk/msabi.h"
|
#include "libc/nt/thunk/msabi.h"
|
||||||
#include "libc/nt/winsock.h"
|
#include "libc/nt/winsock.h"
|
||||||
#include "libc/sock/internal.h"
|
#include "libc/sock/internal.h"
|
||||||
|
#include "libc/sock/struct/sockaddr.h"
|
||||||
#include "libc/sock/syscall_fd.internal.h"
|
#include "libc/sock/syscall_fd.internal.h"
|
||||||
|
#include "libc/sysv/consts/af.h"
|
||||||
|
#include "libc/sysv/consts/fio.h"
|
||||||
#ifdef __x86_64__
|
#ifdef __x86_64__
|
||||||
|
|
||||||
__msabi extern typeof(__sys_listen_nt) *const __imp_listen;
|
__msabi extern typeof(__sys_listen_nt) *const __imp_listen;
|
||||||
|
|
||||||
textwindows int sys_listen_nt(struct Fd *fd, int backlog) {
|
textwindows int sys_listen_nt(struct Fd *f, int backlog) {
|
||||||
npassert(fd->kind == kFdSocket);
|
unassert(f->kind == kFdSocket);
|
||||||
if (__imp_listen(fd->handle, backlog) != -1) {
|
|
||||||
return 0;
|
// winsock listen() requires bind() be called beforehand
|
||||||
} else {
|
if (!f->isbound) {
|
||||||
return __winsockerr();
|
struct sockaddr_in sin = {AF_INET};
|
||||||
|
if (sys_bind_nt(f, (struct sockaddr *)&sin, sizeof(sin)) == -1)
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (__imp_listen(f->handle, backlog) == -1)
|
||||||
|
return __winsockerr();
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* __x86_64__ */
|
#endif /* __x86_64__ */
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#ifndef COSMOPOLITAN_LIBC_SOCK_SYSCALL_INTERNAL_H_
|
#ifndef COSMOPOLITAN_LIBC_SOCK_SYSCALL_INTERNAL_H_
|
||||||
#define COSMOPOLITAN_LIBC_SOCK_SYSCALL_INTERNAL_H_
|
#define COSMOPOLITAN_LIBC_SOCK_SYSCALL_INTERNAL_H_
|
||||||
#include "libc/intrin/fds.h"
|
|
||||||
#include "libc/calls/struct/iovec.h"
|
#include "libc/calls/struct/iovec.h"
|
||||||
|
#include "libc/intrin/fds.h"
|
||||||
#include "libc/nt/struct/overlapped.h"
|
#include "libc/nt/struct/overlapped.h"
|
||||||
#include "libc/sock/struct/sockaddr.h"
|
#include "libc/sock/struct/sockaddr.h"
|
||||||
COSMOPOLITAN_C_START_
|
COSMOPOLITAN_C_START_
|
||||||
|
@ -10,6 +10,7 @@ void sys_connect_nt_cleanup(struct Fd *, bool);
|
||||||
int sys_accept_nt(struct Fd *, struct sockaddr_storage *, int);
|
int sys_accept_nt(struct Fd *, struct sockaddr_storage *, int);
|
||||||
int sys_bind_nt(struct Fd *, const void *, uint32_t);
|
int sys_bind_nt(struct Fd *, const void *, uint32_t);
|
||||||
int sys_closesocket_nt(struct Fd *);
|
int sys_closesocket_nt(struct Fd *);
|
||||||
|
int sys_ioctlsocket_nt(struct Fd *);
|
||||||
int sys_connect_nt(struct Fd *, const void *, uint32_t);
|
int sys_connect_nt(struct Fd *, const void *, uint32_t);
|
||||||
int sys_getpeername_nt(struct Fd *, void *, uint32_t *);
|
int sys_getpeername_nt(struct Fd *, void *, uint32_t *);
|
||||||
int sys_getsockname_nt(struct Fd *, void *, uint32_t *);
|
int sys_getsockname_nt(struct Fd *, void *, uint32_t *);
|
||||||
|
|
|
@ -37,9 +37,6 @@
|
||||||
#include "libc/thread/thread.h"
|
#include "libc/thread/thread.h"
|
||||||
|
|
||||||
TEST(O_NONBLOCK, canBeSetBySocket_toMakeListenNonBlocking) {
|
TEST(O_NONBLOCK, canBeSetBySocket_toMakeListenNonBlocking) {
|
||||||
// TODO(jart): this doesn't make any sense on windows
|
|
||||||
if (IsWindows())
|
|
||||||
return;
|
|
||||||
char buf[16] = {0};
|
char buf[16] = {0};
|
||||||
uint32_t addrsize = sizeof(struct sockaddr_in);
|
uint32_t addrsize = sizeof(struct sockaddr_in);
|
||||||
struct sockaddr_in addr = {
|
struct sockaddr_in addr = {
|
||||||
|
|
|
@ -33,8 +33,6 @@
|
||||||
// two clients send a udp packet containing their local address
|
// two clients send a udp packet containing their local address
|
||||||
// server verifies content of packet matches the peer's address
|
// server verifies content of packet matches the peer's address
|
||||||
TEST(recvfrom, test) {
|
TEST(recvfrom, test) {
|
||||||
if (!IsWindows())
|
|
||||||
return;
|
|
||||||
uint32_t addrsize = sizeof(struct sockaddr_in);
|
uint32_t addrsize = sizeof(struct sockaddr_in);
|
||||||
struct sockaddr_in server = {
|
struct sockaddr_in server = {
|
||||||
.sin_family = AF_INET,
|
.sin_family = AF_INET,
|
||||||
|
|
|
@ -41,9 +41,9 @@
|
||||||
|
|
||||||
void SetUpOnce(void) {
|
void SetUpOnce(void) {
|
||||||
if (IsNetbsd())
|
if (IsNetbsd())
|
||||||
exit(0);
|
exit(0); // no sendfile support
|
||||||
if (IsOpenbsd())
|
if (IsOpenbsd())
|
||||||
exit(0);
|
exit(0); // no sendfile support
|
||||||
testlib_enable_tmp_setup_teardown();
|
testlib_enable_tmp_setup_teardown();
|
||||||
ASSERT_SYS(0, 0, pledge("stdio rpath wpath cpath proc inet", 0));
|
ASSERT_SYS(0, 0, pledge("stdio rpath wpath cpath proc inet", 0));
|
||||||
}
|
}
|
||||||
|
@ -102,9 +102,6 @@ TEST(sendfile, testSeeking) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(sendfile, testPositioning) {
|
TEST(sendfile, testPositioning) {
|
||||||
// TODO(jart): fix test regression on windows
|
|
||||||
if (IsWindows())
|
|
||||||
return;
|
|
||||||
char buf[1024];
|
char buf[1024];
|
||||||
uint32_t addrsize = sizeof(struct sockaddr_in);
|
uint32_t addrsize = sizeof(struct sockaddr_in);
|
||||||
struct sockaddr_in addr = {
|
struct sockaddr_in addr = {
|
||||||
|
@ -130,9 +127,8 @@ TEST(sendfile, testPositioning) {
|
||||||
ASSERT_TRUE(errno == EINVAL || errno == EPIPE);
|
ASSERT_TRUE(errno == EINVAL || errno == EPIPE);
|
||||||
errno = 0;
|
errno = 0;
|
||||||
// XXX: WSL1 clobbers file offset on failure!
|
// XXX: WSL1 clobbers file offset on failure!
|
||||||
if (!__iswsl1()) {
|
if (!__iswsl1())
|
||||||
ASSERT_EQ(12, GetFileOffset(5));
|
ASSERT_EQ(12, GetFileOffset(5));
|
||||||
}
|
|
||||||
_Exit(0);
|
_Exit(0);
|
||||||
}
|
}
|
||||||
ASSERT_SYS(0, 0, close(3));
|
ASSERT_SYS(0, 0, close(3));
|
||||||
|
|
|
@ -32,7 +32,9 @@ TEST_POSIX_DIRECTDEPS = \
|
||||||
LIBC_INTRIN \
|
LIBC_INTRIN \
|
||||||
LIBC_MEM \
|
LIBC_MEM \
|
||||||
LIBC_PROC \
|
LIBC_PROC \
|
||||||
|
LIBC_LOG \
|
||||||
LIBC_RUNTIME \
|
LIBC_RUNTIME \
|
||||||
|
LIBC_SOCK \
|
||||||
LIBC_STDIO \
|
LIBC_STDIO \
|
||||||
LIBC_STR \
|
LIBC_STR \
|
||||||
LIBC_SYSV \
|
LIBC_SYSV \
|
||||||
|
|
71
test/posix/accept4_nonblock_test.c
Normal file
71
test/posix/accept4_nonblock_test.c
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <cosmo.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <poll.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
|
||||||
|
// Create server socket
|
||||||
|
int server_fd;
|
||||||
|
struct sockaddr_in address;
|
||||||
|
int addrlen = sizeof(address);
|
||||||
|
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
|
||||||
|
return 1;
|
||||||
|
address.sin_family = AF_INET;
|
||||||
|
address.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
||||||
|
address.sin_port = 0; // let os assign random port
|
||||||
|
if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)))
|
||||||
|
return 2;
|
||||||
|
if (getsockname(server_fd, (struct sockaddr *)&address,
|
||||||
|
(socklen_t *)&addrlen))
|
||||||
|
return 3;
|
||||||
|
if (listen(server_fd, SOMAXCONN))
|
||||||
|
return 4;
|
||||||
|
|
||||||
|
{
|
||||||
|
// poll server
|
||||||
|
struct pollfd fds[2] = {
|
||||||
|
{server_fd, POLLIN | POLLOUT},
|
||||||
|
};
|
||||||
|
int ret = poll(fds, 1, 0);
|
||||||
|
if (ret != 0)
|
||||||
|
return 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
// create client socket
|
||||||
|
int client_fd;
|
||||||
|
if ((client_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
|
||||||
|
return 6;
|
||||||
|
if (connect(client_fd, (struct sockaddr *)&address, sizeof(address)))
|
||||||
|
return 7;
|
||||||
|
|
||||||
|
// accept client
|
||||||
|
int server_client_fd;
|
||||||
|
if ((server_client_fd = accept4(server_fd, 0, 0, SOCK_NONBLOCK)) == -1)
|
||||||
|
return 8;
|
||||||
|
|
||||||
|
// check that it's non-blocking
|
||||||
|
char buf[1];
|
||||||
|
if (read(server_client_fd, buf, 1) != -1)
|
||||||
|
return 9;
|
||||||
|
if (errno != EAGAIN && errno != EWOULDBLOCK)
|
||||||
|
return 10;
|
||||||
|
|
||||||
|
// Clean up
|
||||||
|
if (close(server_client_fd))
|
||||||
|
return 12;
|
||||||
|
if (close(client_fd))
|
||||||
|
return 13;
|
||||||
|
if (close(server_fd))
|
||||||
|
return 14;
|
||||||
|
|
||||||
|
CheckForMemoryLeaks();
|
||||||
|
}
|
85
test/posix/accept_inherit_nonblock_test.c
Normal file
85
test/posix/accept_inherit_nonblock_test.c
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <cosmo.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <poll.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
void on_signal(int sig) {
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
|
||||||
|
// Create server socket
|
||||||
|
int server_fd;
|
||||||
|
struct sockaddr_in address;
|
||||||
|
int addrlen = sizeof(address);
|
||||||
|
if ((server_fd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0)) == -1)
|
||||||
|
return 1;
|
||||||
|
address.sin_family = AF_INET;
|
||||||
|
address.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
||||||
|
address.sin_port = 0; // let os assign random port
|
||||||
|
if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)))
|
||||||
|
return 2;
|
||||||
|
if (getsockname(server_fd, (struct sockaddr *)&address,
|
||||||
|
(socklen_t *)&addrlen))
|
||||||
|
return 3;
|
||||||
|
if (listen(server_fd, SOMAXCONN))
|
||||||
|
return 4;
|
||||||
|
|
||||||
|
{
|
||||||
|
// poll server
|
||||||
|
struct pollfd fds[] = {{server_fd, POLLIN | POLLOUT}};
|
||||||
|
int ret = poll(fds, 1, 0);
|
||||||
|
if (ret != 0)
|
||||||
|
return 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
// verify server socket is non-blocking
|
||||||
|
if (accept(server_fd, 0, 0) != -1)
|
||||||
|
return 20;
|
||||||
|
if (errno != EAGAIN && errno != EWOULDBLOCK)
|
||||||
|
return 21;
|
||||||
|
|
||||||
|
// create client socket
|
||||||
|
int client_fd;
|
||||||
|
if ((client_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
|
||||||
|
return 6;
|
||||||
|
if (connect(client_fd, (struct sockaddr *)&address, sizeof(address)))
|
||||||
|
return 7;
|
||||||
|
|
||||||
|
// prevent race condition
|
||||||
|
// impacts platforms like openbsd
|
||||||
|
fcntl(server_fd, F_SETFL, fcntl(server_fd, F_GETFL) & ~O_NONBLOCK);
|
||||||
|
|
||||||
|
// accept client
|
||||||
|
int server_client_fd;
|
||||||
|
if ((server_client_fd = accept(server_fd, 0, 0)) == -1)
|
||||||
|
return 8;
|
||||||
|
|
||||||
|
// check that non-blocking wasn't inherited from listener
|
||||||
|
char buf[1];
|
||||||
|
sigaction(SIGALRM, &(struct sigaction){.sa_handler = on_signal}, 0);
|
||||||
|
ualarm(100000, 0);
|
||||||
|
if (read(server_client_fd, buf, 1) != -1)
|
||||||
|
return 9;
|
||||||
|
if (errno != EINTR)
|
||||||
|
return 10;
|
||||||
|
|
||||||
|
// Clean up
|
||||||
|
if (close(server_client_fd))
|
||||||
|
return 12;
|
||||||
|
if (close(client_fd))
|
||||||
|
return 13;
|
||||||
|
if (close(server_fd))
|
||||||
|
return 14;
|
||||||
|
|
||||||
|
CheckForMemoryLeaks();
|
||||||
|
}
|
94
test/posix/accept_poll_test.c
Normal file
94
test/posix/accept_poll_test.c
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <cosmo.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <poll.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
|
||||||
|
// Create server socket
|
||||||
|
int server_fd;
|
||||||
|
struct sockaddr_in address;
|
||||||
|
int addrlen = sizeof(address);
|
||||||
|
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
|
||||||
|
return 1;
|
||||||
|
address.sin_family = AF_INET;
|
||||||
|
address.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
||||||
|
address.sin_port = 0; // let os assign random port
|
||||||
|
if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)))
|
||||||
|
return 2;
|
||||||
|
if (getsockname(server_fd, (struct sockaddr *)&address,
|
||||||
|
(socklen_t *)&addrlen))
|
||||||
|
return 3;
|
||||||
|
if (listen(server_fd, SOMAXCONN))
|
||||||
|
return 4;
|
||||||
|
|
||||||
|
{
|
||||||
|
// poll server
|
||||||
|
struct pollfd fds[2] = {
|
||||||
|
{server_fd, POLLIN | POLLOUT},
|
||||||
|
};
|
||||||
|
int ret = poll(fds, 1, 0);
|
||||||
|
if (ret != 0)
|
||||||
|
return 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
// create client socket
|
||||||
|
int client_fd;
|
||||||
|
if ((client_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
|
||||||
|
return 6;
|
||||||
|
if (connect(client_fd, (struct sockaddr *)&address, sizeof(address)))
|
||||||
|
return 7;
|
||||||
|
|
||||||
|
{
|
||||||
|
// poll server
|
||||||
|
struct pollfd fds[] = {{server_fd, POLLIN | POLLOUT}};
|
||||||
|
int ret = poll(fds, 1, -1u);
|
||||||
|
if (ret != 1)
|
||||||
|
return 8;
|
||||||
|
if (!(fds[0].revents & POLLIN))
|
||||||
|
return 9;
|
||||||
|
if (fds[0].revents & POLLOUT)
|
||||||
|
return 10;
|
||||||
|
if (fds[0].revents & POLLHUP)
|
||||||
|
return 11;
|
||||||
|
if (fds[0].revents & POLLERR)
|
||||||
|
return 12;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// poll server with invalid thing
|
||||||
|
struct pollfd fds[] = {
|
||||||
|
{server_fd, POLLIN | POLLOUT},
|
||||||
|
{666, POLLIN | POLLOUT},
|
||||||
|
};
|
||||||
|
int ret = poll(fds, 2, -1u);
|
||||||
|
if (ret != 2)
|
||||||
|
return 18;
|
||||||
|
if (!(fds[0].revents & POLLIN))
|
||||||
|
return 19;
|
||||||
|
if (fds[0].revents & POLLOUT)
|
||||||
|
return 20;
|
||||||
|
if (fds[1].revents & POLLIN)
|
||||||
|
return 21;
|
||||||
|
if (fds[1].revents & POLLOUT)
|
||||||
|
return 22;
|
||||||
|
if (!(fds[1].revents & POLLNVAL))
|
||||||
|
return 23;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean up
|
||||||
|
if (close(client_fd))
|
||||||
|
return 13;
|
||||||
|
if (close(server_fd))
|
||||||
|
return 14;
|
||||||
|
|
||||||
|
CheckForMemoryLeaks();
|
||||||
|
}
|
75
test/posix/nonblock_pipe2_test.c
Normal file
75
test/posix/nonblock_pipe2_test.c
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
#include <cosmo.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
int pipefd[2];
|
||||||
|
char buf[PIPE_BUF];
|
||||||
|
char buf2[PIPE_BUF];
|
||||||
|
ssize_t bytes_read;
|
||||||
|
ssize_t bytes_written;
|
||||||
|
|
||||||
|
// Create a pipe
|
||||||
|
if (pipe2(pipefd, O_NONBLOCK) == -1)
|
||||||
|
exit(1);
|
||||||
|
|
||||||
|
// Test 1: Reading from an empty pipe should fail with EAGAIN
|
||||||
|
bytes_read = read(pipefd[0], buf, PIPE_BUF);
|
||||||
|
if (bytes_read != -1 || errno != EAGAIN)
|
||||||
|
exit(4);
|
||||||
|
|
||||||
|
// Test 2: Writing to the pipe
|
||||||
|
bytes_written = write(pipefd[1], buf, PIPE_BUF);
|
||||||
|
if (bytes_written != PIPE_BUF)
|
||||||
|
exit(5);
|
||||||
|
|
||||||
|
// Test 3: Reading from the pipe after writing
|
||||||
|
bytes_read = read(pipefd[0], buf2, PIPE_BUF);
|
||||||
|
if (bytes_read != PIPE_BUF || memcmp(buf, buf2, PIPE_BUF))
|
||||||
|
exit(6);
|
||||||
|
|
||||||
|
// Test 4: Fill the pipe buffer
|
||||||
|
int ch = 10;
|
||||||
|
size_t total_written = 0;
|
||||||
|
for (;;) {
|
||||||
|
memset(buf, ch, PIPE_BUF);
|
||||||
|
bytes_written = write(pipefd[1], buf, PIPE_BUF);
|
||||||
|
if (bytes_written == -1) {
|
||||||
|
if (errno == EAGAIN || errno == EWOULDBLOCK) {
|
||||||
|
break; // Pipe is full
|
||||||
|
} else {
|
||||||
|
exit(7); // Unexpected error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
total_written += bytes_written;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test 5: Verify that we can read all the data we wrote
|
||||||
|
ch = 10;
|
||||||
|
size_t total_read = 0;
|
||||||
|
while (total_read < total_written) {
|
||||||
|
bytes_read = read(pipefd[0], buf2, PIPE_BUF);
|
||||||
|
if (bytes_read == -1)
|
||||||
|
exit(8);
|
||||||
|
memset(buf, ch, PIPE_BUF);
|
||||||
|
if (memcmp(buf, buf2, PIPE_BUF))
|
||||||
|
exit(9);
|
||||||
|
total_read += bytes_read;
|
||||||
|
}
|
||||||
|
if (total_read != total_written)
|
||||||
|
exit(10);
|
||||||
|
|
||||||
|
// Clean up
|
||||||
|
if (close(pipefd[0]))
|
||||||
|
exit(11);
|
||||||
|
if (close(pipefd[1]))
|
||||||
|
exit(12);
|
||||||
|
|
||||||
|
CheckForMemoryLeaks();
|
||||||
|
}
|
84
test/posix/nonblock_pipe_test.c
Normal file
84
test/posix/nonblock_pipe_test.c
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
#include <cosmo.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
int pipefd[2];
|
||||||
|
char buf[PIPE_BUF];
|
||||||
|
char buf2[PIPE_BUF];
|
||||||
|
ssize_t bytes_read;
|
||||||
|
ssize_t bytes_written;
|
||||||
|
|
||||||
|
// Create a pipe
|
||||||
|
if (pipe(pipefd) == -1)
|
||||||
|
exit(1);
|
||||||
|
|
||||||
|
// Set O_NONBLOCK flag on the pipe
|
||||||
|
for (int i = 0; i < 2; ++i) {
|
||||||
|
int flags;
|
||||||
|
if ((flags = fcntl(pipefd[i], F_GETFL, 0)) == -1)
|
||||||
|
exit(2);
|
||||||
|
if (fcntl(pipefd[i], F_SETFL, flags | O_NONBLOCK) == -1)
|
||||||
|
exit(3);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test 1: Reading from an empty pipe should fail with EAGAIN
|
||||||
|
bytes_read = read(pipefd[0], buf, PIPE_BUF);
|
||||||
|
if (bytes_read != -1 || errno != EAGAIN)
|
||||||
|
exit(4);
|
||||||
|
|
||||||
|
// Test 2: Writing to the pipe
|
||||||
|
bytes_written = write(pipefd[1], buf, PIPE_BUF);
|
||||||
|
if (bytes_written != PIPE_BUF)
|
||||||
|
exit(5);
|
||||||
|
|
||||||
|
// Test 3: Reading from the pipe after writing
|
||||||
|
bytes_read = read(pipefd[0], buf2, PIPE_BUF);
|
||||||
|
if (bytes_read != PIPE_BUF || memcmp(buf, buf2, PIPE_BUF))
|
||||||
|
exit(6);
|
||||||
|
|
||||||
|
// Test 4: Fill the pipe buffer
|
||||||
|
int ch = 10;
|
||||||
|
size_t total_written = 0;
|
||||||
|
for (;;) {
|
||||||
|
memset(buf, ch, PIPE_BUF);
|
||||||
|
bytes_written = write(pipefd[1], buf, PIPE_BUF);
|
||||||
|
if (bytes_written == -1) {
|
||||||
|
if (errno == EAGAIN || errno == EWOULDBLOCK) {
|
||||||
|
break; // Pipe is full
|
||||||
|
} else {
|
||||||
|
exit(7); // Unexpected error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
total_written += bytes_written;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test 5: Verify that we can read all the data we wrote
|
||||||
|
ch = 10;
|
||||||
|
size_t total_read = 0;
|
||||||
|
while (total_read < total_written) {
|
||||||
|
bytes_read = read(pipefd[0], buf2, PIPE_BUF);
|
||||||
|
if (bytes_read == -1)
|
||||||
|
exit(8);
|
||||||
|
memset(buf, ch, PIPE_BUF);
|
||||||
|
if (memcmp(buf, buf2, PIPE_BUF))
|
||||||
|
exit(9);
|
||||||
|
total_read += bytes_read;
|
||||||
|
}
|
||||||
|
if (total_read != total_written)
|
||||||
|
exit(10);
|
||||||
|
|
||||||
|
// Clean up
|
||||||
|
if (close(pipefd[0]))
|
||||||
|
exit(11);
|
||||||
|
if (close(pipefd[1]))
|
||||||
|
exit(12);
|
||||||
|
|
||||||
|
CheckForMemoryLeaks();
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue