Get threads working on all platforms

We now have a high-quality clone() implementation for creating
lightweight threads on Linux/Windows/FreeBSD/NetBSD/OpenBSD.
This commit is contained in:
Justine Tunney 2022-05-12 17:52:13 -07:00
parent 4e62cefa6e
commit fec396037a
43 changed files with 850 additions and 532 deletions

View file

@ -16,14 +16,12 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/bits/weaken.h"
#include "libc/calls/internal.h"
#include "libc/calls/strace.internal.h"
#include "libc/dce.h"
#include "libc/intrin/setjmp.internal.h"
#include "libc/intrin/winthread.internal.h"
#include "libc/mem/mem.h"
#include "libc/nt/runtime.h"
#include "libc/nt/thread.h"
#include "libc/runtime/runtime.h"
#include "libc/sysv/consts/nr.h"
/**
@ -36,18 +34,15 @@
*/
privileged wontreturn void _Exit1(int rc) {
struct WinThread *wt;
STRACE("_Exit1(%d)", rc);
/* STRACE("_Exit1(%d)", rc); */
if (!IsWindows() && !IsMetal()) {
register long r10 asm("r10") = 0;
asm volatile("syscall"
: /* no outputs */
: "a"(__NR_exit), "D"(IsLinux() ? rc : 0)
: "a"(__NR_exit), "D"(IsLinux() ? rc : 0), "S"(0), "d"(0),
"r"(r10)
: "rcx", "r11", "memory");
__builtin_unreachable();
} else if (IsWindows()) {
if ((wt = GetWinThread())) {
__releasefd(wt->pid);
weaken(free)(wt);
}
ExitThread(rc);
}
for (;;) {

View file

@ -19,18 +19,21 @@
#include "libc/calls/calls.h"
#include "libc/dce.h"
#include "libc/intrin/tls.h"
#include "libc/intrin/winthread.internal.h"
#include "libc/nt/thread.h"
/**
* Returns current thread id.
* @asyncsignalsafe
*/
int gettid(void) {
privileged int gettid(void) {
int rc;
int64_t wut;
struct WinThread *wt;
if (IsWindows()) {
return GetCurrentThreadId();
}
if (IsLinux()) {
asm("syscall"
: "=a"(rc) // man says always succeeds
@ -59,7 +62,7 @@ int gettid(void) {
asm("syscall"
: "=a"(rc) // man says always succeeds
: "0"(311) // _lwp_self()
: "rcx", "r11", "memory", "cc");
: "rcx", "rdx", "r11", "memory", "cc");
return rc;
}
@ -73,13 +76,5 @@ int gettid(void) {
return wut; // narrowing intentional
}
if (IsWindows()) {
if ((wt = GetWinThread())) {
return wt->pid;
} else {
return GetCurrentThreadId();
}
}
return getpid();
}

View file

@ -31,6 +31,7 @@
#include "libc/intrin/lockcmpxchg.h"
#include "libc/intrin/nomultics.internal.h"
#include "libc/intrin/spinlock.h"
#include "libc/intrin/threaded.internal.h"
#include "libc/limits.h"
#include "libc/log/internal.h"
#include "libc/macros.internal.h"
@ -56,7 +57,6 @@ struct Timestamps {
unsigned long long start;
};
extern bool __threaded;
unsigned long long __kbirth; // see fork-nt.c
privileged static struct Timestamps kenter(void) {

View file

@ -0,0 +1,29 @@
#ifndef COSMOPOLITAN_LIBC_INTRIN_SETJMP_INTERNAL_H_
#define COSMOPOLITAN_LIBC_INTRIN_SETJMP_INTERNAL_H_
#include "libc/limits.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
/**
* Encodes nonzero number for longjmp().
*
* This is a workaround to the fact that the value has to be non-zero.
* So we work around it by dedicating the highest bit to being a flag.
*/
static inline int EncodeLongjmp(int x) {
return x | INT_MIN;
}
/**
* Decodes nonzero number returned by setjmp().
*
* This is a workaround to the fact that the value has to be non-zero.
* So we work around it by dedicating the highest bit to being a flag.
*/
static inline int DecodeSetjmp(int x) {
return (int)((unsigned)x << 1) >> 1;
}
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_INTRIN_SETJMP_INTERNAL_H_ */

View file

@ -2,27 +2,33 @@
#define COSMOPOLITAN_LIBC_INTRIN_SPINLOCK_H_
#ifdef TINY
#define _spinlock(lock) \
do { \
while (__sync_lock_test_and_set(lock, 1)) { \
__builtin_ia32_pause(); \
} \
#define _spinlock(lock) \
do { \
while (__atomic_test_and_set(lock, __ATOMIC_SEQ_CST)) { \
__builtin_ia32_pause(); \
} \
} while (0)
#else
#define _spinlock(lock) \
do { \
for (;;) { \
typeof(*(lock)) x; \
__atomic_load(lock, &x, __ATOMIC_RELAXED); \
if (!x && !__sync_lock_test_and_set(lock, 1)) { \
break; \
} else { \
__builtin_ia32_pause(); \
} \
} \
#define _spinlock(lock) \
do { \
for (;;) { \
typeof(*(lock)) x; \
__atomic_load(lock, &x, __ATOMIC_RELAXED); \
if (!x && !__atomic_test_and_set(lock, __ATOMIC_SEQ_CST)) { \
break; \
} else { \
__builtin_ia32_pause(); \
} \
} \
} while (0)
#endif
#define _spunlock(lock) __sync_lock_release(lock)
#define _spunlock(lock) __atomic_clear(lock, __ATOMIC_RELAXED)
#define _seizelock(lock) \
do { \
typeof(*(lock)) x = 1; \
__atomic_store(lock, &x, __ATOMIC_SEQ_CST); \
} while (0)
#endif /* COSMOPOLITAN_LIBC_INTRIN_SPINLOCK_H_ */

View file

@ -16,5 +16,6 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/intrin/threaded.internal.h"
bool __threaded;

View file

@ -0,0 +1,10 @@
#ifndef COSMOPOLITAN_LIBC_INTRIN_THREADED_INTERNAL_H_
#define COSMOPOLITAN_LIBC_INTRIN_THREADED_INTERNAL_H_
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
extern bool __threaded;
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_INTRIN_THREADED_INTERNAL_H_ */

View file

@ -1,11 +1,16 @@
#ifndef COSMOPOLITAN_LIBC_RUNTIME_WINTHREAD_INTERNAL_H_
#define COSMOPOLITAN_LIBC_RUNTIME_WINTHREAD_INTERNAL_H_
#include "libc/intrin/tls.h"
#include "libc/runtime/runtime.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
struct WinThread {
int pid;
uint32_t tid;
int flags;
int *ctid;
int (*func)(void *);
void *arg;
};
extern int __winthread;