Implement swapcontext() and makecontext()

This change introduces support for Linux-style uc_context manipulation
that's fast and works well on all supported OSes and architectures. It
also integrates with the Cosmpolitan runtime which can show backtraces
comprised of multiple stacks and fibers. See the test and example code
for further details. This will be used by Mold once it's been vendored
This commit is contained in:
Justine Tunney 2023-07-02 03:50:29 -07:00
parent 7ec84655b4
commit 197aa0d465
No known key found for this signature in database
GPG key ID: BE714B4575D6E328
28 changed files with 617 additions and 355 deletions

79
examples/setcontext.c Normal file
View file

@ -0,0 +1,79 @@
#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/calls/calls.h"
#include "libc/calls/ucontext.h"
#include "libc/runtime/runtime.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/exit.h"
/**
* @fileoverview swapcontext() and makecontext() example
* @note adapted from the Linux man-pages project
*/
static ucontext_t uctx_main;
static ucontext_t uctx_func1;
static ucontext_t uctx_func2;
#define say(s) write(1, s, strlen(s))
#define handle_error(msg) \
do { \
perror(msg); \
exit(EXIT_FAILURE); \
} while (0)
static void func1(void) {
say("func1: started\n");
say("func1: swapcontext(&uctx_func1, &uctx_func2)\n");
if (swapcontext(&uctx_func1, &uctx_func2) == -1) {
handle_error("swapcontext");
}
say("func1: returning\n");
}
static void func2(void) {
say("func2: started\n");
say("func2: swapcontext(&uctx_func2, &uctx_func1)\n");
if (swapcontext(&uctx_func2, &uctx_func1) == -1) {
handle_error("swapcontext");
}
say("func2: returning\n");
}
int main(int argc, char *argv[]) {
char func1_stack[8192];
char func2_stack[8192];
if (getcontext(&uctx_func1) == -1) {
handle_error("getcontext");
}
uctx_func1.uc_stack.ss_sp = func1_stack;
uctx_func1.uc_stack.ss_size = sizeof(func1_stack);
uctx_func1.uc_link = &uctx_main;
makecontext(&uctx_func1, func1, 0);
if (getcontext(&uctx_func2) == -1) {
handle_error("getcontext");
}
uctx_func2.uc_stack.ss_sp = func2_stack;
uctx_func2.uc_stack.ss_size = sizeof(func2_stack);
/* Successor context is f1(), unless argc > 1 */
uctx_func2.uc_link = (argc > 1) ? NULL : &uctx_func1;
makecontext(&uctx_func2, func2, 0);
say("main: swapcontext(&uctx_main, &uctx_func2)\n");
if (swapcontext(&uctx_main, &uctx_func2) == -1) {
handle_error("swapcontext");
}
say("main: exiting\n");
exit(EXIT_SUCCESS);
}

View file

@ -214,7 +214,9 @@ o/$(MODE)/libc/calls/pledge-linux.o: private \
# these assembly files are safe to build on aarch64
o/$(MODE)/libc/calls/getcontext.o: libc/calls/getcontext.S
@$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -c $<
o/$(MODE)/libc/calls/setcontext2.o: libc/calls/setcontext2.S
o/$(MODE)/libc/calls/swapcontext.o: libc/calls/swapcontext.S
@$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -c $<
o/$(MODE)/libc/calls/tailcontext.o: libc/calls/tailcontext.S
@$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -c $<
LIBC_CALLS_LIBS = $(foreach x,$(LIBC_CALLS_ARTIFACTS),$($(x)))

View file

