mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-07-20 09:30:31 +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
|
@ -43,6 +43,7 @@
|
|||
#include "libc/sysv/consts/sock.h"
|
||||
#include "libc/sysv/consts/sol.h"
|
||||
#include "libc/sysv/consts/tcp.h"
|
||||
#include "libc/thread/spawn.h"
|
||||
#include "libc/time/struct/tm.h"
|
||||
#include "libc/time/time.h"
|
||||
#include "net/http/http.h"
|
||||
|
@ -106,7 +107,7 @@ _Atomic(int) connections;
|
|||
_Atomic(int) closingtime;
|
||||
const char *volatile status;
|
||||
|
||||
int Worker(void *id) {
|
||||
int Worker(void *id, int tid) {
|
||||
int server, yes = 1;
|
||||
|
||||
// load balance incoming connections for port 8080 across all threads
|
||||
|
@ -273,8 +274,7 @@ void PrintStatus(void) {
|
|||
|
||||
int main(int argc, char *argv[]) {
|
||||
int i;
|
||||
char **tls;
|
||||
char **stack;
|
||||
struct spawn *th;
|
||||
uint32_t *hostips;
|
||||
// ShowCrashReports();
|
||||
|
||||
|
@ -293,36 +293,23 @@ int main(int argc, char *argv[]) {
|
|||
PORT);
|
||||
}
|
||||
|
||||
// spawn over 9,000 worker threads
|
||||
tls = 0;
|
||||
stack = 0;
|
||||
threads = argc > 1 ? atoi(argv[1]) : GetCpuCount();
|
||||
if ((1 <= threads && threads <= INT_MAX) &&
|
||||
(tls = malloc(threads * sizeof(*tls))) &&
|
||||
(stack = malloc(threads * sizeof(*stack)))) {
|
||||
for (i = 0; i < threads; ++i) {
|
||||
if ((tls[i] = __initialize_tls(malloc(64))) &&
|
||||
(stack[i] = mmap(0, GetStackSize(), PROT_READ | PROT_WRITE,
|
||||
MAP_STACK | MAP_ANONYMOUS, -1, 0)) != MAP_FAILED) {
|
||||
++workers;
|
||||
if (clone(Worker, stack[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)) == -1) {
|
||||
--workers;
|
||||
kprintf("error: clone(%d) failed %m\n", i);
|
||||
}
|
||||
} else {
|
||||
kprintf("error: mmap(%d) failed %m\n", i);
|
||||
}
|
||||
if (!(i % 500)) {
|
||||
PrintStatus();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if ((1 <= threads && threads <= 100000)) {
|
||||
kprintf("error: invalid number of threads\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// spawn over 9,000 worker threads
|
||||
th = calloc(threads, sizeof(*th));
|
||||
for (i = 0; i < threads; ++i) {
|
||||
++workers;
|
||||
if (_spawn(Worker, (void *)(intptr_t)i, th + i) == -1) {
|
||||
--workers;
|
||||
kprintf("error: _spawn(%d) failed %m\n", i);
|
||||
}
|
||||
if (!(i % 500)) {
|
||||
PrintStatus();
|
||||
}
|
||||
}
|
||||
|
||||
// wait for workers to terminate
|
||||
|
@ -335,17 +322,11 @@ int main(int argc, char *argv[]) {
|
|||
kprintf("\r\e[K");
|
||||
|
||||
// join the workers
|
||||
// this is how we guarantee stacks are safe to free
|
||||
if (tls && stack) {
|
||||
for (i = 0; i < threads; ++i) {
|
||||
_wait0((int *)(tls[i] + 0x38));
|
||||
munmap(stack[i], GetStackSize());
|
||||
free(tls[i]);
|
||||
}
|
||||
for (i = 0; i < threads; ++i) {
|
||||
_join(th + i);
|
||||
}
|
||||
|
||||
// clean up memory
|
||||
free(hostips);
|
||||
free(stack);
|
||||
free(tls);
|
||||
free(th);
|
||||
}
|
||||
|
|
|
@ -1,70 +0,0 @@
|
|||
#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/dce.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/log/log.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/thread/thread.h"
|
||||
#include "libc/time/time.h"
|
||||
|
||||
cthread_sem_t semaphore;
|
||||
_Thread_local int test_tls = 0x12345678;
|
||||
|
||||
static void *worker(void *arg) {
|
||||
int tid;
|
||||
cthread_t self;
|
||||
cthread_sem_signal(&semaphore);
|
||||
self = cthread_self();
|
||||
tid = self->tid;
|
||||
printf("[%p] %d -> %#x\n", self, tid, test_tls);
|
||||
if (test_tls != 0x12345678) {
|
||||
printf(".tdata test #2 failed\n");
|
||||
}
|
||||
return (void *)4;
|
||||
}
|
||||
|
||||
int main() {
|
||||
int rc, tid;
|
||||
void *exitcode;
|
||||
cthread_t self, thread;
|
||||
|
||||
if (IsWindows() || IsXnu()) {
|
||||
fprintf(stderr,
|
||||
"error: can't run example\n"
|
||||
"_Thread_local only works on Linux/FreeBSD/NetBSD/OpenBSD\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
self = cthread_self();
|
||||
tid = self->tid;
|
||||
printf("[%p] %d -> %#x\n", self, tid, test_tls);
|
||||
if (test_tls != 0x12345678) {
|
||||
printf(".tdata test #1 failed\n");
|
||||
}
|
||||
cthread_sem_init(&semaphore, 0);
|
||||
rc = cthread_create(&thread, NULL, &worker, NULL);
|
||||
if (rc == 0) {
|
||||
cthread_sem_wait(&semaphore, 0, NULL);
|
||||
printf("thread created: %p\n", thread);
|
||||
#if 1
|
||||
cthread_join(thread, &exitcode);
|
||||
#else
|
||||
exitcode = cthread_detach(thread);
|
||||
#endif
|
||||
cthread_sem_signal(&semaphore);
|
||||
cthread_sem_wait(&semaphore, 0, NULL);
|
||||
printf("thread joined: %p -> %p\n", thread, exitcode);
|
||||
} else {
|
||||
fprintf(stderr, "ERROR: thread could not be started: %d\n", rc);
|
||||
}
|
||||
return 0;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue