Reduce stack virtual memory consumption on Linux

This commit is contained in:
Justine Tunney 2024-12-25 19:43:43 -08:00
parent cc8a9eb93c
commit 36e5861b0c
No known key found for this signature in database
GPG key ID: BE714B4575D6E328
31 changed files with 583 additions and 166 deletions

View file

@ -16,64 +16,42 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/limits.h"
#include "libc/runtime/stack.h"
#include "libc/thread/thread.h"
/**
* Configures custom allocated stack for thread, e.g.
* Configures custom stack for thread.
*
* pthread_t id;
* pthread_attr_t attr;
* char *stk = NewCosmoStack();
* pthread_attr_init(&attr);
* pthread_attr_setstack(&attr, stk, GetStackSize());
* pthread_create(&id, &attr, func, 0);
* pthread_attr_destroy(&attr);
* pthread_join(id, 0);
* FreeCosmoStack(stk);
* Normally you want to use pthread_attr_setstacksize() and
* pthread_attr_setguardsize() to configure how pthread_create()
* allocates stack memory for newly created threads. Cosmopolitan is
* very good at managing stack memory. However if you still want to
* allocate stack memory on your own, POSIX defines this function.
*
* Your stack must have at least `PTHREAD_STACK_MIN` bytes, which
* Cosmpolitan Libc defines as `GetStackSize()`. It's a link-time
* constant used by Actually Portable Executable that's 128 kb by
* default. See libc/runtime/stack.h for docs on your stack limit
* since the APE ELF phdrs are the one true source of truth here.
* Your `stackaddr` points to the byte at the very bottom of your stack.
* You are responsible for this memory. Your POSIX threads runtime will
* not free or unmap this allocation when the thread has terminated. If
* `stackaddr` is null then `stacksize` is ignored and default behavior
* is restored, i.e. pthread_create() will manage stack allocations.
*
* Cosmpolitan Libc runtime magic (e.g. ftrace) and memory safety
* (e.g. kprintf) assumes that stack sizes are two-powers and are
* aligned to that two-power. Conformance isn't required since we
* say caveat emptor to those who don't maintain these invariants
* please consider using NewCosmoStack(), which is always perfect
* or use `mmap(0, GetStackSize() << 1, ...)` for a bigger stack.
* Your `stackaddr` could be created by malloc(). On OpenBSD,
* pthread_create() will augment your custom allocation so it's
* permissable by the kernel to use as a stack. You may also call
* Cosmopolitan APIs such NewCosmoStack() and cosmo_stack_alloc().
* Static memory can be used, but it won't reduce pthread footprint.
*
* Unlike pthread_attr_setstacksize(), this function permits just
* about any parameters and will change the values and allocation
* as needed to conform to the mandatory requirements of the host
* operating system even if it doesn't meet the stricter needs of
* Cosmopolitan Libc userspace libraries. For example with malloc
* allocations, things like page size alignment, shall be handled
* automatically for compatibility with existing codebases.
*
* The same stack shouldn't be used for two separate threads. Use
* fresh stacks for each thread so that ASAN can be much happier.
*
* @param stackaddr is address of stack allocated by caller, and
* may be NULL in which case default behavior is restored
* @param stacksize is size of caller allocated stack
* @return 0 on success, or errno on error
* @raise EINVAL if parameters were unacceptable
* @raise EINVAL if `stacksize` is less than `PTHREAD_STACK_MIN`
* @see pthread_attr_setstacksize()
*/
errno_t pthread_attr_setstack(pthread_attr_t *attr, void *stackaddr,
size_t stacksize) {
if (!stackaddr) {
attr->__stackaddr = 0;
attr->__stacksize = 0;
attr->__stacksize = GetStackSize();
return 0;
}
if (stacksize > INT_MAX)
return EINVAL;
if (stacksize < PTHREAD_STACK_MIN)
return EINVAL;
attr->__stackaddr = stackaddr;