@ -20,132 +20,14 @@
// Gets machine state.
//
// @return 0 on success, or -1 w/ errno
// @see makecontext()
// @see swapcontext()
// @see setcontext()
// @threadsafe
.ftrace1
getcontext:
#ifdef __x86_64__
pushf
pop 176(%rdi)
movaps %xmm0,480(%rdi)
movaps %xmm1,496(%rdi)
movaps %xmm2,512(%rdi)
movaps %xmm3,528(%rdi)
movaps %xmm4,544(%rdi)
movaps %xmm5,560(%rdi)
movaps %xmm6,576(%rdi)
movaps %xmm7,592(%rdi)
movaps %xmm8,608(%rdi)
movaps %xmm9,624(%rdi)
movaps %xmm10,640(%rdi)
movaps %xmm11,656(%rdi)
movaps %xmm12,672(%rdi)
movaps %xmm13,688(%rdi)
movaps %xmm14,704(%rdi)
movaps %xmm15,720(%rdi)
mov %r8,40(%rdi)
mov %r9,48(%rdi)
mov %r10,56(%rdi)
mov %r11,64(%rdi)
mov %r12,72(%rdi)
mov %r13,80(%rdi)
mov %r14,88(%rdi)
mov %r15,96(%rdi)
mov %rdi,104(%rdi)
mov %rsi,112(%rdi)
mov %rbp,120(%rdi)
mov %rbx,128(%rdi)
mov %rdx,136(%rdi)
mov %rax,144(%rdi)
mov %rcx,152(%rdi)
lea 320(%rdi),%rax
mov %rax,224(%rdi)
lea 8(%rsp),%rax
mov %rax,160(%rdi)
mov (%rsp),%rax
mov %rax,168(%rdi)
jmp __getcontext
#elif defined(__aarch64__)
stp x0,x1,[x0,184]
stp x2,x3,[x0,200]
stp x4,x5,[x0,216]
stp x6,x7,[x0,232]
stp x8,x9,[x0,248]
stp x10,x11,[x0,264]
stp x12,x13,[x0,280]
stp x14,x15,[x0,296]
stp x16,x17,[x0,312]
stp x18,x19,[x0,328]
stp x20,x21,[x0,344]
stp x22,x23,[x0,360]
stp x24,x25,[x0,376]
stp x26,x27,[x0,392]
stp x28,x29,[x0,408]
str x30,[x0,424]
mov x1,sp
str x1,[x0,432] // sp = caller's sp
str x30,[x0,440] // pc = caller's pc
b __getcontext
#else
#error "unsupported architecture"
#endif
.ftrace2
#include "libc/calls/getcontext.inc"
jmp __getcontextsig
.endfn getcontext,globl
.end
////////////////////////////////////////////////////////////////////////////////
noasan noubsan dontinstrument int getcontext(ucontext_t *uc) {
asm volatile("movaps\t%%xmm0,%0" : /* no outputs */ : "m"(uc->__fpustate.xmm[0]));
asm volatile("movaps\t%%xmm1,%0" : /* no outputs */ : "m"(uc->__fpustate.xmm[1]));
asm volatile("movaps\t%%xmm2,%0" : /* no outputs */ : "m"(uc->__fpustate.xmm[2]));
asm volatile("movaps\t%%xmm3,%0" : /* no outputs */ : "m"(uc->__fpustate.xmm[3]));
asm volatile("movaps\t%%xmm4,%0" : /* no outputs */ : "m"(uc->__fpustate.xmm[4]));
asm volatile("movaps\t%%xmm5,%0" : /* no outputs */ : "m"(uc->__fpustate.xmm[5]));
asm volatile("movaps\t%%xmm6,%0" : /* no outputs */ : "m"(uc->__fpustate.xmm[6]));
asm volatile("movaps\t%%xmm7,%0" : /* no outputs */ : "m"(uc->__fpustate.xmm[7]));
asm volatile("movaps\t%%xmm8,%0" : /* no outputs */ : "m"(uc->__fpustate.xmm[8]));
asm volatile("movaps\t%%xmm9,%0" : /* no outputs */ : "m"(uc->__fpustate.xmm[9]));
asm volatile("movaps\t%%xmm10,%0" : /* no outputs */ : "m"(uc->__fpustate.xmm[10]));
asm volatile("movaps\t%%xmm11,%0" : /* no outputs */ : "m"(uc->__fpustate.xmm[11]));
asm volatile("movaps\t%%xmm12,%0" : /* no outputs */ : "m"(uc->__fpustate.xmm[12]));
asm volatile("movaps\t%%xmm13,%0" : /* no outputs */ : "m"(uc->__fpustate.xmm[13]));
asm volatile("movaps\t%%xmm14,%0" : /* no outputs */ : "m"(uc->__fpustate.xmm[14]));
asm volatile("movaps\t%%xmm15,%0" : /* no outputs */ : "m"(uc->__fpustate.xmm[15]));
asm volatile("mov\t%%r8,%0" : "=m"(uc->uc_mcontext.r8));
asm volatile("mov\t%%r9,%0" : "=m"(uc->uc_mcontext.r9));
asm volatile("mov\t%%r10,%0" : "=m"(uc->uc_mcontext.r10));
asm volatile("mov\t%%r11,%0" : "=m"(uc->uc_mcontext.r11));
asm volatile("mov\t%%r12,%0" : "=m"(uc->uc_mcontext.r12));
asm volatile("mov\t%%r13,%0" : "=m"(uc->uc_mcontext.r13));
asm volatile("mov\t%%r14,%0" : "=m"(uc->uc_mcontext.r14));
asm volatile("mov\t%%r15,%0" : "=m"(uc->uc_mcontext.r15));
asm volatile("mov\t%%rdi,%0" : "=m"(uc->uc_mcontext.rdi));
asm volatile("mov\t%%rsi,%0" : "=m"(uc->uc_mcontext.rsi));
asm volatile("mov\t%%rbp,%0" : "=m"(uc->uc_mcontext.rbp));
asm volatile("mov\t%%rbx,%0" : "=m"(uc->uc_mcontext.rbx));
asm volatile("mov\t%%rdx,%0" : "=m"(uc->uc_mcontext.rdx));
asm volatile("mov\t%%rax,%0" : "=m"(uc->uc_mcontext.rax));
asm volatile("mov\t%%rcx,%0" : "=m"(uc->uc_mcontext.rcx));
uc->uc_mcontext.fpregs = &uc->__fpustate;
asm volatile("lea\t8(%%rsp),%%rax\n\t"
"mov\t%%rax,%0"
: "=m"(uc->uc_mcontext.rsp)
: /* no inputs */
: "rax");
asm volatile("mov\t(%%rsp),%%rax\n\t"
"mov\t%%rax,%0"
: "=m"(uc->uc_mcontext.rip)
: /* no inputs */
: "rax");
return 0;
}
noasan noubsan dontinstrument int getcontext(ucontext_t *uc) {
asm volatile("stp\tx0,x1,%0" : "=m"(uc->uc_mcontext.regs[0]));
asm volatile("stp\tx2,x3,%0" : "=m"(uc->uc_mcontext.regs[2]));
asm volatile("mov\tx1,sp\n\tstr\tx1,%0" : "=m"(uc->uc_mcontext.sp));
asm volatile("str\tx30,%0" : "=m"(uc->uc_mcontext.pc));
return 0;
}

89
libc/calls/getcontext.inc Normal file
View file

@ -0,0 +1,89 @@
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-
│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│
╞══════════════════════════════════════════════════════════════════════════════╡
Copyright 2022 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.
╚─────────────────────────────────────────────────────────────────────────────*/
// @fileoverview textual include for loading processor state
#ifdef __x86_64__
mov %r8,40(%rdi)
mov %r9,48(%rdi)
mov %r10,56(%rdi)
mov %r11,64(%rdi)
mov %r12,72(%rdi)
mov %r13,80(%rdi)
mov %r14,88(%rdi)
mov %r15,96(%rdi)
mov %rdi,104(%rdi)
mov %rsi,112(%rdi)
mov %rbp,120(%rdi)
mov %rbx,128(%rdi)
mov %rdx,136(%rdi)
mov %rcx,152(%rdi)
lea 8(%rsp),%rax
mov %rax,160(%rdi) // rsp = caller's rsp
mov (%rsp),%rax
mov %rax,168(%rdi) // rip = return address
lea 608(%rdi),%rax
movaps %xmm0,-0x80(%rax)
movaps %xmm1,-0x70(%rax)
movaps %xmm2,-0x60(%rax)
movaps %xmm3,-0x50(%rax)
movaps %xmm4,-0x40(%rax)
movaps %xmm5,-0x30(%rax)
movaps %xmm6,-0x20(%rax)
movaps %xmm7,-0x10(%rax)
movaps %xmm8,0x00(%rax)
movaps %xmm9,0x10(%rax)
movaps %xmm10,0x20(%rax)
movaps %xmm11,0x30(%rax)
movaps %xmm12,0x40(%rax)
movaps %xmm13,0x50(%rax)
movaps %xmm14,0x60(%rax)
movaps %xmm15,0x70(%rax)
lea 320(%rdi),%rax // rax = &__fpustate
mov %rax,224(%rdi) // fpregs = rax
#elif defined(__aarch64__)
#define REGS(i) 184+i*8
stp xzr,x1,[x0,REGS(0)] // x0 = 0
stp x2,x3,[x0,REGS(2)]
stp x4,x5,[x0,REGS(4)]
stp x6,x7,[x0,REGS(6)]
stp x8,x9,[x0,REGS(8)]
stp x10,x11,[x0,REGS(10)]
stp x12,x13,[x0,REGS(12)]
stp x14,x15,[x0,REGS(14)]
stp x16,x17,[x0,REGS(16)]
stp x18,x19,[x0,REGS(18)]
stp x20,x21,[x0,REGS(20)]
stp x22,x23,[x0,REGS(22)]
stp x24,x25,[x0,REGS(24)]
stp x26,x27,[x0,REGS(26)]
stp x28,x29,[x0,REGS(28)]
str x30,[x0,REGS(30)]
mov x15,sp
stp x15,x30,[x0,REGS(31)] // sp, pc = caller sp, ret address
str xzr,[x0,448] // pstate = 0
str xzr,[x0,456] // no vectors yet
#else
#error "unsupported architecture"
#endif

View file

@ -1,136 +0,0 @@
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi
Copyright 2022 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/macros.internal.h"
// tailed called by setcontext() implementation
__setcontext:
#ifdef __x86_64__
mov 224(%rdi),%rax
test %rax,%rax
je 1f
movaps 160(%rax),%xmm0
movaps 176(%rax),%xmm1
movaps 192(%rax),%xmm2
movaps 208(%rax),%xmm3
movaps 224(%rax),%xmm4
movaps 240(%rax),%xmm5
movaps 256(%rax),%xmm6
movaps 272(%rax),%xmm7
movaps 288(%rax),%xmm8
movaps 304(%rax),%xmm9
movaps 320(%rax),%xmm10
movaps 336(%rax),%xmm11
movaps 352(%rax),%xmm12
movaps 368(%rax),%xmm13
movaps 384(%rax),%xmm14
movaps 400(%rax),%xmm15
1: push 176(%rdi)
popf
mov %rsi,%rsp
mov 40(%rdi),%r8
mov 48(%rdi),%r9
mov 56(%rdi),%r10
mov 64(%rdi),%r11
mov 72(%rdi),%r12
mov 80(%rdi),%r13
mov 88(%rdi),%r14
mov 96(%rdi),%r15
mov 112(%rdi),%rsi
mov 120(%rdi),%rbp
mov 128(%rdi),%rbx
mov 136(%rdi),%rdx
mov 144(%rdi),%rax
mov 152(%rdi),%rcx
xor %rax,%rax
push 168(%rdi)
mov 104(%rdi),%rdi
ret
#elif defined(__aarch64__)
mov sp,x1 // sp = second argument
ldr x30,[x0,440] // return address <- pc
ldp x28,x29,[x0,408] // x0 and x30 discarded
ldp x26,x27,[x0,392]
ldp x24,x25,[x0,376]
ldp x22,x23,[x0,360]
ldp x20,x21,[x0,344]
ldp x18,x19,[x0,328]
ldp x16,x17,[x0,312]
ldp x14,x15,[x0,296]
ldp x12,x13,[x0,280]
ldp x10,x11,[x0,264]
ldp x8,x9,[x0,248]
ldp x6,x7,[x0,232]
ldp x4,x5,[x0,216]
ldp x2,x3,[x0,200]
ldr x1,[x0,192]
mov w0,0
ret
#else
#error "unsupported architecture"
#endif
.endfn __setcontext,globl
.end
////////////////////////////////////////////////////////////////////////////////
noasan noubsan int __setcontext(const ucontext_t *uc) {
if (uc->uc_mcontext.fpregs) {
asm volatile("movaps\t%0,%%xmm0" : /* no outputs */ : "m"(uc->uc_mcontext.fpregs->xmm[0]));
asm volatile("movaps\t%0,%%xmm1" : /* no outputs */ : "m"(uc->uc_mcontext.fpregs->xmm[1]));
asm volatile("movaps\t%0,%%xmm2" : /* no outputs */ : "m"(uc->uc_mcontext.fpregs->xmm[2]));
asm volatile("movaps\t%0,%%xmm3" : /* no outputs */ : "m"(uc->uc_mcontext.fpregs->xmm[3]));
asm volatile("movaps\t%0,%%xmm4" : /* no outputs */ : "m"(uc->uc_mcontext.fpregs->xmm[4]));
asm volatile("movaps\t%0,%%xmm5" : /* no outputs */ : "m"(uc->uc_mcontext.fpregs->xmm[5]));
asm volatile("movaps\t%0,%%xmm6" : /* no outputs */ : "m"(uc->uc_mcontext.fpregs->xmm[6]));
asm volatile("movaps\t%0,%%xmm7" : /* no outputs */ : "m"(uc->uc_mcontext.fpregs->xmm[7]));
asm volatile("movaps\t%0,%%xmm8" : /* no outputs */ : "m"(uc->uc_mcontext.fpregs->xmm[8]));
asm volatile("movaps\t%0,%%xmm9" : /* no outputs */ : "m"(uc->uc_mcontext.fpregs->xmm[9]));
asm volatile("movaps\t%0,%%xmm10" : /* no outputs */ : "m"(uc->uc_mcontext.fpregs->xmm[10]));
asm volatile("movaps\t%0,%%xmm11" : /* no outputs */ : "m"(uc->uc_mcontext.fpregs->xmm[11]));
asm volatile("movaps\t%0,%%xmm12" : /* no outputs */ : "m"(uc->uc_mcontext.fpregs->xmm[12]));
asm volatile("movaps\t%0,%%xmm13" : /* no outputs */ : "m"(uc->uc_mcontext.fpregs->xmm[13]));
asm volatile("movaps\t%0,%%xmm14" : /* no outputs */ : "m"(uc->uc_mcontext.fpregs->xmm[14]));
asm volatile("movaps\t%0,%%xmm15" : /* no outputs */ : "m"(uc->uc_mcontext.fpregs->xmm[15]));
}
asm volatile("mov\t%0,%%r8" : /* no outputs */ : "m"(uc->uc_mcontext.r8));
asm volatile("mov\t%0,%%r9" : /* no outputs */ : "m"(uc->uc_mcontext.r9));
asm volatile("mov\t%0,%%r10" : /* no outputs */ : "m"(uc->uc_mcontext.r10));
asm volatile("mov\t%0,%%r11" : /* no outputs */ : "m"(uc->uc_mcontext.r11));
asm volatile("mov\t%0,%%r12" : /* no outputs */ : "m"(uc->uc_mcontext.r12));
asm volatile("mov\t%0,%%r13" : /* no outputs */ : "m"(uc->uc_mcontext.r13));
asm volatile("mov\t%0,%%r14" : /* no outputs */ : "m"(uc->uc_mcontext.r14));
asm volatile("mov\t%0,%%r15" : /* no outputs */ : "m"(uc->uc_mcontext.r15));
asm volatile("mov\t%0,%%rsi" : /* no outputs */ : "m"(uc->uc_mcontext.rsi));
asm volatile("mov\t%0,%%rbp" : /* no outputs */ : "m"(uc->uc_mcontext.rbp));
asm volatile("mov\t%0,%%rbx" : /* no outputs */ : "m"(uc->uc_mcontext.rbx));
asm volatile("mov\t%0,%%rdx" : /* no outputs */ : "m"(uc->uc_mcontext.rdx));
asm volatile("mov\t%0,%%rax" : /* no outputs */ : "m"(uc->uc_mcontext.rax));
asm volatile("mov\t%0,%%rcx" : /* no outputs */ : "m"(uc->uc_mcontext.rcx));
asm volatile("mov\t%0,%%rsp" : /* no outputs */ : "m"(uc->uc_mcontext.rsp));
asm volatile("xor\t%%rax,%%rax\n\t"
"push\t%0\n\t"
"mov\t%1,%%rdi\n\t"
"ret"
: /* no outputs */
: "m"(uc->uc_mcontext.rip), "m"(uc->uc_mcontext.rdi));
__builtin_unreachable();
}

View file

@ -6,9 +6,25 @@
COSMOPOLITAN_C_START_
#ifdef __x86_64__
#define SP rsp
#define PC rip
#define SP rsp
#define BP rbp
#define ARG0 rdi
#define ARG1 rsi
#define ARG2 rdx
#define ARG3 rcx
#define ARG4 r8
#define ARG5 r9
#elif defined(__aarch64__)
#define SP sp
#define PC pc
#define SP sp
#define BP regs[29]
#define ARG0 regs[0]
#define ARG1 regs[1]
#define ARG2 regs[2]
#define ARG3 regs[3]
#define ARG4 regs[4]
#define ARG5 regs[5]
#else
#error "unsupported architecture"
#endif

View file

