Skip to content

Commit

Permalink
Reduce stack virtual memory consumption on Linux
Browse files Browse the repository at this point in the history
  • Loading branch information
jart committed Dec 26, 2024
1 parent cc8a9eb commit 327863d
Show file tree
Hide file tree
Showing 31 changed files with 583 additions and 166 deletions.
2 changes: 1 addition & 1 deletion examples/greenbean.c
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,7 @@ int main(int argc, char *argv[]) {
sigaddset(&block, SIGQUIT);
pthread_attr_t attr;
unassert(!pthread_attr_init(&attr));
unassert(!pthread_attr_setstacksize(&attr, 65536));
unassert(!pthread_attr_setstacksize(&attr, 65536 - getpagesize()));
unassert(!pthread_attr_setguardsize(&attr, getpagesize()));
unassert(!pthread_attr_setsigmask_np(&attr, &block));
unassert(!pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, 0));
Expand Down
18 changes: 16 additions & 2 deletions examples/stackexplorer.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,13 @@
│ • http://creativecommons.org/publicdomain/zero/1.0/ │
╚─────────────────────────────────────────────────────────────────*/
#endif
#include "libc/dce.h"
#include "libc/intrin/maps.h"
#include "libc/mem/alg.h"
#include "libc/mem/mem.h"
#include "libc/runtime/runtime.h"
#include "libc/runtime/stack.h"
#include "libc/runtime/winargs.internal.h"
#include "libc/stdio/stdio.h"
#include "libc/x/xasprintf.h"

Expand Down Expand Up @@ -67,8 +71,18 @@ int main(int argc, char *argv[]) {
Append((uintptr_t)&__auxv[i + 1],
xasprintf("&auxv[%d] = %#lx", i + 1, __auxv[i + 1]));
}
if (!IsWindows()) {
struct AddrSize stak = __get_main_stack();
Append((intptr_t)stak.addr + stak.size, "top of stack");
Append((intptr_t)stak.addr, "bottom of stack");
} else {
#ifdef __x86_64__
Append(GetStaticStackAddr(0) + GetStaticStackSize(), "top of stack");
Append(GetStaticStackAddr(0) + GetGuardSize(), "bottom of stack");
Append(GetStaticStackAddr(0), "bottom of guard region");
#endif
}
qsort(things.p, things.n, sizeof(*things.p), Compare);
for (int i = 0; i < things.n; ++i) {
for (int i = 0; i < things.n; ++i)
printf("%012lx %s\n", things.p[i].i, things.p[i].s);
}
}
17 changes: 17 additions & 0 deletions examples/thread.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#include <pthread.h>
#include <stdio.h>

// how to spawn a thread

void *my_thread(void *arg) {
printf("my_thread(%p) is running\n", arg);
return (void *)0x456L;
}

