mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-01-31 03:27:39 +00:00
Fix ctrl-c in redbean on Windows
This commit is contained in:
parent
d458642790
commit
4bcb107cb0
12 changed files with 326 additions and 32 deletions
|
@ -157,9 +157,6 @@ void *Worker(void *id) {
|
|||
// wait for next http message (non-fragmented required)
|
||||
unassert(!pthread_setcancelstate(PTHREAD_CANCEL_MASKED, 0));
|
||||
got = read(client, inbuf, sizeof(inbuf));
|
||||
for (int i = 0; i < got; ++i) {
|
||||
if (!inbuf[i]) inbuf[i] = 1;
|
||||
}
|
||||
unassert(!pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, 0));
|
||||
if (got <= 0) {
|
||||
if (!got) {
|
||||
|
|
|
@ -247,6 +247,7 @@ static textwindows int __sig_killer(struct PosixThread *pt, int sig, int sic) {
|
|||
STRACE("kill contention of %u on tid %d", old_suspend_count,
|
||||
_pthread_tid(pt));
|
||||
pt->tib->tib_sigpending |= 1ull << (sig - 1);
|
||||
ResumeThread(th);
|
||||
return 0;
|
||||
}
|
||||
struct NtContext nc;
|
||||
|
|
|
@ -1059,15 +1059,12 @@ privileged size_t kvsnprintf(char *b, size_t n, const char *fmt, va_list v) {
|
|||
privileged void kvprintf(const char *fmt, va_list v) {
|
||||
#pragma GCC push_options
|
||||
#pragma GCC diagnostic ignored "-Walloca-larger-than="
|
||||
|
||||
long size = 3000;
|
||||
/* long size = __get_safe_size(8000, 3000); */
|
||||
/* if (size < 80) { */
|
||||
/* asm("int3"); */
|
||||
/* klog(STACK_ERROR, sizeof(STACK_ERROR) - 1); */
|
||||
/* return; */
|
||||
/* } */
|
||||
|
||||
long size = __get_safe_size(8000, 3000);
|
||||
if (size < 80) {
|
||||
asm("int3");
|
||||
klog(STACK_ERROR, sizeof(STACK_ERROR) - 1);
|
||||
return;
|
||||
}
|
||||
char *buf = alloca(size);
|
||||
CheckLargeStackAllocation(buf, size);
|
||||
#pragma GCC pop_options
|
||||
|
|
|
@ -107,7 +107,7 @@ static int PrintBacktraceUsingAddr2line(int fd, const struct StackFrame *bp) {
|
|||
}
|
||||
addr = frame->addr;
|
||||
#ifdef __x86_64__
|
||||
if (addr == (uintptr_t)_weaken(__gc)) {
|
||||
if (gi && addr == (uintptr_t)_weaken(__gc)) {
|
||||
do {
|
||||
--gi;
|
||||
} while ((addr = garbage->p[gi].ret) == (uintptr_t)_weaken(__gc));
|
||||
|
@ -116,7 +116,7 @@ static int PrintBacktraceUsingAddr2line(int fd, const struct StackFrame *bp) {
|
|||
argv[i++] = buf + j;
|
||||
buf[j++] = '0';
|
||||
buf[j++] = 'x';
|
||||
j += uint64toarray_radix16(addr - 1, buf + j) + 1;
|
||||
j += uint64toarray_radix16(addr, buf + j) + 1;
|
||||
}
|
||||
argv[i++] = NULL;
|
||||
if (sys_pipe2(pipefds, O_CLOEXEC) == -1) {
|
||||
|
|
|
@ -73,20 +73,7 @@ dontinstrument dontasan int PrintBacktraceUsingSymbols(
|
|||
}
|
||||
#endif
|
||||
if (addr) {
|
||||
if (
|
||||
#ifdef __x86_64__
|
||||
/*
|
||||
* we subtract one to handle the case of noreturn functions
|
||||
* with a call instruction at the end, since %rip in such
|
||||
* cases will point to the start of the next function.
|
||||
* generally %rip always points to the byte after the
|
||||
* instruction. one exception is in case like __restore_rt
|
||||
* where the kernel creates a stack frame that points to the
|
||||
* beginning of the function.
|
||||
*/
|
||||
(symbol = __get_symbol(st, addr - 1)) != -1 ||
|
||||
#endif
|
||||
(symbol = __get_symbol(st, addr)) != -1) {
|
||||
if ((symbol = __get_symbol(st, addr)) != -1) {
|
||||
addend = addr - st->addr_base;
|
||||
addend -= st->symbols[symbol].x;
|
||||
} else {
|
||||
|
|
|
@ -135,7 +135,7 @@ void(vflogf)(unsigned level, const char *file, int line, FILE *f,
|
|||
(dprintf)(STDERR_FILENO,
|
||||
"exiting due to aforementioned error (host %s pid %d tid %d)\n",
|
||||
buf32, getpid(), gettid());
|
||||
__die();
|
||||
_Exit(22);
|
||||
}
|
||||
|
||||
ALLOW_SIGNALS;
|
||||
|
|
|
@ -291,7 +291,7 @@ struct NtInterfaceInfo {
|
|||
*/
|
||||
|
||||
int32_t WSAStartup(uint16_t wVersionRequested, struct NtWsaData *lpWSAData)
|
||||
paramsnonnull() __wur;
|
||||
paramsnonnull();
|
||||
|
||||
int WSACleanup(void);
|
||||
int WSAGetLastError(void) nosideeffect;
|
||||
|
|
|
@ -17,8 +17,10 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/struct/sigset.internal.h"
|
||||
#include "libc/calls/syscall-nt.internal.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/dll.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/nt/console.h"
|
||||
#include "libc/nt/enum/ctrlevent.h"
|
||||
|
@ -53,6 +55,16 @@ textwindows int sys_kill_nt(int pid, int sig) {
|
|||
// just call raise() if we're targeting self
|
||||
if (pid <= 0 || pid == getpid()) {
|
||||
if (sig) {
|
||||
if (pid <= 0) {
|
||||
struct Dll *e;
|
||||
BLOCK_SIGNALS;
|
||||
__proc_lock();
|
||||
for (e = dll_first(__proc.list); e; e = dll_next(__proc.list, e)) {
|
||||
TerminateProcess(PROC_CONTAINER(e)->handle, sig);
|
||||
}
|
||||
__proc_unlock();
|
||||
ALLOW_SIGNALS;
|
||||
}
|
||||
return raise(sig);
|
||||
} else {
|
||||
return 0; // ability check passes
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include "libc/calls/struct/fd.internal.h"
|
||||
#include "libc/calls/struct/sigset.internal.h"
|
||||
#include "libc/cosmo.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/nt/enum/wsaid.h"
|
||||
#include "libc/nt/thunk/msabi.h"
|
||||
#include "libc/nt/winsock.h"
|
||||
|
@ -140,6 +141,9 @@ textwindows int sys_accept_nt(struct Fd *f, struct sockaddr_storage *addr,
|
|||
WeFailed:
|
||||
pthread_cleanup_pop(false);
|
||||
__sig_unblock(m);
|
||||
if (client == -1 && errno == ECONNRESET) {
|
||||
errno = ECONNABORTED;
|
||||
}
|
||||
return client;
|
||||
}
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ TOOL_NET_BINS = \
|
|||
|
||||
TOOL_NET_COMS = \
|
||||
o/$(MODE)/tool/net/dig.com \
|
||||
o/$(MODE)/tool/net/winbench.com \
|
||||
o/$(MODE)/tool/net/redbean.com \
|
||||
o/$(MODE)/tool/net/redbean-demo.com \
|
||||
o/$(MODE)/tool/net/redbean-static.com \
|
||||
|
@ -37,6 +38,7 @@ TOOL_NET_DIRECTDEPS = \
|
|||
LIBC_NEXGEN32E \
|
||||
LIBC_NT_IPHLPAPI \
|
||||
LIBC_NT_KERNEL32 \
|
||||
LIBC_NT_WS2_32 \
|
||||
LIBC_PROC \
|
||||
LIBC_RUNTIME \
|
||||
LIBC_SOCK \
|
||||
|
|
|
@ -6894,7 +6894,7 @@ static int HandleConnection(size_t i) {
|
|||
} else if (errno == ECONNABORTED) {
|
||||
LockInc(&shared->c.accepterrors);
|
||||
LockInc(&shared->c.acceptresets);
|
||||
WARNF("(srvr) %S accept error: %s", DescribeServer(),
|
||||
WARNF("(srvr) %s accept error: %s", DescribeServer(),
|
||||
"acceptreset: connection reset before accept");
|
||||
} else if (errno == ENETUNREACH || errno == EHOSTUNREACH ||
|
||||
errno == EOPNOTSUPP || errno == ENOPROTOOPT || errno == EPROTO) {
|
||||
|
@ -6903,7 +6903,7 @@ static int HandleConnection(size_t i) {
|
|||
WARNF("(srvr) accept error: %s ephemeral accept error: %m",
|
||||
DescribeServer());
|
||||
} else {
|
||||
DIEF("(srvr) %s accept error: %m", DescribeServer());
|
||||
WARNF("(srvr) %s accept error: %m", DescribeServer());
|
||||
}
|
||||
errno = 0;
|
||||
}
|
||||
|
|
294
tool/net/winbench.c
Normal file
294
tool/net/winbench.c
Normal file
|
@ -0,0 +1,294 @@
|
|||
#if 0
|
||||
/*─────────────────────────────────────────────────────────────────╗
|
||||
│ To the extent possible under law, Justine Tunney has waived │
|
||||
│ all copyright and related or neighboring rights to this file, │
|
||||
│ as it is written in the following disclaimers: │
|
||||
│ • http://unlicense.org/ │
|
||||
│ • http://creativecommons.org/publicdomain/zero/1.0/ │
|
||||
╚─────────────────────────────────────────────────────────────────*/
|
||||
#endif
|
||||
#include "libc/assert.h"
|
||||
#include "libc/atomic.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/struct/sigaction.h"
|
||||
#include "libc/calls/struct/timespec.h"
|
||||
#include "libc/dns/dns.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/fmt/conv.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/nt/accounting.h"
|
||||
#include "libc/nt/enum/wsaid.h"
|
||||
#include "libc/nt/errors.h"
|
||||
#include "libc/nt/iocp.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/nt/struct/overlapped.h"
|
||||
#include "libc/nt/thunk/msabi.h"
|
||||
#include "libc/nt/winsock.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/sock/sock.h"
|
||||
#include "libc/sock/struct/sockaddr.h"
|
||||
#include "libc/sock/wsaid.internal.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/af.h"
|
||||
#include "libc/sysv/consts/auxv.h"
|
||||
#include "libc/sysv/consts/ipproto.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/sysv/consts/sock.h"
|
||||
#include "libc/thread/thread.h"
|
||||
#include "libc/thread/thread2.h"
|
||||
#include "third_party/getopt/getopt.internal.h"
|
||||
|
||||
__msabi extern typeof(__sys_bind_nt) *const __imp_bind;
|
||||
__msabi extern typeof(__sys_closesocket_nt) *const __imp_closesocket;
|
||||
|
||||
enum State { SENDING, RECEIVING };
|
||||
|
||||
struct Client {
|
||||
struct NtOverlapped overlap;
|
||||
int64_t handle;
|
||||
enum State state;
|
||||
char buf[1500];
|
||||
struct NtIovec iov;
|
||||
};
|
||||
|
||||
static int64_t iocp;
|
||||
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;
|
||||
|
||||
static wontreturn void PrintUsage(int fd, int rc) {
|
||||
tinyprint(fd, "Usage: ", prog, " -H HOST -P PORT\n", NULL);
|
||||
exit(rc);
|
||||
}
|
||||
|
||||
static bool32 (*__msabi ConnectEx)(int64_t hSocket, const void *name,
|
||||
int namelen, const void *opt_lpSendBuffer,
|
||||
uint32_t dwSendDataLength,
|
||||
uint32_t *opt_out_lpdwBytesSent,
|
||||
struct NtOverlapped *lpOverlapped);
|
||||
|
||||
static int64_t CreateNewCompletionPort(uint32_t dwNumberOfConcurrentThreads) {
|
||||
return CreateIoCompletionPort(-1, 0, 0, dwNumberOfConcurrentThreads);
|
||||
}
|
||||
|
||||
static bool AssociateDeviceWithCompletionPort(int64_t hCompletionPort,
|
||||
int64_t hDevice,
|
||||
uint64_t dwCompletionKey) {
|
||||
int64_t h =
|
||||
CreateIoCompletionPort(hDevice, hCompletionPort, dwCompletionKey, 0);
|
||||
return h == hCompletionPort;
|
||||
}
|
||||
|
||||
static void NewClient(struct Client *client, const struct sockaddr_in *addr) {
|
||||
|
||||
client->handle = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0,
|
||||
kNtWsaFlagOverlapped);
|
||||
if (client->handle == -1) {
|
||||
fprintf(stderr, "WSASocket() failed w/ %d\n", WSAGetLastError());
|
||||
exit(1);
|
||||
}
|
||||
struct sockaddr_in bindaddr = {AF_INET};
|
||||
if (__imp_bind(client->handle, &bindaddr, sizeof(bindaddr))) {
|
||||
fprintf(stderr, "bind() failed w/ %d\n", WSAGetLastError());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (!AssociateDeviceWithCompletionPort(iocp, client->handle,
|
||||
(intptr_t)client)) {
|
||||
fprintf(stderr, "AssociateDeviceWithCompletionPort() failed w/ %d\n",
|
||||
GetLastError());
|
||||
exit(1);
|
||||
}
|
||||
if (!SetFileCompletionNotificationModes(client->handle,
|
||||
kNtFileSkipSetEventOnHandle)) {
|
||||
fprintf(stderr, "SetFileCompletionNotificationModes() failed w/ %d\n",
|
||||
GetLastError());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
struct NtOverlapped overlap = {0};
|
||||
if (!ConnectEx(client->handle, addr, sizeof(*addr), msg, msglen, 0,
|
||||
&overlap) &&
|
||||
WSAGetLastError() != kNtErrorIoPending) {
|
||||
fprintf(stderr, "ConnectEx() failed w/ %d\n", WSAGetLastError());
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void *Worker(void *arg) {
|
||||
while (!a_finished) {
|
||||
bool32 ok;
|
||||
uint32_t dwFlags;
|
||||
uint32_t dwBytes;
|
||||
struct Client *client;
|
||||
uint64_t CompletionKey;
|
||||
struct NtOverlapped *lpOverlapped;
|
||||
if (!(ok = GetQueuedCompletionStatus(iocp, &dwBytes, &CompletionKey,
|
||||
&lpOverlapped, -1u)) &&
|
||||
!lpOverlapped) {
|
||||
fprintf(stderr, "GetQueuedCompletionStatus() failed w/ %d\n",
|
||||
GetLastError());
|
||||
exit(1);
|
||||
}
|
||||
client = (struct Client *)CompletionKey;
|
||||
switch (client->state) {
|
||||
case SENDING:
|
||||
if (ok) {
|
||||
dwFlags = 0;
|
||||
client->state = RECEIVING;
|
||||
client->iov.buf = client->buf;
|
||||
client->iov.len = sizeof(client->buf) - 1;
|
||||
bzero(&client->overlap, sizeof(client->overlap));
|
||||
if (WSARecv(client->handle, &client->iov, 1, 0, &dwFlags,
|
||||
&client->overlap, 0) &&
|
||||
WSAGetLastError() != kNtErrorIoPending) {
|
||||
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) {
|
||||
if (!dwBytes) {
|
||||
fprintf(stderr, "got disconnect\n");
|
||||
break;
|
||||
}
|
||||
client->buf[dwBytes] = 0;
|
||||
if (!startswith(client->buf, "HTTP/1.1 200")) {
|
||||
fprintf(stderr, "request failed: %d bytes %`'s\n", dwBytes,
|
||||
client->buf);
|
||||
exit(1);
|
||||
}
|
||||
++a_requests;
|
||||
client->state = SENDING;
|
||||
client->iov.buf = msg;
|
||||
client->iov.len = msglen;
|
||||
bzero(&client->overlap, sizeof(client->overlap));
|
||||
if (WSASend(client->handle, &client->iov, 1, 0, 0, &client->overlap,
|
||||
0) &&
|
||||
WSAGetLastError() != kNtErrorIoPending) {
|
||||
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();
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void OnTerm(int sig) {
|
||||
a_termsig = sig;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
|
||||
prog = argv[0];
|
||||
if (!prog) {
|
||||
prog = "ab";
|
||||
}
|
||||
|
||||
int opt;
|
||||
int nclients = 20;
|
||||
int nthreads = GetMaximumProcessorCount(0xffff) * 2;
|
||||
struct sockaddr_in destaddr = {AF_INET, htons(8080), {htonl(0x7f000001)}};
|
||||
while ((opt = getopt(argc, argv, "hH:P:")) != -1) {
|
||||
switch (opt) {
|
||||
case 'H':
|
||||
destaddr.sin_addr.s_addr = inet_addr(optarg);
|
||||
break;
|
||||
case 'P':
|
||||
destaddr.sin_port = htons(atoi(optarg));
|
||||
break;
|
||||
case 'h':
|
||||
PrintUsage(1, 0);
|
||||
default:
|
||||
PrintUsage(2, 1);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t ip = ntohl(destaddr.sin_addr.s_addr);
|
||||
uint16_t port = ntohs(destaddr.sin_port);
|
||||
msglen = snprintf(msg, sizeof(msg),
|
||||
"GET / HTTP/1.1\r\n"
|
||||
"Host: %hhu.%hhu.%hhu.%hhu:%hu\r\n"
|
||||
"\r\n",
|
||||
ip >> 24, ip >> 16, ip >> 8, ip, port);
|
||||
|
||||
struct NtWsaData kNtWsaData = {0};
|
||||
WSAStartup(0x0202, &kNtWsaData);
|
||||
|
||||
static struct NtGuid ConnectExGuid = WSAID_CONNECTEX;
|
||||
ConnectEx = __get_wsaid(&ConnectExGuid);
|
||||
|
||||
iocp = CreateNewCompletionPort(0);
|
||||
if (!iocp) {
|
||||
fprintf(stderr, "CreateNewCompletionPort() failed w/ %d\n", GetLastError());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
struct sigaction sa = {.sa_handler = OnTerm};
|
||||
unassert(!sigaction(SIGINT, &sa, 0));
|
||||
unassert(!sigaction(SIGHUP, &sa, 0));
|
||||
unassert(!sigaction(SIGTERM, &sa, 0));
|
||||
|
||||
sigset_t block;
|
||||
sigfillset(&block);
|
||||
pthread_attr_t attr;
|
||||
int pagesz = getauxval(AT_PAGESZ);
|
||||
pthread_t *threads = calloc(nthreads, sizeof(pthread_t));
|
||||
unassert(!pthread_attr_init(&attr));
|
||||
unassert(!pthread_attr_setstacksize(&attr, 65536));
|
||||
unassert(!pthread_attr_setguardsize(&attr, pagesz));
|
||||
unassert(!pthread_attr_setsigmask_np(&attr, &block));
|
||||
for (int i = 0; i < nthreads; ++i) {
|
||||
if ((errno = pthread_create(threads + i, &attr, Worker, 0))) {
|
||||
perror("pthread_create");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
unassert(!pthread_attr_destroy(&attr));
|
||||
|
||||
struct timespec start_time = timespec_real();
|
||||
|
||||
struct Client *clients = calloc(nclients, sizeof(struct Client));
|
||||
for (int i = 0; i < nclients; ++i) {
|
||||
NewClient(clients + i, &destaddr);
|
||||
}
|
||||
|
||||
sleep(5);
|
||||
|
||||
a_finished = true;
|
||||
long request_count = a_requests;
|
||||
struct timespec end_time = timespec_real();
|
||||
|
||||
for (int i = 0; i < nthreads; ++i) {
|
||||
if ((errno = pthread_join(threads[i], 0))) {
|
||||
perror("pthread_join");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
double elapsed_seconds =
|
||||
timespec_tonanos(timespec_sub(end_time, start_time)) * 1e-9;
|
||||
double requests_per_second = request_count / elapsed_seconds;
|
||||
fprintf(stderr, "qps = %g\n", requests_per_second);
|
||||
}
|
Loading…
Reference in a new issue