@ -1,7 +1,7 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi
Copyright 2023 Justine Alexandra Roberts Tunney
Copyright 2022 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
@ -16,19 +16,45 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/struct/sigset.h"
#include "libc/calls/struct/ucontext.internal.h"
#include "libc/calls/ucontext.h"
#include "libc/runtime/stack.h"
#include "libc/sysv/consts/sig.h"
#include "libc/sysv/consts/ss.h"
#include "libc/macros.internal.h"
// this is tail called by the getcontext() implementation
int __getcontext(ucontext_t *uc) {
uc->uc_flags = 0;
uc->uc_link = 0;
uc->uc_stack.ss_sp = (void *)uc->uc_mcontext.SP;
uc->uc_stack.ss_flags = SS_ONSTACK;
uc->uc_stack.ss_size = uc->uc_mcontext.SP - GetStackAddr();
return sigprocmask(SIG_SETMASK, 0, &uc->uc_sigmask);
}
// Saves machine to 𝑥 and activates 𝑦, i.e.
//
// getcontext(x);
// setcontext(y);
//
// Except using this API is safer and goes 2x faster:
//
// swapcontext(x, y);
//
// @return 0 on success, or -1 w/ errno
// @returnstwice
// @threadsafe
.ftrace1
swapcontext:
.ftrace2
#include "libc/calls/getcontext.inc"
#ifdef __x86_64__
push %rbp
mov %rsp,%rbp
push %rsi
push %rsi
call __swapcontextsig
pop %rdi
pop %rdi
pop %rbp
test %eax,%eax
jnz 1f
#elif defined(__aarch64__)
stp x29,x30,[sp,#-32]!
mov x29,sp
str x1,[sp,16]
bl __swapcontextsig
ldr x1,[sp,16]
ldp x29,x30,[sp],#32
cbnz w0,1f
mov x0,x1
#endif
jmp __tailcontext
1: ret
.endfn swapcontext,globl

89
libc/calls/tailcontext.S Normal file
View file

@ -0,0 +1,89 @@
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi
Copyright 2022 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/macros.internal.h"
// tailed called by setcontext() implementation
__tailcontext:
#ifdef __x86_64__
lea 608(%rdi),%rax
movaps -0x80(%rax),%xmm0
movaps -0x70(%rax),%xmm1
movaps -0x60(%rax),%xmm2
movaps -0x50(%rax),%xmm3
movaps -0x40(%rax),%xmm4
movaps -0x30(%rax),%xmm5
movaps -0x20(%rax),%xmm6
movaps -0x10(%rax),%xmm7
movaps 0x00(%rax),%xmm8
movaps 0x10(%rax),%xmm9
movaps 0x20(%rax),%xmm10
movaps 0x30(%rax),%xmm11
movaps 0x40(%rax),%xmm12
movaps 0x50(%rax),%xmm13
movaps 0x60(%rax),%xmm14
movaps 0x70(%rax),%xmm15
mov 40(%rdi),%r8
mov 48(%rdi),%r9
mov 56(%rdi),%r10
mov 64(%rdi),%r11
mov 72(%rdi),%r12
mov 80(%rdi),%r13
mov 88(%rdi),%r14
mov 96(%rdi),%r15
mov 112(%rdi),%rsi
mov 120(%rdi),%rbp
mov 128(%rdi),%rbx
mov 136(%rdi),%rdx
mov 152(%rdi),%rcx
mov 160(%rdi),%rsp
push 168(%rdi)
mov 104(%rdi),%rdi
xor %eax,%eax
ret
#elif defined(__aarch64__)
#define REGS(i) 184+i*8
ldp x1,x16,[x0,REGS(31)] // sp, pc
mov sp,x1
ldr x30,[x0,REGS(30)]
ldp x28,x29,[x0,REGS(28)]
ldp x26,x27,[x0,REGS(26)]
ldp x24,x25,[x0,REGS(24)]
ldp x22,x23,[x0,REGS(22)]
ldp x20,x21,[x0,REGS(20)]
ldp x18,x19,[x0,REGS(18)]
ldr x17,[x0,REGS(17)]
ldp x14,x15,[x0,REGS(14)]
ldp x12,x13,[x0,REGS(12)]
ldp x10,x11,[x0,REGS(10)]
ldp x8,x9,[x0,REGS(8)]
ldp x6,x7,[x0,REGS(6)]
ldp x4,x5,[x0,REGS(4)]
ldp x2,x3,[x0,REGS(2)]
ldp x0,x1,[x0,REGS(0)]
br x16
#else
#error "unsupported architecture"
#endif
.endfn __tailcontext,globl

View file

@ -16,26 +16,30 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/struct/sigset.h"
#include "libc/calls/struct/ucontext.internal.h"
#include "libc/calls/ucontext.h"
#include "libc/calls/struct/sigset.h"
#include "libc/sysv/consts/sig.h"
#include "libc/sysv/consts/ss.h"
int __setcontext(const ucontext_t *, uintptr_t);
int __tailcontext(const ucontext_t *);
/**
* Sets machine context.
*
* @return -1 on error w/ errno, otherwise won't return unless sent back
* @see swapcontext()
* @see makecontext()
* @see getcontext()
* @threadsafe
*/
int setcontext(const ucontext_t *uc) {
uintptr_t sp;
if (sigprocmask(SIG_SETMASK, &uc->uc_sigmask, 0)) return -1;
if (uc->uc_stack.ss_flags & (SS_DISABLE | SS_ONSTACK)) {
sp = uc->uc_mcontext.SP;
} else {
sp = (uintptr_t)uc->uc_stack.ss_sp & -16;
}
return __setcontext(uc, sp);
return __tailcontext(uc);
}
int __getcontextsig(ucontext_t *uc) {
return sigprocmask(SIG_SETMASK, 0, &uc->uc_sigmask);
}
int __swapcontextsig(ucontext_t *x, const ucontext_t *y) {
return sigprocmask(SIG_SETMASK, &y->uc_sigmask, &x->uc_sigmask);
}

View file

@ -41,24 +41,24 @@ struct sigcontext {
#ifdef __x86_64__
union {
struct {
uint64_t r8;
uint64_t r9;
uint64_t r10;
uint64_t r11;
uint64_t r12;
uint64_t r13;
uint64_t r14;
uint64_t r15;
uint64_t rdi;
uint64_t rsi;
uint64_t rbp;
uint64_t rbx;
uint64_t rdx;
uint64_t rax;
uint64_t rcx;
uint64_t rsp;
uint64_t rip;
uint64_t eflags;
uint64_t r8; /* 40 */
uint64_t r9; /* 48 */
uint64_t r10; /* 56 */
uint64_t r11; /* 64 */
uint64_t r12; /* 72 */
uint64_t r13; /* 80 */
uint64_t r14; /* 88 */
uint64_t r15; /* 96 */
uint64_t rdi; /* 104 */
uint64_t rsi; /* 112 */
uint64_t rbp; /* 120 */
uint64_t rbx; /* 128 */
uint64_t rdx; /* 136 */
uint64_t rax; /* 144 */
uint64_t rcx; /* 152 */
uint64_t rsp; /* 160 */
uint64_t rip; /* 168 */
uint64_t eflags; /* 176 */
uint16_t cs;
uint16_t gs;
uint16_t fs;
@ -102,8 +102,10 @@ struct ucontext {
typedef struct ucontext ucontext_t;
int getcontext(ucontext_t *);
int setcontext(const ucontext_t *);
int getcontext(ucontext_t *) dontthrow;
int setcontext(const ucontext_t *) dontthrow;
int swapcontext(ucontext_t *, const ucontext_t *) dontthrow returnstwice;
void makecontext(ucontext_t *, void (*)(), int, ...) dontthrow nocallback;
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */

View file

@ -21,10 +21,10 @@
#include "libc/calls/struct/ucontext.internal.h"
#include "libc/calls/ucontext.h"
#include "libc/intrin/describebacktrace.internal.h"
#include "libc/intrin/kprintf.h"
#include "libc/intrin/strace.internal.h"
#include "libc/nt/enum/exceptionhandleractions.h"
#include "libc/nt/enum/signal.h"
#include "libc/nt/enum/status.h"
#include "libc/nt/runtime.h"
#include "libc/nt/struct/ntexceptionpointers.h"
#include "libc/str/str.h"
@ -90,6 +90,10 @@ unsigned __wincrash(struct NtExceptionPointers *ep) {
code = SI_USER;
sig = SIGABRT;
break;
case kNtStatusIntegerOverflow:
code = FPE_INTOVF;
sig = SIGFPE;
break;
case kNtSignalFltDivideByZero:
code = FPE_FLTDIV;
sig = SIGFPE;

View file

@ -1,8 +0,0 @@
#ifndef COSMOPOLITAN_LIBC_CALLS_WINERR_INTERNAL_H_
#define COSMOPOLITAN_LIBC_CALLS_WINERR_INTERNAL_H_
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_CALLS_WINERR_INTERNAL_H_ */

View file

@ -45,7 +45,6 @@
#include "libc/str/str.h"
#include "libc/thread/thread.h"
#include "libc/thread/tls.h"
#include "libc/str/str.h"
#include "third_party/libcxx/math.h"
#ifdef __x86_64__
@ -230,8 +229,8 @@ relegated void ShowCrashReport(int err, int sig, struct siginfo *si,
ctx->uc_mcontext.rsp <= GetStaticStackAddr(0) + APE_GUARDSIZE))
? "Stack Overflow"
: GetSiCodeName(sig, si->si_code),
host, getpid(), gettid(), program_invocation_name, strerror(err), names.sysname,
names.version, names.nodename, names.release);
host, getpid(), gettid(), program_invocation_name, strerror(err),
names.sysname, names.version, names.nodename, names.release);
if (ctx) {
p = ShowGeneralRegisters(p, ctx);
p = ShowSseRegisters(p, ctx);
@ -314,6 +313,7 @@ relegated void __oncrash_amd64(int sig, struct siginfo *si, void *arg) {
DebugBreak();
} else if (__nocolor || g_isrunningundermake) {
gdbpid = -1;
#if 0
} else if (!IsTiny() && IsLinux() && FindDebugBinary() && !__isworker) {
// RestoreDefaultCrashSignalHandlers();
gdbpid = AttachDebugger(
@ -321,6 +321,7 @@ relegated void __oncrash_amd64(int sig, struct siginfo *si, void *arg) {
(rip >= (intptr_t)&__executable_start && rip < (intptr_t)&_etext))
? rip
: 0);
#endif
}
if (!(gdbpid > 0 && (sig == SIGTRAP || sig == SIGQUIT))) {
__restore_tty();

View file

@ -16,21 +16,19 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/errno.h"
#include "libc/log/log.h"
#include "libc/fmt/magnumstrs.internal.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
/**
* Writes error messages to standard error.
*/
void perror(const char *message) {
int err;
err = errno;
if (message && *message) {
fputs(message, stderr);
fputs(": ", stderr);
}
fputs(strerror(err), stderr);
fputc('\n', stderr);
const char *estr;
estr = _strerdoc(errno);
if (!message) message = "";
if (!estr) estr = "Unknown error";
tinyprint(2, message, *message ? ": " : "", estr, "\n", NULL);
}

View file

@ -26,7 +26,6 @@ char *strndup(const char *, size_t) paramsnonnull() mallocesque;
void *aligned_alloc(size_t, size_t) attributeallocalign((1))
attributeallocsize((2)) returnspointerwithnoaliases libcesque dontdiscard;
int posix_memalign(void **, size_t, size_t);
bool __grow(void *, size_t *, size_t, size_t) paramsnonnull((1, 2)) libcesque;
int mallopt(int, int);
int malloc_trim(size_t);
@ -59,6 +58,10 @@ size_t malloc_set_footprint_limit(size_t);
void malloc_inspect_all(void (*handler)(void *, void *, size_t, void *),
void *);
#ifdef COSMO
bool __grow(void *, size_t *, size_t, size_t) paramsnonnull((1, 2)) libcesque;
#endif
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_MEM_MEM_H_ */

View file

@ -533,7 +533,7 @@ static int CloneLinux(int (*func)(void *arg, int rc), char *stk, size_t stksz,
* int worker(void *arg) { return 0; }
* struct CosmoTib tib = {.tib_self = &tib, .tib_tid = -1};
* atomic_int tid;
* char *stk = _mapstack();
* char *stk = NewCosmoStack();
* clone(worker, stk, GetStackSize() - 16,
* CLONE_VM | CLONE_THREAD | CLONE_FS | CLONE_FILES |
* CLONE_SYSVSEM | CLONE_SIGHAND | CLONE_PARENT_SETTID |
@ -545,7 +545,7 @@ static int CloneLinux(int (*func)(void *arg, int rc), char *stk, size_t stksz,
* // thread is running
* while (atomic_load(&tib.tib_tid) > 0) sched_yield();
* // thread has terminated
* _freestack(stk);
* FreeCosmoStack(stk);
*
* Threads are created in a detached manner. They currently can't be
* synchronized using wait() or posix signals. Threads created by this

View file

@ -32,10 +32,6 @@
#define GUARANTEE_TERMINATOR 1
#define INITIAL_CAPACITY (32 - GUARANTEE_TERMINATOR)
/**
* Grows array.
* @deprecated favor realloc
*/
bool __grow(void *pp, size_t *capacity, size_t itemsize, size_t extra) {
void **p, *p1, *p2;
size_t n1, n2;

View file

@ -39,7 +39,7 @@
*
* @return stack bottom address on success, or null w/ errno
*/
void *_mapstack(void) {
void *NewCosmoStack(void) {
char *p;
if ((p = mmap(0, GetStackSize(), PROT_READ | PROT_WRITE,
MAP_STACK | MAP_ANONYMOUS, -1, 0)) != MAP_FAILED) {
@ -56,8 +56,8 @@ void *_mapstack(void) {
/**
* Frees stack.
*
* @param stk was allocated by _mapstack()
* @param stk was allocated by NewCosmoStack()
*/
int _freestack(void *stk) {
int FreeCosmoStack(void *stk) {
return munmap(stk, GetStackSize());
}

View file

@ -120,8 +120,6 @@ long _missingno();
void _weakfree(void *);
void *_mapanon(size_t) attributeallocsize((1)) mallocesque;
void *_mapshared(size_t) attributeallocsize((1)) mallocesque;
void *_mapstack(void) returnsaligned((APE_PAGESIZE)) mallocesque;
int _freestack(void *);
void __oom_hook(size_t);
void _peekall(void);
bool _isheap(void *);

View file

@ -133,6 +133,9 @@ forceinline void CheckLargeStackAllocation(void *p, ssize_t n) {
}
}
void *NewCosmoStack(void) vallocesque;
int FreeCosmoStack(void *) libcesque;
#endif /* COSMO */
COSMOPOLITAN_C_END_
#endif /* GNU ELF */

129
libc/thread/makecontext.c Normal file
View file

@ -0,0 +1,129 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2023 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/assert.h"
#include "libc/calls/struct/ucontext.internal.h"
#include "libc/calls/ucontext.h"
#include "libc/nexgen32e/nexgen32e.h"
#include "libc/nexgen32e/stackframe.h"
#include "libc/runtime/runtime.h"
#include "libc/stdalign.internal.h"
#include "libc/str/str.h"
#include "libc/thread/thread.h"
struct Gadget {
void (*func)();
int args[6];
};
static void runcontext(struct Gadget *call, ucontext_t *link) {
call->func(call->args[0], //
call->args[1], //
call->args[2], //
call->args[3], //
call->args[4], //
call->args[5]);
if (link) {
setcontext(link);
abort();
} else {
pthread_exit(0);
}
}
/**
* Creates coroutine gadget, e.g.
*
* ucontext_t uc;
* getcontext(&uc);
* uc.uc_link = 0;
* uc.uc_stack.ss_sp = NewCosmoStack();
* uc.uc_stack.ss_size = GetStackSize();
* makecontext(&uc, exit, 1, 42);
* setcontext(&uc);
*
* Is equivalent to:
*
* exit(42);
*
* @param uc stores processor state; the caller must:
* 1. initialize it using getcontext()
* 2. assign new value to `uc->uc_stack.ss_sp`
* 3. use `uc->uc_link` to define successor context
* @param func is the function to call when `uc` is activated;
* when `func` returns control passes to the linked context
* which if null will result in pthread_exit() being called
* @param argc is number of `int` arguments for `func` (max 6)
* @threadsafe
*/
void makecontext(ucontext_t *uc, void func(), int argc, ...) {
int i;
va_list va;
uintptr_t sp;
struct Gadget *call;
struct StackFrame *sf;
assert(argc <= 6u);
// allocate call
sp = (uintptr_t)uc->uc_stack.ss_sp;
sp += uc->uc_stack.ss_size;
sp -= sizeof(*call);
sp &= -alignof(*call);
call = (struct Gadget *)sp;
// get arguments
va_start(va, argc);
call->func = func;
for (i = 0; i < argc; ++i) {
call->args[i] = va_arg(va, int);
}
va_end(va);
// construct fake function call on new stack
//
// the location where getcontext() was called shall be the previous
// entry in the backtrace when runcontext was called, e.g.
//
// 1000800bf160 423024 systemfive_linux+31
// 1000800fff90 405299 abort+58
// 1000800fffb0 40d98c runcontext+42
// 1000800fffd0 40b308 makecontext_backtrace+20
// 7fff22a7ff50 40c2d5 testlib_runtestcases+218
// 7fff22a7ff90 40b1c3 testlib_runalltests+79
// 7fff22a7ffa0 4038cb main+475
// 7fff22a7ffe0 403cfb cosmo+69
// 7fff22a7fff0 4034e2 _start+137
//
// is about what it should look like.
sp &= -(sizeof(uintptr_t) * 2);
#ifdef __x86__
*(uintptr_t *)(sp -= sizeof(uintptr_t)) = uc->uc_mcontext.PC;
#elif defined(__aarch64__)
*(uintptr_t *)(sp -= sizeof(uintptr_t)) = uc->uc_mcontext.regs[30];
*(uintptr_t *)(sp -= sizeof(uintptr_t)) = uc->uc_mcontext.regs[29];
uc->uc_mcontext.BP = uc->uc_mcontext.SP;
#else
#error "unsupported architecture"
#endif
// program context
uc->uc_mcontext.SP = sp;
uc->uc_mcontext.PC = (uintptr_t)runcontext;
uc->uc_mcontext.ARG0 = (uintptr_t)call;
uc->uc_mcontext.ARG1 = (uintptr_t)uc->uc_link;
}

View file

@ -26,13 +26,13 @@
*
* pthread_t id;
* pthread_attr_t attr;
* char *stk = _mapstack();
* 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);
* _freestack(stk);
* FreeCosmoStack(stk);
*
* Your stack must have at least `PTHREAD_STACK_MIN` bytes, which
* Cosmpolitan Libc defines as `GetStackSize()`. It's a link-time
@ -44,7 +44,7 @@
* (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 _mapstack() which always does it perfect
* please consider using NewCosmoStack(), which is always perfect
* or use `mmap(0, GetStackSize() << 1, ...)` for a bigger stack.
*
* Unlike pthread_attr_setstacksize(), this function permits just

View file

@ -117,7 +117,7 @@ int _spawn(int fun(void *, int), void *arg, struct spawn *opt_out_thread) {
// we must use _mapstack() to allocate the stack because OpenBSD has
// very strict requirements for what's allowed to be used for stacks
if (!(th->stk = _mapstack())) {
if (!(th->stk = NewCosmoStack())) {
free(th->tls);
return -1;
}
@ -132,7 +132,7 @@ int _spawn(int fun(void *, int), void *arg, struct spawn *opt_out_thread) {
spawner, &th->ptid, __adj_tls(th->tib), &th->tib->tib_tid);
if (rc) {
errno = rc;
_freestack(th->stk);
FreeCosmoStack(th->stk);
free(th->tls);
return -1;
}

View file

@ -83,3 +83,19 @@ void SetGetContext(void) {
BENCH(getcontext, bench) {
EZBENCH2("get/setcontext", donothing, SetGetContext());
}
BENCH(swapcontext, bench) {
ucontext_t main, loop;
volatile bool ready = false;
getcontext(&main);
if (ready) {
for (;;) {
swapcontext(&main, &loop);
// kprintf("boom\n");
}
} else {
ready = true;
EZBENCH2("x2 swapcontext", donothing, swapcontext(&loop, &main));
// kprintf("dollar\n");
}
}

View file

@ -123,7 +123,7 @@ void TestContendedLock(const char *name, int kind) {
pthread_mutexattr_destroy(&attr);
atomic_store(&ready, 0);
atomic_store(&success, 0);
stk = _mapstack();
stk = NewCosmoStack();
rc = clone(Worker, stk, GetStackSize() - 16 /* openbsd:stackbound */,
CLONE_VM | CLONE_THREAD | CLONE_FS | CLONE_FILES | CLONE_SIGHAND |
CLONE_SYSVSEM | CLONE_PARENT_SETTID | CLONE_CHILD_SETTID |
@ -146,7 +146,7 @@ void TestContendedLock(const char *name, int kind) {
while (tib.tib_tid) donothing;
ASSERT_EQ(1, atomic_load(&success));
ASSERT_EQ(0, atomic_load(&counter));
_freestack(stk);
FreeCosmoStack(stk);
ASSERT_EQ(0, pthread_mutex_destroy(&mu));
ns = time2dbl(timespec_sub(t2, t1)) / n;
kprintf("%s contended took %s\n", name, time2str(ns));

View file

@ -0,0 +1,68 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2023 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/calls.h"
#include "libc/calls/ucontext.h"
#include "libc/limits.h"
#include "libc/mem/gc.internal.h"
#include "libc/runtime/runtime.h"
#include "libc/runtime/stack.h"
#include "libc/runtime/symbols.internal.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/sig.h"
#include "libc/testlib/subprocess.h"
#include "libc/testlib/testlib.h"
#include "libc/thread/thread.h"
#include "libc/x/x.h"
#include "third_party/libcxx/math.h"
ucontext_t uc;
char testlib_enable_tmp_setup_teardown;
void itsatrap(int x, int y) {
*(int *)(intptr_t)x = scalbn(x, y);
}
TEST(makecontext, test) {
SPAWN(fork);
getcontext(&uc);
uc.uc_link = 0;
uc.uc_stack.ss_sp = NewCosmoStack();
uc.uc_stack.ss_size = GetStackSize();
makecontext(&uc, exit, 1, 42);
setcontext(&uc);
EXITS(42);
}
TEST(makecontext, backtrace) {
SPAWN(fork);
ASSERT_SYS(0, 0, close(2));
ASSERT_SYS(0, 2, creat("log", 0644));
getcontext(&uc);
uc.uc_link = 0;
uc.uc_stack.ss_sp = NewCosmoStack();
uc.uc_stack.ss_size = GetStackSize();
makecontext(&uc, itsatrap, 2, 123, 456);
setcontext(&uc);
EXITS(128 + SIGSEGV);
if (!GetSymbolTable()) return;
char *log = gc(xslurp("log", 0));
EXPECT_NE(0, strstr(log, "itsatrap"));
EXPECT_NE(0, strstr(log, "runcontext"));
EXPECT_NE(0, strstr(log, "makecontext_backtrace"));
}

View file

@ -35,6 +35,7 @@ TEST_LIBC_THREAD_DIRECTDEPS = \
LIBC_TESTLIB \
LIBC_THREAD \
LIBC_TIME \
LIBC_X \
THIRD_PARTY_NSYNC \
THIRD_PARTY_NSYNC_MEM

View file

@ -25,7 +25,7 @@
(require 'ld-script)
(require 'make-mode)
(setq cosmo-dbg-mode "dbg")
(setq cosmo-dbg-mode "zero")
(setq cosmo-default-mode "")
(setq c-doc-comment-style 'javadown)