int main(int argc, char *argv[]) {
void *res;
pthread_t th;
pthread_create(&th, 0, my_thread, (void *)0x123L);
pthread_join(th, &res);
printf("my_thread() returned %p\n", res);
}
4 changes: 2 additions & 2 deletions libc/calls/getrlimit.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "libc/calls/syscall-sysv.internal.h"
#include "libc/dce.h"
#include "libc/intrin/describeflags.h"
#include "libc/intrin/rlimit.h"
#include "libc/intrin/strace.h"
#include "libc/runtime/runtime.h"
#include "libc/runtime/stack.h"
Expand All @@ -47,8 +48,7 @@ int getrlimit(int resource, struct rlimit *rlim) {
} else if (!IsWindows()) {
rc = sys_getrlimit(resource, rlim);
} else if (resource == RLIMIT_STACK) {
rlim->rlim_cur = GetStaticStackSize();
rlim->rlim_max = GetStaticStackSize();
*rlim = __rlimit_stack_get();
rc = 0;
} else if (resource == RLIMIT_AS) {
rlim->rlim_cur = __virtualmax;
Expand Down
5 changes: 4 additions & 1 deletion libc/calls/setrlimit.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/intrin/describeflags.h"
#include "libc/intrin/rlimit.h"
#include "libc/intrin/strace.h"
#include "libc/macros.h"
#include "libc/runtime/runtime.h"
Expand Down Expand Up @@ -88,10 +89,12 @@ int setrlimit(int resource, const struct rlimit *rlim) {
} else if (!IsWindows() && !(IsNetbsd() && resource == RLIMIT_AS)) {
rc = sys_setrlimit(resource, rlim);
} else if (resource == RLIMIT_STACK) {
rc = enotsup();
rc = 0;
} else {
rc = einval();
}
if (!rc && resource == RLIMIT_STACK)
__rlimit_stack_set(*rlim); // so __rlimit_stack_get() works on all OSes
if (resource == RLIMIT_AS) {
__virtualmax = rlim->rlim_cur;
errno = olde;
Expand Down
4 changes: 2 additions & 2 deletions libc/cosmo.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ int cosmo_futex_wake(_COSMO_ATOMIC(int) *, int, char);
int cosmo_futex_wait(_COSMO_ATOMIC(int) *, int, char, int,
const struct timespec *);

errno_t cosmo_stack_alloc(unsigned *, unsigned *, void **) libcesque;
errno_t cosmo_stack_free(void *, unsigned, unsigned) libcesque;
errno_t cosmo_stack_alloc(size_t *, size_t *, void **) libcesque;
errno_t cosmo_stack_free(void *, size_t, size_t) libcesque;
void cosmo_stack_clear(void) libcesque;
void cosmo_stack_setmaxstacks(int) libcesque;
int cosmo_stack_getmaxstacks(void) libcesque;
Expand Down
26 changes: 15 additions & 11 deletions libc/intrin/describemapflags.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,25 +16,29 @@
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/dce.h"
#include "libc/intrin/describeflags.h"
#include "libc/macros.h"
#include "libc/nt/enum/consolemodeflags.h"
#include "libc/sysv/consts/map.h"
#include "libc/sysv/consts/prot.h"

#define MAP_GROWSDOWN_LINUX 0x00000100

const char *_DescribeMapFlags(char buf[64], int x) {
const struct DescribeFlags kMapFlags[] = {
{MAP_PRIVATE, "PRIVATE"}, //
{MAP_ANONYMOUS, "ANONYMOUS"}, //
{MAP_SHARED, "SHARED"}, //
{MAP_FIXED, "FIXED"}, //
{MAP_FIXED_NOREPLACE, "FIXED_NOREPLACE"}, //
{MAP_HUGETLB, "HUGETLB"}, //
{MAP_CONCEAL, "CONCEAL"}, //
{MAP_LOCKED, "LOCKED"}, //
{MAP_NORESERVE, "NORESERVE"}, //
{MAP_NONBLOCK, "NONBLOCK"}, //
{MAP_POPULATE, "POPULATE"}, //
{MAP_PRIVATE, "PRIVATE"}, //
{MAP_ANONYMOUS, "ANONYMOUS"}, //
{MAP_SHARED, "SHARED"}, //
{MAP_FIXED, "FIXED"}, //
{MAP_FIXED_NOREPLACE, "FIXED_NOREPLACE"}, //
{MAP_HUGETLB, "HUGETLB"}, //
{MAP_CONCEAL, "CONCEAL"}, //
{MAP_LOCKED, "LOCKED"}, //
{MAP_NORESERVE, "NORESERVE"}, //
{MAP_NONBLOCK, "NONBLOCK"}, //
{MAP_POPULATE, "POPULATE"}, //
{IsLinux() ? MAP_GROWSDOWN_LINUX : 0, "GROWSDOWN"}, //
};
return _DescribeFlags(buf, 64, kMapFlags, ARRAYLEN(kMapFlags), "MAP_", x);
}
2 changes: 2 additions & 0 deletions libc/intrin/describeprotflags.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
#include "libc/sysv/consts/prot.h"

const char *_DescribeProtFlags(char buf[48], int x) {
if (!x)
return "PROT_NONE";
const struct DescribeFlags kProtFlags[] = {
{PROT_READ, "READ"}, //
{PROT_WRITE, "WRITE"}, //
Expand Down
16 changes: 5 additions & 11 deletions libc/intrin/getmainstack.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,13 @@
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/calls/struct/rlimit.h"
#include "libc/calls/struct/rlimit.internal.h"
#include "libc/dce.h"
#include "libc/intrin/getauxval.h"
#include "libc/intrin/kprintf.h"
#include "libc/intrin/maps.h"
#include "libc/intrin/rlimit.h"
#include "libc/macros.h"
#include "libc/runtime/runtime.h"
#include "libc/stdio/sysparam.h"
#include "libc/sysv/consts/auxv.h"
#include "libc/sysv/consts/rlim.h"
#include "libc/sysv/consts/rlimit.h"

// Hack for guessing boundaries of _start()'s stack
//
Expand Down Expand Up @@ -91,12 +88,9 @@ static uintptr_t __get_main_top(int pagesz) {
}

static size_t __get_stack_size(int pagesz, uintptr_t start, uintptr_t top) {
size_t size, max = 8 * 1024 * 1024;
struct rlimit rlim = {RLIM_INFINITY};
sys_getrlimit(RLIMIT_STACK, &rlim);
if ((size = rlim.rlim_cur) > max)
size = max;
return MAX(ROUNDUP(size, pagesz), ROUNDUP(top - start, pagesz));
size_t stacksz = __rlimit_stack_get().rlim_cur;
stacksz = MIN(stacksz, 1024ul * 1024 * 1024 * 1024);
return MAX(ROUNDDOWN(stacksz, pagesz), ROUNDUP(top - start, pagesz));
}

/**
Expand Down
File renamed without changes.
50 changes: 50 additions & 0 deletions libc/intrin/lockless.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#ifndef COSMOPOLITAN_LIBC_INTRIN_LOCKLESS_H_
#define COSMOPOLITAN_LIBC_INTRIN_LOCKLESS_H_
#include "libc/atomic.h"
#include "libc/intrin/atomic.h"
COSMOPOLITAN_C_START_

// lockless memory transactions
//
// - one writer
// - many readers
// - generation is monotonic
// - even numbers mean memory is ready
// - odd numbers mean memory is actively being changed
// - always use acquire semantics inside your read transaction
//
// let's say you want to be able to atomically read and write to 128-bit
// values, but you've only got a 64-bit system. if you expect that it'll
// frequently written, then you should use a mutex. but if you expect it
// to be frequently read and rarely written, then it's possible to do it
// without a mutex; in fact you don't even need the x86 lock instruction
// prefix; all that is required is a series of carefully ordered mov ops
// which are designed to exploit the strong ordering of the architecture

static inline unsigned lockless_write_begin(atomic_uint* genptr) {
unsigned gen = atomic_load_explicit(genptr, memory_order_acquire);
atomic_store_explicit(genptr, gen + 1, memory_order_release);
return gen;
}

static inline void lockless_write_end(atomic_uint* genptr, unsigned gen) {
atomic_store_explicit(genptr, gen + 2, memory_order_release);
}

static inline unsigned lockless_read_begin(atomic_uint* genptr) {
return atomic_load_explicit(genptr, memory_order_acquire);
}

static inline bool lockless_read_end(atomic_uint* genptr, unsigned* want) {
unsigned gen1 = *want;
unsigned gen2 = atomic_load_explicit(genptr, memory_order_acquire);
unsigned is_being_actively_changed = gen1 & 1;
unsigned we_lost_race_with_writers = gen1 ^ gen2;
if (!(is_being_actively_changed | we_lost_race_with_writers))
return true;
*want = gen2;
return false;
}

COSMOPOLITAN_C_END_
#endif /* COSMOPOLITAN_LIBC_INTRIN_LOCKLESS_H_ */
10 changes: 9 additions & 1 deletion libc/intrin/maps.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ void *__maps_randaddr(void);
void __maps_add(struct Map *);
void __maps_free(struct Map *);
void __maps_insert(struct Map *);
bool __maps_track(char *, size_t);
int __maps_untrack(char *, size_t);
bool __maps_track(char *, size_t, int, int);
struct Map *__maps_alloc(void);
struct Map *__maps_floor(const char *);
void __maps_stack(char *, int, int, size_t, int, intptr_t);
Expand All @@ -78,6 +79,13 @@ static inline struct Map *__maps_next(struct Map *map) {
return 0;
}

static inline struct Map *__maps_prev(struct Map *map) {
struct Tree *node;
if ((node = tree_prev(&map->tree)))
return MAP_TREE_CONTAINER(node);
return 0;
}

static inline struct Map *__maps_first(void) {
struct Tree *node;
if ((node = tree_first(__maps.maps)))
Expand Down
Loading

0 comments on commit 327863d

Please sign in to comment.