mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-06-30 00:08:30 +00:00
Make _Thread_local work across platforms
We now rewrite the binary image at runtime on Windows and XNU to change mov %fs:0,%reg instructions to use %gs instead. There's also simpler threading API introduced by this change and it's called _spawn() and _join(), which has replaced most clone() usage.
This commit is contained in:
parent
e4d6e263d4
commit
5f4f6b0e69
51 changed files with 808 additions and 1043 deletions
|
@ -40,6 +40,7 @@
|
|||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/testlib/hyperion.h"
|
||||
#include "libc/testlib/testlib.h"
|
||||
#include "libc/thread/spawn.h"
|
||||
#include "libc/time/time.h"
|
||||
|
||||
STATIC_YOINK("zip_uri_support");
|
||||
|
@ -51,9 +52,6 @@ void PullSomeZipFilesIntoLinkage(void) {
|
|||
gmtime(0);
|
||||
}
|
||||
|
||||
char *stack[THREADS];
|
||||
char tls[THREADS][64];
|
||||
|
||||
TEST(reservefd, testGrowthOfFdsDataStructure) {
|
||||
int i, n;
|
||||
struct rlimit rlim;
|
||||
|
@ -87,7 +85,7 @@ void OnSigAlrm(int sig, siginfo_t *si, ucontext_t *ctx) {
|
|||
close(fd); // can eintr which doesn't matter
|
||||
}
|
||||
|
||||
int Worker(void *p) {
|
||||
int Worker(void *p, int tid) {
|
||||
char buf[64];
|
||||
int i, rc, fd;
|
||||
for (i = 0; i < 64; ++i) {
|
||||
|
@ -111,6 +109,7 @@ int Worker(void *p) {
|
|||
|
||||
TEST(reservefd, tortureTest) {
|
||||
int i;
|
||||
struct spawn th[THREADS];
|
||||
struct sigaction oldsa;
|
||||
struct itimerval oldit;
|
||||
struct itimerval it = {{0, 10000}, {0, 100}};
|
||||
|
@ -119,17 +118,10 @@ TEST(reservefd, tortureTest) {
|
|||
// ASSERT_SYS(0, 0, sigaction(SIGALRM, &sa, &oldsa));
|
||||
// ASSERT_SYS(0, 0, setitimer(ITIMER_REAL, &it, &oldit));
|
||||
for (i = 0; i < THREADS; ++i) {
|
||||
clone(Worker,
|
||||
(stack[i] = mmap(0, GetStackSize(), PROT_READ | PROT_WRITE,
|
||||
MAP_STACK | MAP_ANONYMOUS, -1, 0)),
|
||||
GetStackSize(),
|
||||
CLONE_THREAD | CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND |
|
||||
CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID | CLONE_SETTLS,
|
||||
0, 0, __initialize_tls(tls[i]), sizeof(tls[i]),
|
||||
(int *)(tls[i] + 0x38));
|
||||
_spawn(Worker, 0, th + i);
|
||||
}
|
||||
for (i = 0; i < THREADS; ++i) {
|
||||
_wait0((int *)(tls[i] + 0x38));
|
||||
_join(th + i);
|
||||
}
|
||||
// EXPECT_SYS(0, 0, sigaction(SIGALRM, &oldsa, 0));
|
||||
// EXPECT_SYS(0, 0, setitimer(ITIMER_REAL, &oldit, 0));
|
||||
|
|
|
@ -42,6 +42,7 @@ TEST_LIBC_CALLS_DIRECTDEPS = \
|
|||
LIBC_STR \
|
||||
LIBC_STUBS \
|
||||
LIBC_SYSV \
|
||||
LIBC_THREAD \
|
||||
LIBC_TIME \
|
||||
LIBC_TESTLIB \
|
||||
LIBC_UNICODE \
|
||||
|
|
|
@ -43,7 +43,8 @@
|
|||
*/
|
||||
static uint64_t Rando(void) {
|
||||
uint64_t x;
|
||||
do x = lemur64();
|
||||
do
|
||||
x = lemur64();
|
||||
while (((x ^ READ64LE("!!!!!!!!")) - 0x0101010101010101) &
|
||||
~(x ^ READ64LE("!!!!!!!!")) & 0x8080808080808080);
|
||||
return x;
|
||||
|
@ -279,7 +280,7 @@ TEST(ksnprintf, testMisalignedPointer_wontFormat) {
|
|||
TEST(ksnprintf, testUnterminatedOverrun_truncatesAtPageBoundary) {
|
||||
char *m;
|
||||
char b[32];
|
||||
m = memset(mapanon(FRAMESIZE * 2), 1, FRAMESIZE);
|
||||
m = memset(_mapanon(FRAMESIZE * 2), 1, FRAMESIZE);
|
||||
EXPECT_SYS(0, 0, munmap(m + FRAMESIZE, FRAMESIZE));
|
||||
EXPECT_EQ(12, ksnprintf(b, 32, "%'s", m + FRAMESIZE - 3));
|
||||
EXPECT_STREQ("\\001\\001\\001", b);
|
||||
|
|
|
@ -41,32 +41,18 @@
|
|||
#include "libc/sysv/consts/rlimit.h"
|
||||
#include "libc/testlib/ezbench.h"
|
||||
#include "libc/testlib/testlib.h"
|
||||
#include "libc/thread/spawn.h"
|
||||
#include "libc/thread/thread.h"
|
||||
|
||||
#define THREADS 8
|
||||
#define ITERATIONS 512
|
||||
#define TLS_SIZE PAGESIZE
|
||||
|
||||
char *tls[THREADS];
|
||||
char *stack[THREADS];
|
||||
_Alignas(PAGESIZE) char tlsdata[THREADS * 3][TLS_SIZE];
|
||||
|
||||
int count;
|
||||
_Atomic(int) started;
|
||||
_Atomic(int) finished;
|
||||
_Alignas(64) char slock;
|
||||
pthread_mutex_t mylock;
|
||||
|
||||
__attribute__((__constructor__)) void init(void) {
|
||||
int i;
|
||||
__enable_tls();
|
||||
__enable_threads();
|
||||
for (i = 0; i < THREADS; ++i) {
|
||||
CHECK_NE(-1, mprotect(tlsdata[i * 3 + 0], TLS_SIZE, PROT_NONE));
|
||||
tls[i] = tlsdata[i * 3 + 1];
|
||||
CHECK_NE(-1, mprotect(tlsdata[i * 3 + 2], TLS_SIZE, PROT_NONE));
|
||||
}
|
||||
}
|
||||
struct spawn th[THREADS];
|
||||
|
||||
TEST(pthread_mutex_lock, normal) {
|
||||
pthread_mutex_t lock;
|
||||
|
@ -116,7 +102,7 @@ TEST(pthread_mutex_lock, errorcheck) {
|
|||
__assert_disable = false;
|
||||
}
|
||||
|
||||
int MutexWorker(void *p) {
|
||||
int MutexWorker(void *p, int tid) {
|
||||
int i;
|
||||
++started;
|
||||
for (i = 0; i < ITERATIONS; ++i) {
|
||||
|
@ -124,7 +110,6 @@ int MutexWorker(void *p) {
|
|||
++count;
|
||||
pthread_mutex_unlock(&mylock);
|
||||
}
|
||||
ASSERT_NE(0, (int *)(tls[(intptr_t)p] + 0x38));
|
||||
++finished;
|
||||
return 0;
|
||||
}
|
||||
|
@ -140,29 +125,14 @@ TEST(pthread_mutex_lock, contention) {
|
|||
started = 0;
|
||||
finished = 0;
|
||||
for (i = 0; i < THREADS; ++i) {
|
||||
ASSERT_NE(MAP_FAILED,
|
||||
(stack[i] = mmap(0, GetStackSize(), PROT_READ | PROT_WRITE,
|
||||
MAP_STACK | MAP_ANONYMOUS, -1, 0)));
|
||||
ASSERT_NE(-1, clone(MutexWorker, stack[i], GetStackSize(),
|
||||
CLONE_THREAD | CLONE_VM | CLONE_FS | CLONE_FILES |
|
||||
CLONE_SIGHAND | CLONE_CHILD_SETTID |
|
||||
CLONE_CHILD_CLEARTID | CLONE_SETTLS,
|
||||
(void *)(intptr_t)i, 0, __initialize_tls(tls[i]),
|
||||
TLS_SIZE, (int *)(tls[i] + 0x38)));
|
||||
ASSERT_SYS(0, 0, _spawn(MutexWorker, (void *)(intptr_t)i, th + i));
|
||||
}
|
||||
for (i = 0; i < THREADS; ++i) {
|
||||
_wait0((int *)(tls[i] + 0x38));
|
||||
ASSERT_EQ(0, *(int *)(tls[i] + 0x38));
|
||||
}
|
||||
for (i = 0; i < THREADS; ++i) {
|
||||
ASSERT_EQ(0, *(int *)(tls[i] + 0x38));
|
||||
ASSERT_SYS(0, 0, _join(th + i));
|
||||
}
|
||||
EXPECT_EQ(THREADS, started);
|
||||
EXPECT_EQ(THREADS, finished);
|
||||
EXPECT_EQ(THREADS * ITERATIONS, count);
|
||||
for (i = 0; i < THREADS; ++i) {
|
||||
ASSERT_SYS(0, 0, munmap(stack[i], GetStackSize()));
|
||||
}
|
||||
EXPECT_EQ(0, pthread_mutex_destroy(&mylock));
|
||||
}
|
||||
|
||||
|
@ -177,29 +147,14 @@ TEST(pthread_mutex_lock, rcontention) {
|
|||
started = 0;
|
||||
finished = 0;
|
||||
for (i = 0; i < THREADS; ++i) {
|
||||
ASSERT_NE(MAP_FAILED,
|
||||
(stack[i] = mmap(0, GetStackSize(), PROT_READ | PROT_WRITE,
|
||||
MAP_STACK | MAP_ANONYMOUS, -1, 0)));
|
||||
ASSERT_NE(-1, clone(MutexWorker, stack[i], GetStackSize(),
|
||||
CLONE_THREAD | CLONE_VM | CLONE_FS | CLONE_FILES |
|
||||
CLONE_SIGHAND | CLONE_CHILD_SETTID |
|
||||
CLONE_CHILD_CLEARTID | CLONE_SETTLS,
|
||||
(void *)(intptr_t)i, 0, __initialize_tls(tls[i]),
|
||||
TLS_SIZE, (int *)(tls[i] + 0x38)));
|
||||
ASSERT_NE(-1, _spawn(MutexWorker, (void *)(intptr_t)i, th + i));
|
||||
}
|
||||
for (i = 0; i < THREADS; ++i) {
|
||||
_wait0((int *)(tls[i] + 0x38));
|
||||
ASSERT_EQ(0, *(int *)(tls[i] + 0x38));
|
||||
}
|
||||
for (i = 0; i < THREADS; ++i) {
|
||||
ASSERT_EQ(0, *(int *)(tls[i] + 0x38));
|
||||
_join(th + i);
|
||||
}
|
||||
EXPECT_EQ(THREADS, started);
|
||||
EXPECT_EQ(THREADS, finished);
|
||||
EXPECT_EQ(THREADS * ITERATIONS, count);
|
||||
for (i = 0; i < THREADS; ++i) {
|
||||
ASSERT_SYS(0, 0, munmap(stack[i], GetStackSize()));
|
||||
}
|
||||
EXPECT_EQ(0, pthread_mutex_destroy(&mylock));
|
||||
}
|
||||
|
||||
|
@ -214,33 +169,18 @@ TEST(pthread_mutex_lock, econtention) {
|
|||
started = 0;
|
||||
finished = 0;
|
||||
for (i = 0; i < THREADS; ++i) {
|
||||
ASSERT_NE(MAP_FAILED,
|
||||
(stack[i] = mmap(0, GetStackSize(), PROT_READ | PROT_WRITE,
|
||||
MAP_STACK | MAP_ANONYMOUS, -1, 0)));
|
||||
ASSERT_NE(-1, clone(MutexWorker, stack[i], GetStackSize(),
|
||||
CLONE_THREAD | CLONE_VM | CLONE_FS | CLONE_FILES |
|
||||
CLONE_SIGHAND | CLONE_CHILD_SETTID |
|
||||
CLONE_CHILD_CLEARTID | CLONE_SETTLS,
|
||||
(void *)(intptr_t)i, 0, __initialize_tls(tls[i]),
|
||||
TLS_SIZE, (int *)(tls[i] + 0x38)));
|
||||
ASSERT_NE(-1, _spawn(MutexWorker, (void *)(intptr_t)i, th + i));
|
||||
}
|
||||
for (i = 0; i < THREADS; ++i) {
|
||||
_wait0((int *)(tls[i] + 0x38));
|
||||
ASSERT_EQ(0, *(int *)(tls[i] + 0x38));
|
||||
}
|
||||
for (i = 0; i < THREADS; ++i) {
|
||||
ASSERT_EQ(0, *(int *)(tls[i] + 0x38));
|
||||
_join(th + i);
|
||||
}
|
||||
EXPECT_EQ(THREADS, started);
|
||||
EXPECT_EQ(THREADS, finished);
|
||||
EXPECT_EQ(THREADS * ITERATIONS, count);
|
||||
for (i = 0; i < THREADS; ++i) {
|
||||
ASSERT_SYS(0, 0, munmap(stack[i], GetStackSize()));
|
||||
}
|
||||
EXPECT_EQ(0, pthread_mutex_destroy(&mylock));
|
||||
}
|
||||
|
||||
int SpinlockWorker(void *p) {
|
||||
int SpinlockWorker(void *p, int tid) {
|
||||
int i;
|
||||
++started;
|
||||
for (i = 0; i < ITERATIONS; ++i) {
|
||||
|
@ -248,7 +188,6 @@ int SpinlockWorker(void *p) {
|
|||
++count;
|
||||
_spunlock(&slock);
|
||||
}
|
||||
ASSERT_NE(0, (int *)(tls[(intptr_t)p] + 0x38));
|
||||
++finished;
|
||||
return 0;
|
||||
}
|
||||
|
@ -259,25 +198,14 @@ TEST(_spinlock, contention) {
|
|||
started = 0;
|
||||
finished = 0;
|
||||
for (i = 0; i < THREADS; ++i) {
|
||||
ASSERT_NE(MAP_FAILED,
|
||||
(stack[i] = mmap(0, GetStackSize(), PROT_READ | PROT_WRITE,
|
||||
MAP_STACK | MAP_ANONYMOUS, -1, 0)));
|
||||
ASSERT_NE(-1, clone(SpinlockWorker, stack[i], GetStackSize(),
|
||||
CLONE_THREAD | CLONE_VM | CLONE_FS | CLONE_FILES |
|
||||
CLONE_SIGHAND | CLONE_CHILD_SETTID |
|
||||
CLONE_CHILD_CLEARTID | CLONE_SETTLS,
|
||||
(void *)(intptr_t)i, 0, __initialize_tls(tls[i]),
|
||||
TLS_SIZE, (int *)(tls[i] + 0x38)));
|
||||
ASSERT_NE(-1, _spawn(SpinlockWorker, (void *)(intptr_t)i, th + i));
|
||||
}
|
||||
for (i = 0; i < THREADS; ++i) {
|
||||
_wait0((int *)(tls[i] + 0x38));
|
||||
_join(th + i);
|
||||
}
|
||||
EXPECT_EQ(THREADS, started);
|
||||
EXPECT_EQ(THREADS, finished);
|
||||
EXPECT_EQ(THREADS * ITERATIONS, count);
|
||||
for (i = 0; i < THREADS; ++i) {
|
||||
ASSERT_SYS(0, 0, munmap(stack[i], GetStackSize()));
|
||||
}
|
||||
}
|
||||
|
||||
BENCH(pthread_mutex_lock, bench) {
|
||||
|
|
|
@ -35,6 +35,7 @@ TEST_LIBC_INTRIN_DIRECTDEPS = \
|
|||
LIBC_STR \
|
||||
LIBC_STUBS \
|
||||
LIBC_SYSV \
|
||||
LIBC_THREAD \
|
||||
LIBC_TESTLIB \
|
||||
LIBC_TINYMATH \
|
||||
LIBC_UNICODE \
|
||||
|
|
|
@ -78,7 +78,7 @@ TEST(lz4, zoneFileGmt) {
|
|||
size_t mapsize, gmtsize;
|
||||
char *mapping, *gmtdata;
|
||||
lz4decode((gmtdata = lz4decode(
|
||||
(mapping = mapanon(
|
||||
(mapping = _mapanon(
|
||||
(mapsize = roundup(
|
||||
LZ4_FRAME_BLOCKCONTENTSIZE(lz4check(dict.addr)) +
|
||||
(gmtsize = LZ4_FRAME_BLOCKCONTENTSIZE(
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
#include "libc/sysv/consts/sa.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/testlib/testlib.h"
|
||||
#include "libc/thread/spawn.h"
|
||||
#include "libc/thread/thread.h"
|
||||
#include "libc/time/time.h"
|
||||
|
||||
|
@ -54,7 +55,7 @@ dontinline void Generate(int i) {
|
|||
A[i] = rand64();
|
||||
}
|
||||
|
||||
int Thrasher(void *arg) {
|
||||
int Thrasher(void *arg, int tid) {
|
||||
int i, id = (intptr_t)arg;
|
||||
while (!atomic_load(&ready)) {
|
||||
cthread_memory_wait32(&ready, 0, 0);
|
||||
|
@ -83,9 +84,8 @@ TEST(rand64, testLcg_doesntProduceIdenticalValues) {
|
|||
TEST(rand64, testThreadSafety_doesntProduceIdenticalValues) {
|
||||
int i, j, rc, ws;
|
||||
sigset_t ss, oldss;
|
||||
char *tls[THREADS];
|
||||
void *stacks[THREADS];
|
||||
struct sigaction oldsa;
|
||||
struct spawn th[THREADS];
|
||||
struct sigaction sa = {.sa_handler = OnChld, .sa_flags = SA_RESTART};
|
||||
EXPECT_NE(-1, sigaction(SIGCHLD, &sa, &oldsa));
|
||||
bzero(A, sizeof(A));
|
||||
|
@ -94,25 +94,12 @@ TEST(rand64, testThreadSafety_doesntProduceIdenticalValues) {
|
|||
EXPECT_EQ(0, sigprocmask(SIG_BLOCK, &ss, &oldss));
|
||||
ready = false;
|
||||
for (i = 0; i < THREADS; ++i) {
|
||||
tls[i] = __initialize_tls(calloc(1, 64));
|
||||
stacks[i] = mmap(0, GetStackSize(), PROT_READ | PROT_WRITE,
|
||||
MAP_STACK | MAP_ANONYMOUS, -1, 0);
|
||||
ASSERT_NE(
|
||||
-1,
|
||||
clone(Thrasher, stacks[i], GetStackSize(),
|
||||
CLONE_THREAD | CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND |
|
||||
CLONE_SETTLS | CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID,
|
||||
(void *)(intptr_t)i, 0, tls[i], 64, (int *)(tls[i] + 0x38)));
|
||||
ASSERT_SYS(0, 0, _spawn(Thrasher, (void *)(intptr_t)i, th + i));
|
||||
}
|
||||
atomic_store(&ready, 1);
|
||||
cthread_memory_wake32(&ready, INT_MAX);
|
||||
for (i = 0; i < THREADS; ++i) {
|
||||
while ((j = atomic_load((uint32_t *)(tls[i] + 0x38)))) {
|
||||
// FUTEX_WAIT_PRIVATE makes it hang
|
||||
cthread_memory_wait32((int *)(tls[i] + 0x38), j, 0);
|
||||
}
|
||||
EXPECT_SYS(0, 0, munmap(stacks[i], GetStackSize()));
|
||||
free(tls[i]);
|
||||
ASSERT_SYS(0, 0, _join(th + i));
|
||||
}
|
||||
sigaction(SIGCHLD, &oldsa, 0);
|
||||
sigprocmask(SIG_BLOCK, &oldss, 0);
|
||||
|
|
|
@ -23,9 +23,11 @@
|
|||
#include "libc/intrin/spinlock.h"
|
||||
#include "libc/intrin/wait0.internal.h"
|
||||
#include "libc/log/backtrace.internal.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/nexgen32e/gettls.h"
|
||||
#include "libc/nexgen32e/nexgen32e.h"
|
||||
#include "libc/runtime/internal.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/runtime/stack.h"
|
||||
#include "libc/runtime/symbols.internal.h"
|
||||
|
@ -36,10 +38,10 @@
|
|||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/testlib/ezbench.h"
|
||||
#include "libc/testlib/testlib.h"
|
||||
#include "libc/thread/spawn.h"
|
||||
#include "libc/time/time.h"
|
||||
|
||||
char *stack, *tls;
|
||||
int x, me, tid, *childetid;
|
||||
int x, me, tid;
|
||||
_Atomic(int) thechilde;
|
||||
|
||||
__attribute__((__constructor__)) static void init(void) {
|
||||
|
@ -47,47 +49,38 @@ __attribute__((__constructor__)) static void init(void) {
|
|||
errno = 0;
|
||||
}
|
||||
|
||||
void *__initialize_tls(char tib[64]) {
|
||||
if (tib) {
|
||||
*(intptr_t *)(tib + 0x00) = (intptr_t)tib;
|
||||
*(intptr_t *)(tib + 0x30) = (intptr_t)tib;
|
||||
*(int *)(tib + 0x38) = -1; // tid
|
||||
*(int *)(tib + 0x3c) = 0;
|
||||
}
|
||||
return tib;
|
||||
}
|
||||
|
||||
void SetUp(void) {
|
||||
x = 0;
|
||||
me = gettid();
|
||||
tls = calloc(1, 64);
|
||||
__initialize_tls(tls);
|
||||
*(int *)(tls + 0x3c) = 31337;
|
||||
childetid = (int *)(tls + 0x38);
|
||||
ASSERT_NE(MAP_FAILED, (stack = mmap(0, GetStackSize(), PROT_READ | PROT_WRITE,
|
||||
MAP_STACK | MAP_ANONYMOUS, -1, 0)));
|
||||
}
|
||||
|
||||
void TearDown(void) {
|
||||
EXPECT_SYS(0, 0, munmap(stack, GetStackSize()));
|
||||
free(tls);
|
||||
}
|
||||
|
||||
int DoNothing(void *arg) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// TEST ERROR NUMBERS
|
||||
|
||||
TEST(clone, testNullFunc_raisesEinval) {
|
||||
EXPECT_SYS(EINVAL, -1,
|
||||
clone(0, stack, GetStackSize(),
|
||||
CLONE_THREAD | CLONE_VM | CLONE_FS | CLONE_FILES |
|
||||
CLONE_SIGHAND | CLONE_SETTLS,
|
||||
0, 0, tls, 64, 0));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// TEST THREADS WORK
|
||||
|
||||
int CloneTest1(void *arg) {
|
||||
int CloneTest1(void *arg, int tid) {
|
||||
intptr_t rsp, top, bot;
|
||||
CheckStackIsAligned();
|
||||
// PrintBacktraceUsingSymbols(2, __builtin_frame_address(0),
|
||||
// GetSymbolTable());
|
||||
rsp = (intptr_t)__builtin_frame_address(0);
|
||||
bot = (intptr_t)stack;
|
||||
bot = ROUNDDOWN((intptr_t)rsp, GetStackSize());
|
||||
top = bot + GetStackSize();
|
||||
ASSERT_GT(rsp, bot); // check we're on stack
|
||||
ASSERT_LT(rsp, top); // check we're on stack
|
||||
|
@ -95,28 +88,16 @@ int CloneTest1(void *arg) {
|
|||
ASSERT_TRUE(IS2POW(GetStackSize()));
|
||||
ASSERT_EQ(0, bot & (GetStackSize() - 1));
|
||||
x = 42;
|
||||
if (!IsWindows()) {
|
||||
ASSERT_EQ(31337, errno);
|
||||
} else {
|
||||
errno = 31337;
|
||||
ASSERT_EQ(31337, errno);
|
||||
}
|
||||
ASSERT_EQ(23, (intptr_t)arg);
|
||||
ASSERT_NE(gettid(), getpid());
|
||||
ASSERT_EQ(gettid(), *childetid); // CLONE_CHILD_SETTID
|
||||
return 0;
|
||||
}
|
||||
|
||||
TEST(clone, test1) {
|
||||
int ptid = 0;
|
||||
*childetid = -1;
|
||||
ASSERT_NE(-1, (tid = clone(CloneTest1, stack, GetStackSize(),
|
||||
CLONE_THREAD | CLONE_VM | CLONE_FS | CLONE_FILES |
|
||||
CLONE_SIGHAND | CLONE_PARENT_SETTID |
|
||||
CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID |
|
||||
CLONE_SETTLS,
|
||||
(void *)23, &ptid, tls, 64, childetid)));
|
||||
_wait0(childetid); // CLONE_CHILD_CLEARTID
|
||||
struct spawn th;
|
||||
ASSERT_SYS(0, 0, _spawn(CloneTest1, (void *)23, &th));
|
||||
ASSERT_SYS(0, 0, _join(&th));
|
||||
ASSERT_NE(gettid(), tid);
|
||||
ASSERT_EQ(tid, ptid);
|
||||
ASSERT_EQ(42, x);
|
||||
|
@ -132,7 +113,7 @@ TEST(clone, test1) {
|
|||
|
||||
_Atomic(int) sysbarrier;
|
||||
|
||||
int CloneTestSys(void *arg) {
|
||||
int CloneTestSys(void *arg, int tid) {
|
||||
int i, id = (intptr_t)arg;
|
||||
CheckStackIsAligned();
|
||||
while (!sysbarrier) asm("pause");
|
||||
|
@ -165,25 +146,14 @@ int CloneTestSys(void *arg) {
|
|||
|
||||
TEST(clone, tlsSystemCallsErrno_wontClobberMainThreadBecauseTls) {
|
||||
int i;
|
||||
char *tls[8], *stack[8];
|
||||
struct spawn th[8];
|
||||
ASSERT_EQ(0, errno);
|
||||
for (i = 0; i < 8; ++i) {
|
||||
tls[i] = __initialize_tls(malloc(64));
|
||||
stack[i] = mmap(0, GetStackSize(), PROT_READ | PROT_WRITE,
|
||||
MAP_STACK | MAP_ANONYMOUS, -1, 0);
|
||||
ASSERT_NE(
|
||||
-1,
|
||||
(tid = clone(
|
||||
CloneTestSys, stack[i], GetStackSize(),
|
||||
CLONE_THREAD | CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND |
|
||||
CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID | CLONE_SETTLS,
|
||||
(void *)(intptr_t)i, 0, tls[i], 64, (int *)(tls[i] + 0x38))));
|
||||
ASSERT_SYS(0, 0, _spawn(CloneTestSys, (void *)(intptr_t)i, th + i));
|
||||
}
|
||||
sysbarrier = 1;
|
||||
for (i = 0; i < 8; ++i) {
|
||||
_wait0((int *)(tls[i] + 0x38));
|
||||
free(tls[i]);
|
||||
munmap(stack[i], GetStackSize());
|
||||
ASSERT_SYS(0, 0, _join(th + i));
|
||||
}
|
||||
ASSERT_EQ(0, errno);
|
||||
}
|
||||
|
|
|
@ -119,7 +119,7 @@ TEST(mprotect, testSegfault_writeToReadOnlyAnonymous) {
|
|||
}
|
||||
|
||||
TEST(mprotect, testExecOnly_canExecute) {
|
||||
char *p = mapanon(FRAMESIZE);
|
||||
char *p = _mapanon(FRAMESIZE);
|
||||
void (*f)(void) = (void *)p;
|
||||
p[0] = 0xC3; // RET
|
||||
ASSERT_SYS(0, 0, mprotect(p, FRAMESIZE, PROT_EXEC | PROT_READ));
|
||||
|
|
|
@ -37,6 +37,7 @@ TEST_LIBC_RUNTIME_DIRECTDEPS = \
|
|||
LIBC_STR \
|
||||
LIBC_STUBS \
|
||||
LIBC_SYSV \
|
||||
LIBC_THREAD \
|
||||
LIBC_TESTLIB \
|
||||
LIBC_TINYMATH \
|
||||
LIBC_UNICODE \
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include "libc/sysv/consts/map.h"
|
||||
#include "libc/sysv/consts/prot.h"
|
||||
#include "libc/testlib/testlib.h"
|
||||
#include "libc/thread/spawn.h"
|
||||
#include "libc/x/x.h"
|
||||
|
||||
#define THREADS 32
|
||||
|
@ -46,10 +47,9 @@ union Dub {
|
|||
double x;
|
||||
};
|
||||
|
||||
char *stack[THREADS];
|
||||
char tls[THREADS][64];
|
||||
struct spawn th[THREADS];
|
||||
|
||||
int Worker(void *p) {
|
||||
int Worker(void *p, int tid) {
|
||||
int i;
|
||||
char str[64];
|
||||
for (i = 0; i < 256; ++i) {
|
||||
|
@ -63,17 +63,10 @@ int Worker(void *p) {
|
|||
TEST(dtoa, test) {
|
||||
int i;
|
||||
for (i = 0; i < THREADS; ++i) {
|
||||
clone(Worker,
|
||||
(stack[i] = mmap(0, GetStackSize(), PROT_READ | PROT_WRITE,
|
||||
MAP_STACK | MAP_ANONYMOUS, -1, 0)),
|
||||
GetStackSize(),
|
||||
CLONE_THREAD | CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND |
|
||||
CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID | CLONE_SETTLS,
|
||||
0, 0, __initialize_tls(tls[i]), sizeof(tls[i]),
|
||||
(int *)(tls[i] + 0x38));
|
||||
_spawn(Worker, 0, th + i);
|
||||
}
|
||||
for (i = 0; i < THREADS; ++i) {
|
||||
_wait0((int *)(tls[i] + 0x38));
|
||||
_join(th + i);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -37,6 +37,7 @@ TEST_LIBC_STDIO_DIRECTDEPS = \
|
|||
LIBC_SYSV \
|
||||
LIBC_TINYMATH \
|
||||
LIBC_TESTLIB \
|
||||
LIBC_THREAD \
|
||||
LIBC_TIME \
|
||||
LIBC_LOG \
|
||||
LIBC_UNICODE \
|
||||
|
|
|
@ -1,100 +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. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/runtime/internal.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/testlib/testlib.h"
|
||||
#include "libc/thread/thread.h"
|
||||
#include "libc/time/time.h"
|
||||
|
||||
static _Thread_local int tdata = 31337;
|
||||
static _Thread_local int tbss;
|
||||
|
||||
static void *ReturnArg(void *arg) {
|
||||
return arg;
|
||||
}
|
||||
|
||||
TEST(cthread_create, testJoinDeadlock) {
|
||||
ASSERT_SYS(0, EDEADLK, cthread_join(cthread_self(), 0));
|
||||
}
|
||||
|
||||
TEST(cthread_create, testCreateReturnJoin) {
|
||||
if (IsOpenbsd()) return; // TODO(jart): flakes
|
||||
void *exitcode;
|
||||
cthread_t thread;
|
||||
ASSERT_EQ(0, cthread_create(&thread, 0, ReturnArg, ReturnArg));
|
||||
ASSERT_EQ(0, cthread_join(thread, &exitcode));
|
||||
ASSERT_EQ(ReturnArg, exitcode);
|
||||
}
|
||||
|
||||
static void *ExitArg(void *arg) {
|
||||
cthread_exit(arg);
|
||||
}
|
||||
|
||||
TEST(cthread_create, testCreateExitJoin) {
|
||||
if (IsOpenbsd()) return; // TODO(jart): flakes
|
||||
void *exitcode;
|
||||
cthread_t thread;
|
||||
ASSERT_EQ(0, cthread_create(&thread, 0, ExitArg, (void *)-31337));
|
||||
ASSERT_EQ(0, cthread_join(thread, &exitcode));
|
||||
ASSERT_EQ((void *)-31337, exitcode);
|
||||
}
|
||||
|
||||
TEST(gcctls, size) {
|
||||
if (IsXnu()) return; // TODO(jart): codemorph
|
||||
if (IsWindows()) return; // TODO(jart): codemorph
|
||||
if (IsOpenbsd()) return; // TODO(jart): flakes
|
||||
// schlep in .zip section too
|
||||
// make sure executable isn't too huge
|
||||
size_t size;
|
||||
int64_t x = 0;
|
||||
gmtime(&x);
|
||||
ASSERT_LT((uintptr_t)_tls_size, 8192);
|
||||
size = GetFileSize(GetProgramExecutableName());
|
||||
if (IsTiny()) {
|
||||
ASSERT_LT(size, 200 * 1024);
|
||||
} else if (IsModeDbg() || IsAsan()) {
|
||||
ASSERT_LT(size, 4 * 1024 * 1024);
|
||||
} else {
|
||||
ASSERT_LT(size, 500 * 1024);
|
||||
}
|
||||
}
|
||||
|
||||
static void *TlsWorker(void *arg) {
|
||||
ASSERT_EQ(31337, tdata);
|
||||
ASSERT_EQ(0, tbss);
|
||||
return 0;
|
||||
}
|
||||
|
||||
TEST(gcctls, worksAndIsNonInheritable) {
|
||||
if (IsXnu()) return; // TODO(jart): codemorph
|
||||
if (IsWindows()) return; // TODO(jart): codemorph
|
||||
if (IsOpenbsd()) return; // TODO(jart): flakes
|
||||
void *exitcode;
|
||||
cthread_t thread;
|
||||
ASSERT_EQ(tdata, 31337);
|
||||
ASSERT_EQ(tbss, 0);
|
||||
tdata = 1337;
|
||||
tbss = 1337;
|
||||
ASSERT_EQ(0, cthread_create(&thread, 0, TlsWorker, (void *)-31337));
|
||||
ASSERT_EQ(0, cthread_join(thread, &exitcode));
|
||||
ASSERT_EQ(NULL, exitcode);
|
||||
}
|
|
@ -16,22 +16,15 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/nexgen32e/threaded.h"
|
||||
#include "libc/testlib/ezbench.h"
|
||||
#include "libc/testlib/testlib.h"
|
||||
|
||||
char tib[64];
|
||||
_Thread_local char x;
|
||||
static _Thread_local char y;
|
||||
|
||||
TEST(gettid, test) {
|
||||
if (IsLinux()) EXPECT_EQ(getpid(), gettid());
|
||||
if (IsNetbsd()) EXPECT_EQ(1, gettid());
|
||||
char ha(void) {
|
||||
++y;
|
||||
return x;
|
||||
}
|
||||
|
||||
BENCH(gettid, bench) {
|
||||
int gettid_(void) asm("gettid");
|
||||
EZBENCH2("gettid (single threaded)", donothing, gettid());
|
||||
__install_tls(__initialize_tls(tib));
|
||||
EZBENCH2("gettid (tls enabled)", donothing, gettid());
|
||||
char ya(void) {
|
||||
return y;
|
||||
}
|
|
@ -16,25 +16,42 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/assert.h"
|
||||
#include "libc/bits/atomic.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/nexgen32e/gettls.h"
|
||||
#include "libc/nexgen32e/threaded.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/testlib/testlib.h"
|
||||
#include "libc/thread/spawn.h"
|
||||
#include "libc/thread/thread.h"
|
||||
|
||||
static char tib[64];
|
||||
#define N 128
|
||||
|
||||
TEST(tls, test) {
|
||||
errno = 31337;
|
||||
EXPECT_EQ(31337, errno);
|
||||
EXPECT_EQ(&__errno, __errno_location());
|
||||
__initialize_tls(tib);
|
||||
*(int *)((char *)tib + 0x38) = gettid();
|
||||
*(int *)((char *)tib + 0x3c) = __errno;
|
||||
__install_tls(tib);
|
||||
EXPECT_EQ(31337, errno);
|
||||
EXPECT_EQ(tib, __get_tls());
|
||||
EXPECT_EQ(tib, __get_tls_inline());
|
||||
EXPECT_EQ(tib + 0x3c, (char *)__errno_location());
|
||||
struct spawn t[N];
|
||||
_Atomic(int) itworked;
|
||||
_Thread_local int var;
|
||||
|
||||
int Worker(void *arg, int tid) {
|
||||
int i = (long)arg;
|
||||
ASSERT_EQ(0, var++);
|
||||
ASSERT_EQ(gettid(), tid);
|
||||
ASSERT_EQ(1, var++);
|
||||
ASSERT_EQ(sys_gettid(), tid);
|
||||
ASSERT_EQ(2, var++);
|
||||
itworked++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
TEST(_spawn, test) {
|
||||
long i;
|
||||
for (i = 0; i < N; ++i) EXPECT_SYS(0, 0, _spawn(Worker, (void *)i, t + i));
|
||||
for (i = 0; i < N; ++i) EXPECT_SYS(0, 0, _join(t + i));
|
||||
for (i = 0; i < N; ++i) EXPECT_SYS(0, 0, _join(t + i));
|
||||
EXPECT_EQ(N, itworked);
|
||||
}
|
||||
|
||||
__attribute__((__constructor__)) static void init(void) {
|
||||
pledge("stdio rpath thread", 0);
|
||||
errno = 0;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue