From 4ae5223eae158bc4412604bcb31f063e9c1b5da0 Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Sun, 15 Oct 2023 23:42:09 -0700 Subject: [PATCH] Release Cosmopolitan v3.0 --- README.md | 6 ++--- examples/greenbean.c | 48 +++++++++++++++++++++---------------- libc/calls/uname.c | 8 ++++++- libc/integral/normalize.inc | 7 +++++- 4 files changed, 43 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index c202e8ed5..9f73941c7 100644 --- a/README.md +++ b/README.md @@ -186,7 +186,7 @@ compile. The only exception is the C preprocessor mode, which actually runs x86-64 GCC except with macros like `__x86_64__` undefined. This toolchain works great for C projects that are written in a portable -way and don't produce architecturue-specific artifacts. One example of a +way and don't produce architecture-specific artifacts. One example of a large project that can be easily built is GNU coreutils. ```sh @@ -421,8 +421,8 @@ header file. If you're doing your development work on Linux or BSD then you need just five files to get started. Here's what you do on Linux: ```sh -wget https://justine.lol/cosmopolitan/cosmopolitan-amalgamation-2.2.zip -unzip cosmopolitan-amalgamation-2.2.zip +wget https://justine.lol/cosmopolitan/cosmopolitan-amalgamation-3.0.zip +unzip cosmopolitan-amalgamation-3.0.zip printf 'main() { printf("hello world\\n"); }\n' >hello.c gcc -g -Os -static -nostdlib -nostdinc -fno-pie -no-pie -mno-red-zone \ -fno-omit-frame-pointer -pg -mnop-mcount -mno-tls-direct-seg-refs -gdwarf-4 \ diff --git a/examples/greenbean.c b/examples/greenbean.c index bf789ec8a..38531dc6f 100644 --- a/examples/greenbean.c +++ b/examples/greenbean.c @@ -73,6 +73,7 @@ "Cache-Control: private; max-age=0\r\n" int server; +int threads; atomic_int a_termsig; atomic_int a_workers; atomic_int a_messages; @@ -102,6 +103,14 @@ void SomethingImportantHappened(void) { unassert(!pthread_mutex_unlock(&statuslock)); } +wontreturn void ExplainPrlimit(void) { + kprintf("error: you need more resources; try running:\n" + "sudo prlimit --pid=$$ --nofile=%d\n" + "sudo prlimit --pid=$$ --nproc=%d\n", + threads * 2, threads * 2); + exit(1); +} + void *Worker(void *id) { pthread_setname_np(pthread_self(), "Worker"); @@ -111,7 +120,7 @@ void *Worker(void *id) { uint32_t clientsize; int inmsglen, outmsglen; struct sockaddr_in clientaddr; - char inbuf[1500], outbuf[1500], *p, *q; + char buf[1000], *p, *q; // musl libc and cosmopolitan libc support a posix thread extension // that makes thread cancelation work much better. your io routines @@ -131,6 +140,7 @@ void *Worker(void *id) { // accept() errors are generally ephemeral or recoverable // it'd potentially be a good idea to exponential backoff here if (errno == ECANCELED) continue; // pthread_cancel() was called + if (errno == EMFILE) ExplainPrlimit(); LOG("accept() returned %m"); SomethingHappened(); continue; @@ -156,7 +166,7 @@ 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)); + got = read(client, buf, sizeof(buf)); unassert(!pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, 0)); if (got <= 0) { if (!got) { @@ -174,7 +184,7 @@ void *Worker(void *id) { // check that client message wasn't fragmented into more reads InitHttpMessage(&msg, kHttpRequest); - if ((inmsglen = ParseHttpMessage(&msg, inbuf, got)) <= 0) { + if ((inmsglen = ParseHttpMessage(&msg, buf, got)) <= 0) { if (!inmsglen) { LOG("%6H client sent fragmented message"); } else { @@ -188,7 +198,7 @@ void *Worker(void *id) { ++a_messages; LOG("%6H received message from %hhu.%hhu.%hhu.%hhu:%hu for path %#.*s", clientip >> 24, clientip >> 16, clientip >> 8, clientip, - ntohs(clientaddr.sin_port), msg.uri.b - msg.uri.a, inbuf + msg.uri.a); + ntohs(clientaddr.sin_port), msg.uri.b - msg.uri.a, buf + msg.uri.a); SomethingHappened(); // display hello world html page for http://127.0.0.1:8080/ @@ -196,41 +206,40 @@ void *Worker(void *id) { int64_t unixts; struct timespec ts; if (msg.method == kHttpGet && - (msg.uri.b - msg.uri.a == 1 && inbuf[msg.uri.a + 0] == '/')) { + (msg.uri.b - msg.uri.a == 1 && buf[msg.uri.a + 0] == '/')) { q = "\r\n" "hello world\r\n" "

hello world

\r\n" "

this is a fun webpage\r\n" "

hosted by greenbean\r\n"; - p = stpcpy(outbuf, "HTTP/1.1 200 OK\r\n" STANDARD_RESPONSE_HEADERS - "Content-Type: text/html; charset=utf-8\r\n" - "Date: "); + p = stpcpy(buf, "HTTP/1.1 200 OK\r\n" STANDARD_RESPONSE_HEADERS + "Content-Type: text/html; charset=utf-8\r\n" + "Date: "); clock_gettime(0, &ts), unixts = ts.tv_sec; p = FormatHttpDateTime(p, gmtime_r(&unixts, &tm)); p = stpcpy(p, "\r\nContent-Length: "); p = FormatInt32(p, strlen(q)); p = stpcpy(p, "\r\n\r\n"); p = stpcpy(p, q); - outmsglen = p - outbuf; - sent = write(client, outbuf, outmsglen); + outmsglen = p - buf; + sent = write(client, buf, outmsglen); } else { // display 404 not found error page for every thing else q = "\r\n" "404 not found\r\n" "

404 not found

\r\n"; - p = stpcpy(outbuf, - "HTTP/1.1 404 Not Found\r\n" STANDARD_RESPONSE_HEADERS - "Content-Type: text/html; charset=utf-8\r\n" - "Date: "); + p = stpcpy(buf, "HTTP/1.1 404 Not Found\r\n" STANDARD_RESPONSE_HEADERS + "Content-Type: text/html; charset=utf-8\r\n" + "Date: "); clock_gettime(0, &ts), unixts = ts.tv_sec; p = FormatHttpDateTime(p, gmtime_r(&unixts, &tm)); p = stpcpy(p, "\r\nContent-Length: "); p = FormatInt32(p, strlen(q)); p = stpcpy(p, "\r\n\r\n"); p = stpcpy(p, q); - outmsglen = p - outbuf; - sent = write(client, outbuf, p - outbuf); + outmsglen = p - buf; + sent = write(client, buf, p - buf); } // if the client isn't pipelining and write() wrote the full @@ -283,7 +292,7 @@ int main(int argc, char *argv[]) { unassert(!sigaction(SIGTERM, &sa, 0)); // you can pass the number of threads you want as the first command arg - int threads = argc > 1 ? atoi(argv[1]) : __get_cpu_count(); + threads = argc > 1 ? atoi(argv[1]) : __get_cpu_count(); if (!(1 <= threads && threads <= 100000)) { tinyprint(2, "error: invalid number of threads\n", NULL); exit(1); @@ -372,10 +381,7 @@ int main(int argc, char *argv[]) { if ((rc = pthread_create(th + i, &attr, Worker, (void *)(intptr_t)i))) { --a_workers; kprintf("pthread_create failed: %s\n", strerror(rc)); - if (rc == EAGAIN) { - kprintf("sudo prlimit --pid=$$ --nofile=%d\n", threads * 2); - kprintf("sudo prlimit --pid=$$ --nproc=%d\n", threads * 2); - } + if (rc == EAGAIN) ExplainPrlimit(); if (!i) exit(1); threads = i; break; diff --git a/libc/calls/uname.c b/libc/calls/uname.c index c3a3136da..87453bb84 100644 --- a/libc/calls/uname.c +++ b/libc/calls/uname.c @@ -47,6 +47,12 @@ #define CTL_HW 6 #define HW_MACHINE 1 +#define COSMOPOLITAN_VERSION_STR__(x, y, z) #x "." #y "." #z +#define COSMOPOLITAN_VERSION_STR_(x, y, z) COSMOPOLITAN_VERSION_STR__(x, y, z) +#define COSMOPOLITAN_VERSION_STR \ + COSMOPOLITAN_VERSION_STR_(__COSMOPOLITAN_MAJOR__, __COSMOPOLITAN_MINOR__, \ + __COSMOPOLITAN_PATCH__) + // Gets BSD sysctl() string, guaranteeing NUL-terminator. // We ignore errors since this syscall mutates on ENOMEM. // In that case, sysctl() doesn't guarantee the nul term. @@ -175,7 +181,7 @@ int uname(struct utsname *uts) { } if (!rc) { char buf[SYS_NMLN]; - stpcpy(buf, "Cosmopolitan 3.0-alpha"); + stpcpy(buf, "Cosmopolitan " COSMOPOLITAN_VERSION_STR); if (*MODE) { strlcat(buf, " MODE=" MODE, SYS_NMLN); } diff --git a/libc/integral/normalize.inc b/libc/integral/normalize.inc index a62a4bf83..ef9834e93 100644 --- a/libc/integral/normalize.inc +++ b/libc/integral/normalize.inc @@ -1,4 +1,9 @@ -#define __COSMOPOLITAN__ 1 +#define __COSMOPOLITAN_MAJOR__ 3 +#define __COSMOPOLITAN_MINOR__ 0 +#define __COSMOPOLITAN_PATCH__ 0 +#define __COSMOPOLITAN__ \ + (100000000 * __COSMOPOLITAN_MAJOR__ + 1000000 * __COSMOPOLITAN_MINOR__ + \ + __COSMOPOLITAN_PATCH__) #ifndef __COUNTER__ #define __COUNTER__ __LINE__