mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-03-03 07:29:23 +00:00
Make _Thread_local more seamlessly working
This commit is contained in:
parent
5f4f6b0e69
commit
5fa77f1e8f
23 changed files with 217 additions and 283 deletions
|
@ -484,6 +484,7 @@ PFSTUB4(ape_elf_shstrndx, 0);
|
||||||
|
|
||||||
HIDDEN(_tdata_size = _tdata_end - _tdata_start);
|
HIDDEN(_tdata_size = _tdata_end - _tdata_start);
|
||||||
HIDDEN(_tls_size = _tbss_end - _tdata_start);
|
HIDDEN(_tls_size = _tbss_end - _tdata_start);
|
||||||
|
HIDDEN(_tls_content = (_tdata_end - _tdata_start) + (_tbss_end - _tbss_start));
|
||||||
|
|
||||||
HIDDEN(__privileged_addr = ROUNDDOWN(__privileged_start, PAGESIZE));
|
HIDDEN(__privileged_addr = ROUNDDOWN(__privileged_start, PAGESIZE));
|
||||||
HIDDEN(__privileged_size = (ROUNDUP(__privileged_end, PAGESIZE) -
|
HIDDEN(__privileged_size = (ROUNDUP(__privileged_end, PAGESIZE) -
|
||||||
|
|
|
@ -115,7 +115,7 @@ int Worker(void *id, int tid) {
|
||||||
struct timeval timeo = {KEEPALIVE / 1000, KEEPALIVE % 1000};
|
struct timeval timeo = {KEEPALIVE / 1000, KEEPALIVE % 1000};
|
||||||
struct sockaddr_in addr = {.sin_family = AF_INET, .sin_port = htons(PORT)};
|
struct sockaddr_in addr = {.sin_family = AF_INET, .sin_port = htons(PORT)};
|
||||||
|
|
||||||
server = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
server = socket(AF_INET, SOCK_STREAM, 0);
|
||||||
if (server == -1) {
|
if (server == -1) {
|
||||||
kprintf("socket() failed %m\n"
|
kprintf("socket() failed %m\n"
|
||||||
" try running: sudo prlimit --pid=$$ --nofile=%d\n",
|
" try running: sudo prlimit --pid=$$ --nofile=%d\n",
|
||||||
|
@ -294,7 +294,7 @@ int main(int argc, char *argv[]) {
|
||||||
}
|
}
|
||||||
|
|
||||||
threads = argc > 1 ? atoi(argv[1]) : GetCpuCount();
|
threads = argc > 1 ? atoi(argv[1]) : GetCpuCount();
|
||||||
if ((1 <= threads && threads <= 100000)) {
|
if (!(1 <= threads && threads <= 100000)) {
|
||||||
kprintf("error: invalid number of threads\n");
|
kprintf("error: invalid number of threads\n");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,10 +64,7 @@ struct CloneArgs {
|
||||||
uint32_t utid;
|
uint32_t utid;
|
||||||
int64_t tid64;
|
int64_t tid64;
|
||||||
};
|
};
|
||||||
union {
|
char lock;
|
||||||
char lock;
|
|
||||||
void *oldrsp;
|
|
||||||
};
|
|
||||||
int *ptid;
|
int *ptid;
|
||||||
int *ctid;
|
int *ctid;
|
||||||
int *ztid;
|
int *ztid;
|
||||||
|
@ -287,6 +284,15 @@ static int CloneFreebsd(int (*func)(void *, int), char *stk, size_t stksz,
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// OPEN BESIYATA DISHMAYA
|
// OPEN BESIYATA DISHMAYA
|
||||||
|
|
||||||
|
static void *oldrsp;
|
||||||
|
|
||||||
|
__attribute__((__constructor__)) static void OpenbsdGetSafeRsp(void) {
|
||||||
|
// main thread stack should never be freed during process lifetime. we
|
||||||
|
// won't actually change this stack below. we just need need a place
|
||||||
|
// where threads can park RSP for a few instructions while dying.
|
||||||
|
oldrsp = __builtin_frame_address(0);
|
||||||
|
}
|
||||||
|
|
||||||
static wontreturn void OpenbsdThreadMain(void *p) {
|
static wontreturn void OpenbsdThreadMain(void *p) {
|
||||||
struct CloneArgs *wt = p;
|
struct CloneArgs *wt = p;
|
||||||
*wt->ptid = wt->tid;
|
*wt->ptid = wt->tid;
|
||||||
|
@ -303,7 +309,7 @@ static wontreturn void OpenbsdThreadMain(void *p) {
|
||||||
"movl\t$0,(%%rdi)\n\t" // *wt->ztid = 0
|
"movl\t$0,(%%rdi)\n\t" // *wt->ztid = 0
|
||||||
"syscall" // __threxit()
|
"syscall" // __threxit()
|
||||||
: "=m"(*wt->ztid)
|
: "=m"(*wt->ztid)
|
||||||
: "a"(302), "m"(wt->oldrsp), "D"(wt->ztid)
|
: "a"(302), "m"(oldrsp), "D"(wt->ztid)
|
||||||
: "rcx", "r11", "memory");
|
: "rcx", "r11", "memory");
|
||||||
unreachable;
|
unreachable;
|
||||||
}
|
}
|
||||||
|
@ -325,7 +331,6 @@ static int CloneOpenbsd(int (*func)(void *, int), char *stk, size_t stksz,
|
||||||
wt->ptid = flags & CLONE_PARENT_SETTID ? ptid : &wt->tid;
|
wt->ptid = flags & CLONE_PARENT_SETTID ? ptid : &wt->tid;
|
||||||
wt->ctid = flags & CLONE_CHILD_SETTID ? ctid : &wt->tid;
|
wt->ctid = flags & CLONE_CHILD_SETTID ? ctid : &wt->tid;
|
||||||
wt->ztid = flags & CLONE_CHILD_CLEARTID ? ctid : &wt->tid;
|
wt->ztid = flags & CLONE_CHILD_CLEARTID ? ctid : &wt->tid;
|
||||||
wt->oldrsp = __builtin_frame_address(0);
|
|
||||||
wt->arg = arg;
|
wt->arg = arg;
|
||||||
wt->func = func;
|
wt->func = func;
|
||||||
tf->tf_stack = (char *)wt - 8;
|
tf->tf_stack = (char *)wt - 8;
|
||||||
|
@ -591,13 +596,8 @@ int clone(void *func, void *stk, size_t stksz, int flags, void *arg, int *ptid,
|
||||||
int rc;
|
int rc;
|
||||||
struct CloneArgs *wt;
|
struct CloneArgs *wt;
|
||||||
|
|
||||||
if ((flags & CLONE_SETTLS) && !__tls_enabled) {
|
if (flags & CLONE_SETTLS) __enable_tls();
|
||||||
__enable_tls();
|
if (flags & CLONE_THREAD) __enable_threads();
|
||||||
}
|
|
||||||
|
|
||||||
if ((flags & CLONE_THREAD) && !__threaded) {
|
|
||||||
__enable_threads();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!func) {
|
if (!func) {
|
||||||
rc = einval();
|
rc = einval();
|
||||||
|
|
|
@ -76,6 +76,23 @@ cosmo: push %rbp
|
||||||
ret
|
ret
|
||||||
.endfn cosmo,weak
|
.endfn cosmo,weak
|
||||||
|
|
||||||
|
#if !IsTiny()
|
||||||
|
// Enable TLS early if _Thread_local is used
|
||||||
|
// In MODE=tiny you may need to explicitly call __enable_tls()
|
||||||
|
// Otherwise this would bloat life.com from 16kb → 32kb D:
|
||||||
|
.init.start 304,_init_tls
|
||||||
|
mov $_tls_content,%eax
|
||||||
|
test %eax,%eax
|
||||||
|
jz 1f
|
||||||
|
push %rdi
|
||||||
|
push %rsi
|
||||||
|
call __enable_tls
|
||||||
|
pop %rsi
|
||||||
|
pop %rdi
|
||||||
|
jz 1f
|
||||||
|
1: .init.end 304,_init_tls
|
||||||
|
#endif
|
||||||
|
|
||||||
#if !IsTiny()
|
#if !IsTiny()
|
||||||
// Creates deterministically addressed stack we can use
|
// Creates deterministically addressed stack we can use
|
||||||
//
|
//
|
||||||
|
|
|
@ -16,36 +16,42 @@
|
||||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/bits/atomic.h"
|
#include "libc/assert.h"
|
||||||
#include "libc/mem/mem.h"
|
#include "libc/calls/calls.h"
|
||||||
|
#include "libc/calls/strace.internal.h"
|
||||||
|
#include "libc/nexgen32e/threaded.h"
|
||||||
#include "libc/runtime/runtime.h"
|
#include "libc/runtime/runtime.h"
|
||||||
#include "libc/thread/thread.h"
|
|
||||||
|
|
||||||
static struct Zombie {
|
extern int __threadcalls_end[];
|
||||||
struct Zombie *next;
|
extern int __threadcalls_start[];
|
||||||
cthread_t td;
|
|
||||||
} * cthread_zombies;
|
|
||||||
|
|
||||||
void cthread_zombies_add(cthread_t td) {
|
privileged void __enable_threads(void) {
|
||||||
struct Zombie *z;
|
if (__threaded) return;
|
||||||
if ((z = malloc(sizeof(struct Zombie)))) {
|
STRACE("__enable_threads()");
|
||||||
z->td = td;
|
__threaded = gettid();
|
||||||
z->next = atomic_load(&cthread_zombies);
|
__morph_begin();
|
||||||
for (;;) {
|
/*
|
||||||
if (atomic_compare_exchange_weak(&cthread_zombies, &z->next, z)) {
|
* _NOPL("__threadcalls", func)
|
||||||
break;
|
*
|
||||||
}
|
* The big ugly macro above is used by Cosmopolitan Libc to unser
|
||||||
}
|
* locking primitive (e.g. flockfile, funlockfile) have zero impact on
|
||||||
}
|
* performance and binary size when threads aren't actually in play.
|
||||||
}
|
*
|
||||||
|
* we have this
|
||||||
void cthread_zombies_reap(void) {
|
*
|
||||||
struct Zombie *z;
|
* 0f 1f 05 b1 19 00 00 nopl func(%rip)
|
||||||
// TODO(jart): Is this right? Update to not use malloc/free?
|
*
|
||||||
while ((z = atomic_load(&cthread_zombies)) && !atomic_load(&z->td->tid)) {
|
* we're going to turn it into this
|
||||||
if (atomic_compare_exchange_weak(&cthread_zombies, &z, z->next)) {
|
*
|
||||||
munmap(z->td->alloc.bottom, z->td->alloc.top - z->td->alloc.bottom);
|
* 67 67 e8 b1 19 00 00 addr32 addr32 call func
|
||||||
free(z);
|
*
|
||||||
}
|
* This is cheap and fast because the big ugly macro stored in the
|
||||||
|
* binary the offsets of all the instructions we need to change.
|
||||||
|
*/
|
||||||
|
for (int *p = __threadcalls_start; p < __threadcalls_end; ++p) {
|
||||||
|
_base[*p + 0] = 0x67;
|
||||||
|
_base[*p + 1] = 0x67;
|
||||||
|
_base[*p + 2] = 0xe8;
|
||||||
}
|
}
|
||||||
|
__morph_end();
|
||||||
}
|
}
|
|
@ -16,17 +16,14 @@
|
||||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/assert.h"
|
|
||||||
#include "libc/bits/bits.h"
|
|
||||||
#include "libc/calls/calls.h"
|
#include "libc/calls/calls.h"
|
||||||
|
#include "libc/calls/strace.internal.h"
|
||||||
#include "libc/calls/syscall-sysv.internal.h"
|
#include "libc/calls/syscall-sysv.internal.h"
|
||||||
#include "libc/dce.h"
|
#include "libc/dce.h"
|
||||||
#include "libc/errno.h"
|
#include "libc/errno.h"
|
||||||
#include "libc/intrin/kprintf.h"
|
|
||||||
#include "libc/macros.internal.h"
|
#include "libc/macros.internal.h"
|
||||||
#include "libc/nexgen32e/threaded.h"
|
#include "libc/nexgen32e/threaded.h"
|
||||||
#include "libc/nt/thread.h"
|
#include "libc/nt/thread.h"
|
||||||
#include "libc/nt/thunk/msabi.h"
|
|
||||||
#include "libc/runtime/internal.h"
|
#include "libc/runtime/internal.h"
|
||||||
#include "libc/runtime/runtime.h"
|
#include "libc/runtime/runtime.h"
|
||||||
#include "libc/str/str.h"
|
#include "libc/str/str.h"
|
||||||
|
@ -48,14 +45,14 @@
|
||||||
#define _TLDZ ((intptr_t)_tdata_size)
|
#define _TLDZ ((intptr_t)_tdata_size)
|
||||||
#define _TIBZ sizeof(struct cthread_descriptor_t)
|
#define _TIBZ sizeof(struct cthread_descriptor_t)
|
||||||
|
|
||||||
extern int __threadcalls_end[];
|
|
||||||
extern int __threadcalls_start[];
|
|
||||||
extern unsigned char __get_tls_nt_rax[];
|
|
||||||
__msabi extern typeof(TlsAlloc) *const __imp_TlsAlloc;
|
__msabi extern typeof(TlsAlloc) *const __imp_TlsAlloc;
|
||||||
|
|
||||||
|
extern unsigned char __tls_mov_nt_rax[];
|
||||||
|
extern unsigned char __tls_add_nt_rax[];
|
||||||
|
|
||||||
privileged void __enable_tls(void) {
|
privileged void __enable_tls(void) {
|
||||||
assert(!__threaded);
|
if (__tls_enabled) return;
|
||||||
assert(!__tls_enabled);
|
STRACE("__enable_tls()");
|
||||||
|
|
||||||
// allocate tls memory for main process
|
// allocate tls memory for main process
|
||||||
//
|
//
|
||||||
|
@ -119,6 +116,7 @@ privileged void __enable_tls(void) {
|
||||||
* -mno-tls-direct-seg-refs flag which generates code like this
|
* -mno-tls-direct-seg-refs flag which generates code like this
|
||||||
*
|
*
|
||||||
* 64 48 8b 0R4 25 00 00 00 00 mov %fs:0,%R
|
* 64 48 8b 0R4 25 00 00 00 00 mov %fs:0,%R
|
||||||
|
* 64 48 03 0R4 25 00 00 00 00 add %fs:0,%R
|
||||||
*
|
*
|
||||||
* Which on Mac we can replace with this:
|
* Which on Mac we can replace with this:
|
||||||
*
|
*
|
||||||
|
@ -127,17 +125,22 @@ privileged void __enable_tls(void) {
|
||||||
* Whereas on Windows we'll replace it with this:
|
* Whereas on Windows we'll replace it with this:
|
||||||
*
|
*
|
||||||
* 0f 1f 40 00 fatnop4
|
* 0f 1f 40 00 fatnop4
|
||||||
* e8 xx xx xx xx call __get_tls_nt_%R
|
* e8 xx xx xx xx call __tls_mov_nt_%R
|
||||||
*
|
*
|
||||||
* Since we have no idea where the TLS instructions exist in the
|
* Since we have no idea where the TLS instructions exist in the
|
||||||
* binary, we need to disassemble the whole program image. This'll
|
* binary, we need to disassemble the whole program image. This'll
|
||||||
* potentially take a few milliseconds for some larger programs.
|
* potentially take a few milliseconds for some larger programs.
|
||||||
*
|
*
|
||||||
|
* We check `_tls_content` which is generated by the linker script
|
||||||
|
* since it lets us determine ahead of time if _Thread_local vars
|
||||||
|
* have actually been linked into this program.
|
||||||
|
*
|
||||||
* TODO(jart): compute probability this is just overkill
|
* TODO(jart): compute probability this is just overkill
|
||||||
*/
|
*/
|
||||||
if (IsWindows() || IsXnu()) {
|
if ((intptr_t)_tls_content && (IsWindows() || IsXnu())) {
|
||||||
int n, reg, dis;
|
int n, reg, dis;
|
||||||
unsigned char *p;
|
unsigned char *p;
|
||||||
|
const unsigned char *impl;
|
||||||
struct XedDecodedInst xedd;
|
struct XedDecodedInst xedd;
|
||||||
__morph_begin();
|
__morph_begin();
|
||||||
|
|
||||||
|
@ -154,7 +157,8 @@ privileged void __enable_tls(void) {
|
||||||
if (xedd.length == 9 && //
|
if (xedd.length == 9 && //
|
||||||
0144 == p[0] && // fs
|
0144 == p[0] && // fs
|
||||||
0110 == p[1] && // rex.w (64-bit operand size)
|
0110 == p[1] && // rex.w (64-bit operand size)
|
||||||
0213 == p[2] && // mov reg/mem → reg (word-sized)
|
(0213 == p[2] || // mov reg/mem → reg (word-sized)
|
||||||
|
0003 == p[2]) && // add reg/mem → reg (word-sized)
|
||||||
0004 == (p[3] & 0307) && // mod/rm (4,reg,0) means sib → reg
|
0004 == (p[3] & 0307) && // mod/rm (4,reg,0) means sib → reg
|
||||||
0045 == p[4] && // sib (5,4,0) → (rbp,rsp,0) → disp32
|
0045 == p[4] && // sib (5,4,0) → (rbp,rsp,0) → disp32
|
||||||
0000 == p[5] && // displacement (von Neumann endian)
|
0000 == p[5] && // displacement (von Neumann endian)
|
||||||
|
@ -175,8 +179,13 @@ privileged void __enable_tls(void) {
|
||||||
// that's the same as the mov destination. When setting
|
// that's the same as the mov destination. When setting
|
||||||
// function displacement, &CALL+5+DISP must equal &FUNC.
|
// function displacement, &CALL+5+DISP must equal &FUNC.
|
||||||
else {
|
else {
|
||||||
|
if (p[2] == 3) {
|
||||||
|
impl = __tls_add_nt_rax;
|
||||||
|
} else {
|
||||||
|
impl = __tls_mov_nt_rax;
|
||||||
|
}
|
||||||
reg = (p[3] & 070) >> 3;
|
reg = (p[3] & 070) >> 3;
|
||||||
dis = (__get_tls_nt_rax + reg * 18) - (p + 9);
|
dis = (impl + reg * 18) - (p + 9);
|
||||||
p[0] = 0017; // map1
|
p[0] = 0017; // map1
|
||||||
p[1] = 0037; // nopl (onl if reg=0)
|
p[1] = 0037; // nopl (onl if reg=0)
|
||||||
p[2] = 0100; // mod/rm (%rax)+disp8
|
p[2] = 0100; // mod/rm (%rax)+disp8
|
||||||
|
@ -202,35 +211,6 @@ privileged void __enable_tls(void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// we are now allowed to use tls
|
// we are now allowed to use tls
|
||||||
|
// setting this variable
|
||||||
__tls_enabled = true;
|
__tls_enabled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
privileged void __enable_threads(void) {
|
|
||||||
assert(!__threaded);
|
|
||||||
__threaded = gettid();
|
|
||||||
__morph_begin();
|
|
||||||
/*
|
|
||||||
* _NOPL("__threadcalls", func)
|
|
||||||
*
|
|
||||||
* The big ugly macro above is used by Cosmopolitan Libc to unser
|
|
||||||
* locking primitive (e.g. flockfile, funlockfile) have zero impact on
|
|
||||||
* performance and binary size when threads aren't actually in play.
|
|
||||||
*
|
|
||||||
* we have this
|
|
||||||
*
|
|
||||||
* 0f 1f 05 b1 19 00 00 nopl func(%rip)
|
|
||||||
*
|
|
||||||
* we're going to turn it into this
|
|
||||||
*
|
|
||||||
* 67 67 e8 b1 19 00 00 addr32 addr32 call func
|
|
||||||
*
|
|
||||||
* This is cheap and fast because the big ugly macro stored in the
|
|
||||||
* binary the offsets of all the instructions we need to change.
|
|
||||||
*/
|
|
||||||
for (int *p = __threadcalls_start; p < __threadcalls_end; ++p) {
|
|
||||||
_base[*p + 0] = 0x67;
|
|
||||||
_base[*p + 1] = 0x67;
|
|
||||||
_base[*p + 2] = 0xe8;
|
|
||||||
}
|
|
||||||
__morph_end();
|
|
||||||
}
|
|
|
@ -24,6 +24,7 @@ extern unsigned char _tdata_size[];
|
||||||
extern unsigned char _tbss_start[];
|
extern unsigned char _tbss_start[];
|
||||||
extern unsigned char _tbss_end[];
|
extern unsigned char _tbss_end[];
|
||||||
extern unsigned char _tls_size[];
|
extern unsigned char _tls_size[];
|
||||||
|
extern unsigned char _tls_content[];
|
||||||
|
|
||||||
void _init(void) hidden;
|
void _init(void) hidden;
|
||||||
void __enable_tls(void) hidden;
|
void __enable_tls(void) hidden;
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
#define ShouldUseMsabiAttribute() 1
|
#define ShouldUseMsabiAttribute() 1
|
||||||
#include "libc/bits/asmflag.h"
|
#include "libc/bits/asmflag.h"
|
||||||
#include "libc/calls/internal.h"
|
#include "libc/calls/internal.h"
|
||||||
|
#include "libc/calls/strace.internal.h"
|
||||||
#include "libc/calls/struct/sigset.h"
|
#include "libc/calls/struct/sigset.h"
|
||||||
#include "libc/dce.h"
|
#include "libc/dce.h"
|
||||||
#include "libc/errno.h"
|
#include "libc/errno.h"
|
||||||
|
@ -58,6 +59,7 @@ static privileged void __morph_mprotect(void *addr, size_t size, int prot,
|
||||||
*/
|
*/
|
||||||
privileged void __morph_begin(void) {
|
privileged void __morph_begin(void) {
|
||||||
sigset_t ss = {{-1, -1}};
|
sigset_t ss = {{-1, -1}};
|
||||||
|
STRACE("__morph_begin()");
|
||||||
if (!IsWindows()) {
|
if (!IsWindows()) {
|
||||||
sys_sigprocmask(SIG_BLOCK, &ss, &oldss);
|
sys_sigprocmask(SIG_BLOCK, &ss, &oldss);
|
||||||
}
|
}
|
||||||
|
@ -74,4 +76,5 @@ privileged void __morph_end(void) {
|
||||||
if (!IsWindows()) {
|
if (!IsWindows()) {
|
||||||
sys_sigprocmask(SIG_SETMASK, &oldss, 0);
|
sys_sigprocmask(SIG_SETMASK, &oldss, 0);
|
||||||
}
|
}
|
||||||
|
STRACE("__morph_end()");
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,6 +51,7 @@
|
||||||
_tbss_start = 0
|
_tbss_start = 0
|
||||||
_tbss_end = 0
|
_tbss_end = 0
|
||||||
_tls_size = 0
|
_tls_size = 0
|
||||||
|
_tls_content = 0
|
||||||
|
|
||||||
.globl _base
|
.globl _base
|
||||||
.globl ape_xlm
|
.globl ape_xlm
|
||||||
|
@ -73,6 +74,7 @@
|
||||||
.globl _tbss_start
|
.globl _tbss_start
|
||||||
.globl _tbss_end
|
.globl _tbss_end
|
||||||
.globl _tls_size
|
.globl _tls_size
|
||||||
|
.globl _tls_content
|
||||||
.globl __data_start
|
.globl __data_start
|
||||||
.globl __data_end
|
.globl __data_end
|
||||||
.globl __bss_start
|
.globl __bss_start
|
||||||
|
@ -99,6 +101,7 @@
|
||||||
.weak _tbss_start
|
.weak _tbss_start
|
||||||
.weak _tbss_end
|
.weak _tbss_end
|
||||||
.weak _tls_size
|
.weak _tls_size
|
||||||
|
.weak _tls_content
|
||||||
.weak __data_start
|
.weak __data_start
|
||||||
.weak __data_end
|
.weak __data_end
|
||||||
.weak __bss_start
|
.weak __bss_start
|
||||||
|
|
|
@ -25,66 +25,132 @@
|
||||||
// @note each function is exactly 18 bytes
|
// @note each function is exactly 18 bytes
|
||||||
// @see __enable_threads()
|
// @see __enable_threads()
|
||||||
|
|
||||||
__get_tls_nt_rax:
|
__tls_mov_nt_rax:
|
||||||
push %rcx
|
push %rcx
|
||||||
mov __tls_index(%rip),%ecx
|
mov __tls_index(%rip),%ecx
|
||||||
mov %gs:0x1480(,%rcx,8),%rax
|
mov %gs:0x1480(,%rcx,8),%rax
|
||||||
pop %rcx
|
pop %rcx
|
||||||
ret
|
ret
|
||||||
.endfn __get_tls_nt_rax,globl,hidden
|
.endfn __tls_mov_nt_rax,globl,hidden
|
||||||
|
|
||||||
__get_tls_nt_rcx:
|
__tls_mov_nt_rcx:
|
||||||
push %rax
|
push %rax
|
||||||
mov __tls_index(%rip),%eax
|
mov __tls_index(%rip),%eax
|
||||||
mov %gs:0x1480(,%rax,8),%rcx
|
mov %gs:0x1480(,%rax,8),%rcx
|
||||||
pop %rax
|
pop %rax
|
||||||
ret
|
ret
|
||||||
.endfn __get_tls_nt_rcx
|
.endfn __tls_mov_nt_rcx
|
||||||
|
|
||||||
__get_tls_nt_rdx:
|
__tls_mov_nt_rdx:
|
||||||
push %rax
|
push %rax
|
||||||
mov __tls_index(%rip),%eax
|
mov __tls_index(%rip),%eax
|
||||||
mov %gs:0x1480(,%rax,8),%rdx
|
mov %gs:0x1480(,%rax,8),%rdx
|
||||||
pop %rax
|
pop %rax
|
||||||
ret
|
ret
|
||||||
.endfn __get_tls_nt_rdx
|
.endfn __tls_mov_nt_rdx
|
||||||
|
|
||||||
__get_tls_nt_rbx:
|
__tls_mov_nt_rbx:
|
||||||
push %rax
|
push %rax
|
||||||
mov __tls_index(%rip),%eax
|
mov __tls_index(%rip),%eax
|
||||||
mov %gs:0x1480(,%rax,8),%rbx
|
mov %gs:0x1480(,%rax,8),%rbx
|
||||||
pop %rax
|
pop %rax
|
||||||
ret
|
ret
|
||||||
.endfn __get_tls_nt_rbx
|
.endfn __tls_mov_nt_rbx
|
||||||
|
|
||||||
__get_tls_nt_rsp:
|
__tls_mov_nt_rsp:
|
||||||
push %rax
|
push %rax
|
||||||
mov __tls_index(%rip),%eax
|
mov __tls_index(%rip),%eax
|
||||||
mov %gs:0x1480(,%rax,8),%rsp
|
mov %gs:0x1480(,%rax,8),%rsp
|
||||||
pop %rax
|
pop %rax
|
||||||
ret
|
ret
|
||||||
.endfn __get_tls_nt_rsp
|
.endfn __tls_mov_nt_rsp
|
||||||
|
|
||||||
__get_tls_nt_rbp:
|
__tls_mov_nt_rbp:
|
||||||
push %rax
|
push %rax
|
||||||
mov __tls_index(%rip),%eax
|
mov __tls_index(%rip),%eax
|
||||||
mov %gs:0x1480(,%rax,8),%rbp
|
mov %gs:0x1480(,%rax,8),%rbp
|
||||||
pop %rax
|
pop %rax
|
||||||
ret
|
ret
|
||||||
.endfn __get_tls_nt_rbp
|
.endfn __tls_mov_nt_rbp
|
||||||
|
|
||||||
__get_tls_nt_rsi:
|
__tls_mov_nt_rsi:
|
||||||
push %rax
|
push %rax
|
||||||
mov __tls_index(%rip),%eax
|
mov __tls_index(%rip),%eax
|
||||||
mov %gs:0x1480(,%rax,8),%rsi
|
mov %gs:0x1480(,%rax,8),%rsi
|
||||||
pop %rax
|
pop %rax
|
||||||
ret
|
ret
|
||||||
.endfn __get_tls_nt_rsi
|
.endfn __tls_mov_nt_rsi
|
||||||
|
|
||||||
__get_tls_nt_rdi:
|
__tls_mov_nt_rdi:
|
||||||
push %rax
|
push %rax
|
||||||
mov __tls_index(%rip),%eax
|
mov __tls_index(%rip),%eax
|
||||||
mov %gs:0x1480(,%rax,8),%rdi
|
mov %gs:0x1480(,%rax,8),%rdi
|
||||||
pop %rax
|
pop %rax
|
||||||
ret
|
ret
|
||||||
.endfn __get_tls_nt_rdi
|
.endfn __tls_mov_nt_rdi
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
__tls_add_nt_rax:
|
||||||
|
push %rcx
|
||||||
|
mov __tls_index(%rip),%ecx
|
||||||
|
add %gs:0x1480(,%rcx,8),%rax
|
||||||
|
pop %rcx
|
||||||
|
ret
|
||||||
|
.endfn __tls_add_nt_rax,globl,hidden
|
||||||
|
|
||||||
|
__tls_add_nt_rcx:
|
||||||
|
push %rax
|
||||||
|
mov __tls_index(%rip),%eax
|
||||||
|
add %gs:0x1480(,%rax,8),%rcx
|
||||||
|
pop %rax
|
||||||
|
ret
|
||||||
|
.endfn __tls_add_nt_rcx
|
||||||
|
|
||||||
|
__tls_add_nt_rdx:
|
||||||
|
push %rax
|
||||||
|
mov __tls_index(%rip),%eax
|
||||||
|
add %gs:0x1480(,%rax,8),%rdx
|
||||||
|
pop %rax
|
||||||
|
ret
|
||||||
|
.endfn __tls_add_nt_rdx
|
||||||
|
|
||||||
|
__tls_add_nt_rbx:
|
||||||
|
push %rax
|
||||||
|
mov __tls_index(%rip),%eax
|
||||||
|
add %gs:0x1480(,%rax,8),%rbx
|
||||||
|
pop %rax
|
||||||
|
ret
|
||||||
|
.endfn __tls_add_nt_rbx
|
||||||
|
|
||||||
|
__tls_add_nt_rsp:
|
||||||
|
push %rax
|
||||||
|
mov __tls_index(%rip),%eax
|
||||||
|
add %gs:0x1480(,%rax,8),%rsp
|
||||||
|
pop %rax
|
||||||
|
ret
|
||||||
|
.endfn __tls_add_nt_rsp
|
||||||
|
|
||||||
|
__tls_add_nt_rbp:
|
||||||
|
push %rax
|
||||||
|
mov __tls_index(%rip),%eax
|
||||||
|
add %gs:0x1480(,%rax,8),%rbp
|
||||||
|
pop %rax
|
||||||
|
ret
|
||||||
|
.endfn __tls_add_nt_rbp
|
||||||
|
|
||||||
|
__tls_add_nt_rsi:
|
||||||
|
push %rax
|
||||||
|
mov __tls_index(%rip),%eax
|
||||||
|
add %gs:0x1480(,%rax,8),%rsi
|
||||||
|
pop %rax
|
||||||
|
ret
|
||||||
|
.endfn __tls_add_nt_rsi
|
||||||
|
|
||||||
|
__tls_add_nt_rdi:
|
||||||
|
push %rax
|
||||||
|
mov __tls_index(%rip),%eax
|
||||||
|
add %gs:0x1480(,%rax,8),%rdi
|
||||||
|
pop %rax
|
||||||
|
ret
|
||||||
|
.endfn __tls_add_nt_rdi
|
||||||
|
|
|
@ -1,58 +0,0 @@
|
||||||
/*-*- 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 2020 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/bits/atomic.h"
|
|
||||||
#include "libc/calls/calls.h"
|
|
||||||
#include "libc/calls/strace.internal.h"
|
|
||||||
#include "libc/errno.h"
|
|
||||||
#include "libc/intrin/asan.internal.h"
|
|
||||||
#include "libc/str/str.h"
|
|
||||||
#include "libc/thread/thread.h"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Detaches thread.
|
|
||||||
*
|
|
||||||
* Calling this function will cause the thread to free its own memory
|
|
||||||
* once it exits. Using this function is mutually exclusive from the
|
|
||||||
* chtread_join() API.
|
|
||||||
*
|
|
||||||
* @return 0 on success or errno number on failure
|
|
||||||
* @raises EINVAL if thread isn't joinable
|
|
||||||
* @raises ESRCH if no such thread exists
|
|
||||||
* @threadsafe
|
|
||||||
*/
|
|
||||||
int cthread_detach(cthread_t td) {
|
|
||||||
int rc, tid;
|
|
||||||
if (!td || (IsAsan() && !__asan_is_valid(td, sizeof(*td)))) {
|
|
||||||
rc = ESRCH;
|
|
||||||
tid = -1;
|
|
||||||
} else if ((tid = td->tid) == gettid()) {
|
|
||||||
rc = EDEADLK;
|
|
||||||
} else if (atomic_load(&td->state) & (cthread_detached | cthread_joining)) {
|
|
||||||
rc = EINVAL;
|
|
||||||
} else if (!atomic_fetch_add(&td->state, cthread_detached) &
|
|
||||||
cthread_finished) {
|
|
||||||
rc = 0;
|
|
||||||
} else if (!munmap(td->alloc.bottom, td->alloc.top - td->alloc.bottom)) {
|
|
||||||
rc = 0;
|
|
||||||
} else {
|
|
||||||
rc = errno;
|
|
||||||
}
|
|
||||||
STRACE("cthread_detached(%d) → %s", tid, !rc ? "0" : strerrno(rc));
|
|
||||||
return rc;
|
|
||||||
}
|
|
|
@ -1,33 +0,0 @@
|
||||||
/*-*- 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 2020 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/strace.internal.h"
|
|
||||||
#include "libc/runtime/runtime.h"
|
|
||||||
#include "libc/thread/thread.h"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Exits cosmopolitan thread.
|
|
||||||
*
|
|
||||||
* @param exitcode is passed along to cthread_join()
|
|
||||||
* @threadsafe
|
|
||||||
* @noreturn
|
|
||||||
*/
|
|
||||||
wontreturn void cthread_exit(void *exitcode) {
|
|
||||||
STRACE("cthread_exit(%p)", exitcode);
|
|
||||||
longerjmp(cthread_self()->exiter, (intptr_t)exitcode);
|
|
||||||
}
|
|
|
@ -4,8 +4,9 @@
|
||||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||||
COSMOPOLITAN_C_START_
|
COSMOPOLITAN_C_START_
|
||||||
|
|
||||||
extern uint64_t _pthread_key_usage[(PTHREAD_KEYS_MAX + 63) / 64];
|
hidden extern uint64_t _pthread_key_usage[(PTHREAD_KEYS_MAX + 63) / 64];
|
||||||
extern pthread_key_dtor _pthread_key_dtor[PTHREAD_KEYS_MAX];
|
hidden extern pthread_key_dtor _pthread_key_dtor[PTHREAD_KEYS_MAX];
|
||||||
|
hidden extern _Thread_local void *_pthread_keys[PTHREAD_KEYS_MAX];
|
||||||
|
|
||||||
void _pthread_key_destruct(void *[PTHREAD_KEYS_MAX]);
|
void _pthread_key_destruct(void *[PTHREAD_KEYS_MAX]);
|
||||||
|
|
||||||
|
|
|
@ -18,5 +18,11 @@
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/thread/internal.h"
|
#include "libc/thread/internal.h"
|
||||||
|
|
||||||
|
// tls value slots for pthread keys api
|
||||||
|
_Thread_local void *_pthread_keys[PTHREAD_KEYS_MAX];
|
||||||
|
|
||||||
|
// bitset of tls key registrations
|
||||||
uint64_t _pthread_key_usage[(PTHREAD_KEYS_MAX + 63) / 64];
|
uint64_t _pthread_key_usage[(PTHREAD_KEYS_MAX + 63) / 64];
|
||||||
|
|
||||||
|
// pthread tls key destructors
|
||||||
pthread_key_dtor _pthread_key_dtor[PTHREAD_KEYS_MAX];
|
pthread_key_dtor _pthread_key_dtor[PTHREAD_KEYS_MAX];
|
||||||
|
|
|
@ -18,16 +18,15 @@
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/errno.h"
|
#include "libc/errno.h"
|
||||||
#include "libc/nexgen32e/gettls.h"
|
#include "libc/nexgen32e/gettls.h"
|
||||||
|
#include "libc/thread/internal.h"
|
||||||
#include "libc/thread/thread.h"
|
#include "libc/thread/thread.h"
|
||||||
|
|
||||||
STATIC_YOINK("_main_thread_ctor");
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets value of TLS slot for current thread.
|
* Gets value of TLS slot for current thread.
|
||||||
*/
|
*/
|
||||||
void *pthread_getspecific(pthread_key_t key) {
|
void *pthread_getspecific(pthread_key_t key) {
|
||||||
if (key < PTHREAD_KEYS_MAX) {
|
if (0 <= key && key < PTHREAD_KEYS_MAX) {
|
||||||
return ((cthread_t)__get_tls_inline())->key[key];
|
return _pthread_keys[key];
|
||||||
} else {
|
} else {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,8 +23,6 @@
|
||||||
#include "libc/thread/internal.h"
|
#include "libc/thread/internal.h"
|
||||||
#include "libc/thread/thread.h"
|
#include "libc/thread/thread.h"
|
||||||
|
|
||||||
STATIC_YOINK("_main_thread_ctor");
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allocates TLS slot.
|
* Allocates TLS slot.
|
||||||
*/
|
*/
|
||||||
|
@ -43,7 +41,7 @@ int pthread_key_create(pthread_key_t *key, pthread_key_dtor dtor) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static textexit void _pthread_key_atexit(void) {
|
static textexit void _pthread_key_atexit(void) {
|
||||||
_pthread_key_destruct(((cthread_t)__get_tls())->key);
|
_pthread_key_destruct(_pthread_keys);
|
||||||
}
|
}
|
||||||
|
|
||||||
__attribute__((__constructor__)) static textstartup void _pthread_key_init() {
|
__attribute__((__constructor__)) static textstartup void _pthread_key_init() {
|
||||||
|
|
|
@ -18,16 +18,15 @@
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/errno.h"
|
#include "libc/errno.h"
|
||||||
#include "libc/nexgen32e/gettls.h"
|
#include "libc/nexgen32e/gettls.h"
|
||||||
|
#include "libc/thread/internal.h"
|
||||||
#include "libc/thread/thread.h"
|
#include "libc/thread/thread.h"
|
||||||
|
|
||||||
STATIC_YOINK("_main_thread_ctor");
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets value of TLS slot for current thread.
|
* Sets value of TLS slot for current thread.
|
||||||
*/
|
*/
|
||||||
int pthread_setspecific(pthread_key_t key, void *val) {
|
int pthread_setspecific(pthread_key_t key, void *val) {
|
||||||
if (key < PTHREAD_KEYS_MAX) {
|
if (0 <= key && key < PTHREAD_KEYS_MAX) {
|
||||||
((cthread_t)__get_tls_inline())->key[key] = val;
|
_pthread_keys[key] = val;
|
||||||
return 0;
|
return 0;
|
||||||
} else {
|
} else {
|
||||||
return EINVAL;
|
return EINVAL;
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/bits/atomic.h"
|
#include "libc/bits/atomic.h"
|
||||||
|
#include "libc/calls/calls.h"
|
||||||
#include "libc/thread/thread.h"
|
#include "libc/thread/thread.h"
|
||||||
|
|
||||||
STATIC_YOINK("_main_thread_ctor");
|
STATIC_YOINK("_main_thread_ctor");
|
||||||
|
@ -29,7 +30,7 @@ static void Pause(int attempt) {
|
||||||
__builtin_ia32_pause();
|
__builtin_ia32_pause();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
cthread_yield();
|
sched_yield();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,7 +35,19 @@
|
||||||
STATIC_YOINK("_main_thread_ctor");
|
STATIC_YOINK("_main_thread_ctor");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @fileoverview Simple System Threads API
|
* @fileoverview Simple threading API
|
||||||
|
*
|
||||||
|
* This API is supported on all six operating systems. We have this
|
||||||
|
* because the POSIX threads API is positively enormous. We currently
|
||||||
|
* only implement a small subset of POSIX threads, e.g. mutexes. So
|
||||||
|
* until we can implement all of POSIX threads, this API is great. If we
|
||||||
|
* consider that the classic forking concurrency library consists of a
|
||||||
|
* single function, it's a shame POSIX didn't define threads in the past
|
||||||
|
* to just be this. Since create/join/atomics is really all we need.
|
||||||
|
*
|
||||||
|
* Your spawn library abstracts clone() which also works on all
|
||||||
|
* platforms; however our implementation of clone() is significantly
|
||||||
|
* complicated so we strongly recommend always favoring this API.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define _TLSZ ((intptr_t)_tls_size)
|
#define _TLSZ ((intptr_t)_tls_size)
|
||||||
|
@ -50,7 +62,7 @@ STATIC_YOINK("_main_thread_ctor");
|
||||||
* @param arg shall be passed to `fun`
|
* @param arg shall be passed to `fun`
|
||||||
* @param opt_out_thread needn't be initialiized and is always clobbered
|
* @param opt_out_thread needn't be initialiized and is always clobbered
|
||||||
* except when it isn't specified, in which case, the thread is kind
|
* except when it isn't specified, in which case, the thread is kind
|
||||||
* of detached and will leak in stack / tls memory
|
* of detached and will (currently) just leak the stack / tls memory
|
||||||
* @return 0 on success, or -1 w/ errno
|
* @return 0 on success, or -1 w/ errno
|
||||||
*/
|
*/
|
||||||
int _spawn(int fun(void *, int), void *arg, struct spawn *opt_out_thread) {
|
int _spawn(int fun(void *, int), void *arg, struct spawn *opt_out_thread) {
|
||||||
|
|
|
@ -23,17 +23,11 @@ struct cthread_descriptor_t {
|
||||||
int32_t __pad0; /* 0x10 */
|
int32_t __pad0; /* 0x10 */
|
||||||
int32_t state; /* 0x14 */
|
int32_t state; /* 0x14 */
|
||||||
void *arg; /* 0x18 */
|
void *arg; /* 0x18 */
|
||||||
void *pthread_ret_ptr; /* 0x20 */
|
int64_t __pad1; /* 0x20 */
|
||||||
int64_t __pad1; /* 0x28 */
|
int64_t __pad2; /* 0x28 */
|
||||||
struct cthread_descriptor_t *self2; /* 0x30 */
|
struct cthread_descriptor_t *self2; /* 0x30 */
|
||||||
int32_t tid; /* 0x38 */
|
int32_t tid; /* 0x38 */
|
||||||
int32_t err; /* 0x3c */
|
int32_t err; /* 0x3c */
|
||||||
void *exitcode;
|
|
||||||
struct {
|
|
||||||
char *top, *bottom;
|
|
||||||
} stack, alloc;
|
|
||||||
jmp_buf exiter;
|
|
||||||
void *key[PTHREAD_KEYS_MAX];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct cthread_descriptor_t *cthread_t;
|
typedef struct cthread_descriptor_t *cthread_t;
|
||||||
|
@ -49,16 +43,7 @@ typedef struct cthread_attr_t {
|
||||||
int mode;
|
int mode;
|
||||||
} cthread_attr_t;
|
} cthread_attr_t;
|
||||||
|
|
||||||
extern const void *const _main_thread_ctor[];
|
|
||||||
|
|
||||||
int cthread_create(cthread_t *, const cthread_attr_t *, void *(*)(void *),
|
|
||||||
void *);
|
|
||||||
|
|
||||||
int cthread_yield(void);
|
|
||||||
cthread_t cthread_self(void);
|
cthread_t cthread_self(void);
|
||||||
int cthread_join(cthread_t, void **);
|
|
||||||
void cthread_exit(void *) wontreturn;
|
|
||||||
int cthread_detach(cthread_t);
|
|
||||||
int cthread_attr_init(cthread_attr_t *);
|
int cthread_attr_init(cthread_attr_t *);
|
||||||
int cthread_attr_destroy(cthread_attr_t *);
|
int cthread_attr_destroy(cthread_attr_t *);
|
||||||
int cthread_attr_setstacksize(cthread_attr_t *, size_t);
|
int cthread_attr_setstacksize(cthread_attr_t *, size_t);
|
||||||
|
|
|
@ -1,27 +0,0 @@
|
||||||
/*-*- 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 2020 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/thread/thread.h"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Asks operating system to handoff remaining time slice.
|
|
||||||
*/
|
|
||||||
int cthread_yield(void) {
|
|
||||||
return sched_yield();
|
|
||||||
}
|
|
|
@ -1,30 +0,0 @@
|
||||||
/*-*- 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 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. │
|
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
|
||||||
|
|
||||||
_Thread_local char x;
|
|
||||||
static _Thread_local char y;
|
|
||||||
|
|
||||||
char ha(void) {
|
|
||||||
++y;
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
|
|
||||||
char ya(void) {
|
|
||||||
return y;
|
|
||||||
}
|
|
|
@ -42,6 +42,10 @@ p,e = re.compile("[{")
|
||||||
assert(e:errno() == re.EBRACK)
|
assert(e:errno() == re.EBRACK)
|
||||||
assert(e:doc() == "Missing ']'")
|
assert(e:doc() == "Missing ']'")
|
||||||
|
|
||||||
|
p,e = re.search("notfound", "fanatics have their dreams wherewith they weave")
|
||||||
|
assert(not p)
|
||||||
|
assert(e:errno() == re.NOMATCH)
|
||||||
|
|
||||||
----------------------------------------------------------------------------------------------------
|
----------------------------------------------------------------------------------------------------
|
||||||
-- BENCHMARKS
|
-- BENCHMARKS
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue