mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-08-07 10:20:29 +00:00
Proper thread creation and exit
This commit is contained in:
parent
149b1901b0
commit
fb2dd3ff89
11 changed files with 305 additions and 141 deletions
|
@ -8,7 +8,8 @@
|
||||||
╚─────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────*/
|
||||||
#endif
|
#endif
|
||||||
#include "libc/stdio/stdio.h"
|
#include "libc/stdio/stdio.h"
|
||||||
#include "libc/thread/thread.h"
|
#include "libc/thread/create.h"
|
||||||
|
#include "libc/time/time.h"
|
||||||
|
|
||||||
int worker(void* arg) {
|
int worker(void* arg) {
|
||||||
(void)arg;
|
(void)arg;
|
||||||
|
@ -16,13 +17,13 @@ int worker(void* arg) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
thread_descriptor_t* thread = allocate_thread();
|
cthread_t thread;
|
||||||
|
int rc = cthread_create(&thread, NULL, &worker, NULL);
|
||||||
if (thread) {
|
if (rc == 0) {
|
||||||
long rc = start_thread(thread, &worker, NULL);
|
printf("thread created: %p\n", thread);
|
||||||
printf("thread created: %ld\n", rc);
|
sleep(1000);
|
||||||
} else {
|
} else {
|
||||||
printf("ERROR: thread stack could not be allocated\n");
|
printf("ERROR: thread could not be started: %d\n", rc);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
67
libc/thread/attr.c
Normal file
67
libc/thread/attr.c
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
/*-*- 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/thread/attr.h"
|
||||||
|
#include "libc/errno.h"
|
||||||
|
|
||||||
|
#define MIN_STACKSIZE (8*PAGESIZE)
|
||||||
|
#define MIN_GUARDSIZE PAGESIZE
|
||||||
|
|
||||||
|
// CTOR/DTOR
|
||||||
|
int cthread_attr_init(cthread_attr_t* attr) {
|
||||||
|
attr->stacksize = 1024*PAGESIZE; // 4 MiB
|
||||||
|
attr->guardsize = 16*PAGESIZE; // 64 KiB
|
||||||
|
attr->mode = CTHREAD_CREATE_JOINABLE;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
int cthread_attr_destroy(cthread_attr_t* attr) {
|
||||||
|
(void)attr;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// stacksize
|
||||||
|
int cthread_attr_setstacksize(cthread_attr_t* attr, size_t size) {
|
||||||
|
if (size & (PAGESIZE-1)) return EINVAL;
|
||||||
|
if (size < MIN_STACKSIZE) return EINVAL;
|
||||||
|
attr->stacksize = size;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
size_t cthread_attr_getstacksize(const cthread_attr_t* attr) {
|
||||||
|
return attr->stacksize;
|
||||||
|
}
|
||||||
|
|
||||||
|
// guardsize
|
||||||
|
int cthread_attr_setguardsize(cthread_attr_t* attr, size_t size) {
|
||||||
|
if (size & (PAGESIZE-1)) return EINVAL;
|
||||||
|
if (size < MIN_GUARDSIZE) return EINVAL;
|
||||||
|
attr->guardsize = size;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
size_t cthread_attr_getguardsize(const cthread_attr_t* attr) {
|
||||||
|
return attr->guardsize;
|
||||||
|
}
|
||||||
|
|
||||||
|
// detachstate
|
||||||
|
int cthread_attr_setdetachstate(cthread_attr_t* attr, int mode) {
|
||||||
|
if (mode & ~(CTHREAD_CREATE_JOINABLE | CTHREAD_CREATE_DETACHED)) return EINVAL;
|
||||||
|
attr->mode = mode;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
int cthread_attr_getdetachstate(const cthread_attr_t* attr) {
|
||||||
|
return attr->mode;
|
||||||
|
}
|
36
libc/thread/attr.h
Normal file
36
libc/thread/attr.h
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
#ifndef COSMOPOLITAN_LIBC_THREAD_ATTR_H_
|
||||||
|
#define COSMOPOLITAN_LIBC_THREAD_ATTR_H_
|
||||||
|
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||||
|
COSMOPOLITAN_C_START_
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @fileoverview cosmopolitan thread attributes
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define CTHREAD_CREATE_DETACHED 1
|
||||||
|
#define CTHREAD_CREATE_JOINABLE 0
|
||||||
|
|
||||||
|
typedef struct cthread_attr_t {
|
||||||
|
size_t stacksize, guardsize;
|
||||||
|
int mode;
|
||||||
|
} cthread_attr_t;
|
||||||
|
|
||||||
|
// CTOR/DTOR
|
||||||
|
int cthread_attr_init(cthread_attr_t*);
|
||||||
|
int cthread_attr_destroy(cthread_attr_t*);
|
||||||
|
|
||||||
|
// stacksize
|
||||||
|
int cthread_attr_setstacksize(cthread_attr_t*, size_t);
|
||||||
|
size_t thread_attr_getstacksize(const cthread_attr_t*);
|
||||||
|
|
||||||
|
// guardsize
|
||||||
|
int cthread_attr_setguardsize(cthread_attr_t*, size_t);
|
||||||
|
size_t cthread_attr_getguardsize(const cthread_attr_t*);
|
||||||
|
|
||||||
|
// detachstate
|
||||||
|
int cthread_attr_setdetachstate(cthread_attr_t*, int);
|
||||||
|
int cthread_attr_getdetachstate(const cthread_attr_t*);
|
||||||
|
|
||||||
|
COSMOPOLITAN_C_END_
|
||||||
|
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||||
|
#endif /* COSMOPOLITAN_LIBC_THREAD_ATTR_H_ */
|
108
libc/thread/create.c
Normal file
108
libc/thread/create.c
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
/*-*- 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/thread/create.h"
|
||||||
|
#include "libc/linux/clone.h"
|
||||||
|
#include "libc/runtime/runtime.h"
|
||||||
|
#include "libc/linux/mmap.h"
|
||||||
|
#include "libc/sysv/consts/clone.h"
|
||||||
|
#include "libc/sysv/consts/map.h"
|
||||||
|
#include "libc/sysv/consts/prot.h"
|
||||||
|
#include "libc/errno.h"
|
||||||
|
|
||||||
|
|
||||||
|
static cthread_t _thread_allocate(const cthread_attr_t* attr) {
|
||||||
|
size_t stacksize = attr->stacksize;
|
||||||
|
size_t guardsize = 0;//attr->guardsize;
|
||||||
|
// FIXME: properly count TLS size
|
||||||
|
size_t tlssize = 0;
|
||||||
|
|
||||||
|
size_t totalsize = 3*guardsize + stacksize + tlssize + sizeof(struct cthread_descriptor_t);
|
||||||
|
totalsize = (totalsize + PAGESIZE-1) & -PAGESIZE;
|
||||||
|
|
||||||
|
uintptr_t mem = (uintptr_t)mmap(NULL, totalsize, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
|
||||||
|
if (mem == -1) return NULL;
|
||||||
|
|
||||||
|
void* alloc_bottom = (void*) mem;
|
||||||
|
void* stack_bottom = (void*)(mem + guardsize);
|
||||||
|
void* stack_top = (void*)(mem + guardsize + stacksize);
|
||||||
|
void* tls_bottom = (void*)(mem + guardsize + stacksize + guardsize);
|
||||||
|
void* tls_top = (void*)(mem + totalsize - guardsize);
|
||||||
|
void* alloc_top = (void*)(mem + totalsize);
|
||||||
|
|
||||||
|
if (mprotect(stack_bottom, (uintptr_t)stack_top - (uintptr_t)stack_bottom, PROT_READ | PROT_WRITE) != 0 ||
|
||||||
|
mprotect(tls_bottom, (uintptr_t)tls_top - (uintptr_t)tls_bottom, PROT_READ | PROT_WRITE) != 0) {
|
||||||
|
munmap(alloc_bottom, totalsize);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
cthread_t td = (cthread_t)tls_top - 1;
|
||||||
|
td->self = td;
|
||||||
|
td->stack.top = stack_top;
|
||||||
|
td->stack.bottom = stack_bottom;
|
||||||
|
td->tls.top = tls_top;
|
||||||
|
td->tls.bottom = tls_bottom;
|
||||||
|
td->alloc.top = alloc_top;
|
||||||
|
td->alloc.bottom = alloc_bottom;
|
||||||
|
td->tid = 0;
|
||||||
|
|
||||||
|
return td;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cthread_create(cthread_t*restrict p, const cthread_attr_t*restrict attr, int (*func)(void*), void*restrict arg) {
|
||||||
|
extern wontreturn void _thread_run(int(*func)(void*), void* arg);
|
||||||
|
|
||||||
|
cthread_attr_t default_attr;
|
||||||
|
if (!attr) cthread_attr_init(&default_attr);
|
||||||
|
cthread_t td = _thread_allocate(attr ? attr : &default_attr);
|
||||||
|
if (!attr) cthread_attr_destroy(&default_attr);
|
||||||
|
if (!td) return errno;
|
||||||
|
|
||||||
|
*p = td;
|
||||||
|
|
||||||
|
uintptr_t stack = (uintptr_t)(td->stack.top);
|
||||||
|
|
||||||
|
stack -= sizeof(void*); *(void**)stack = func;
|
||||||
|
stack -= sizeof(void*); *(void**)stack = arg;
|
||||||
|
|
||||||
|
long flags = CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_PARENT | CLONE_THREAD | /*CLONE_IO |*/ CLONE_SETTLS;
|
||||||
|
|
||||||
|
// It is not necessary to check the return of the syscall here as the return address for the thread is setup to to `_thread_run`.
|
||||||
|
// In case of success: the parent callee returns immediately to the caller forwarding the success to the callee
|
||||||
|
// the child return immediately to the entry point of `thread_spawn`
|
||||||
|
// In case of error: the parent callee returns immediately to the caller forwarding the error
|
||||||
|
// the child is never created (and so cannot returned in the wrong place)
|
||||||
|
int rc = LinuxClone(flags, (void*)stack, NULL, NULL, (void*)td);
|
||||||
|
if (!rc) {
|
||||||
|
// child
|
||||||
|
asm volatile(
|
||||||
|
"xor %rbp,%rbp\n\t"
|
||||||
|
/* pop arguments */
|
||||||
|
"pop %rdi\n\t"
|
||||||
|
"pop %rax\n\t"
|
||||||
|
/* call function */
|
||||||
|
"call *%rax\n\t"
|
||||||
|
/* thread exit */
|
||||||
|
"mov %rax, %rdi\n\t"
|
||||||
|
"jmp cthread_exit"
|
||||||
|
);
|
||||||
|
unreachable;
|
||||||
|
}
|
||||||
|
if (rc < 0) return rc;
|
||||||
|
return 0;
|
||||||
|
}
|
18
libc/thread/create.h
Normal file
18
libc/thread/create.h
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
#ifndef COSMOPOLITAN_LIBC_THREAD_CREATE_H_
|
||||||
|
#define COSMOPOLITAN_LIBC_THREAD_CREATE_H_
|
||||||
|
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||||
|
COSMOPOLITAN_C_START_
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @fileoverview thread
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "libc/thread/attr.h"
|
||||||
|
#include "libc/thread/descriptor.h"
|
||||||
|
|
||||||
|
int cthread_create(cthread_t*restrict, const cthread_attr_t*restrict, int (*)(void*), void*restrict);
|
||||||
|
|
||||||
|
|
||||||
|
COSMOPOLITAN_C_END_
|
||||||
|
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||||
|
#endif /* COSMOPOLITAN_LIBC_THREAD_CREATE_H_ */
|
22
libc/thread/descriptor.h
Normal file
22
libc/thread/descriptor.h
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
#ifndef COSMOPOLITAN_LIBC_THREAD_DESCRIPTOR_H_
|
||||||
|
#define COSMOPOLITAN_LIBC_THREAD_DESCRIPTOR_H_
|
||||||
|
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||||
|
COSMOPOLITAN_C_START_
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @fileoverview thread types
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct cthread_descriptor_t {
|
||||||
|
struct cthread_descriptor_t* self; // mandatory for TLS
|
||||||
|
struct {
|
||||||
|
void *top, *bottom;
|
||||||
|
} stack, tls, alloc;
|
||||||
|
int tid;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct cthread_descriptor_t* cthread_t;
|
||||||
|
|
||||||
|
COSMOPOLITAN_C_END_
|
||||||
|
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||||
|
#endif /* COSMOPOLITAN_LIBC_THREAD_DESCRIPTOR_H_ */
|
|
@ -1,5 +1,5 @@
|
||||||
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
|
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||||
│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│
|
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||||
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
||||||
│ │
|
│ │
|
||||||
|
@ -16,38 +16,31 @@
|
||||||
│ 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/macros.internal.h"
|
#include "libc/thread/exit.h"
|
||||||
|
#include "libc/thread/descriptor.h"
|
||||||
|
#include "libc/linux/munmap.h"
|
||||||
|
|
||||||
// wontreturn void thread_spawn(int(*func)(void*), void* arg);
|
static wontreturn void _self_exit(void* p, size_t s, int r) {
|
||||||
//
|
// asm is necessary as the stack does not exist between the unmap and the actual exit
|
||||||
// This function is the root function of a new thread.
|
asm volatile(
|
||||||
// It calls `func` with `arg`.
|
"mov $11, %%rax\n\t"
|
||||||
// The return value of `func` is passed as argument to the `exit` syscall.
|
"syscall\n\t"
|
||||||
//
|
"mov %%rbx, %%rdi\n\t"
|
||||||
// All arguments are passed onto the (newly created) stack.
|
"mov $60, %%rax\n\t"
|
||||||
// The stack must have been set as followed:
|
"syscall"
|
||||||
// Top
|
:
|
||||||
// +------------+
|
: "D"(p), "S"(s), "b"(r)
|
||||||
// | func |
|
: "memory"
|
||||||
// +------------+
|
);
|
||||||
// | arg |
|
unreachable;
|
||||||
// %rsp -> +------------+
|
}
|
||||||
//
|
|
||||||
|
|
||||||
thread_spawn:
|
wontreturn void cthread_exit(int rc) {
|
||||||
.cfi_startproc
|
// TODO: wait joiners
|
||||||
/* stop stack trace from going deeper than this function */
|
|
||||||
.cfi_undefined rip
|
cthread_t td;
|
||||||
xor %rbp,%rbp
|
asm("mov %%fs:0, %0" : "=r"(td));
|
||||||
/* pop arguments */
|
size_t size = (intptr_t)(td->alloc.top) - (intptr_t)(td->alloc.bottom);
|
||||||
pop %rdi
|
|
||||||
pop %rax
|
_self_exit(td->alloc.top, size, rc);
|
||||||
/* call function */
|
}
|
||||||
call *%rax
|
|
||||||
/* thread exit */
|
|
||||||
mov %rax, %rdi
|
|
||||||
mov 60, %rax
|
|
||||||
syscall
|
|
||||||
.cfi_endproc
|
|
||||||
.endfn thread_spawn,globl
|
|
||||||
.source __FILE__
|
|
14
libc/thread/exit.h
Normal file
14
libc/thread/exit.h
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
#ifndef COSMOPOLITAN_LIBC_THREAD_EXIT_H_
|
||||||
|
#define COSMOPOLITAN_LIBC_THREAD_EXIT_H_
|
||||||
|
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||||
|
COSMOPOLITAN_C_START_
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @fileoverview internal function called after a thread exits
|
||||||
|
*/
|
||||||
|
|
||||||
|
wontreturn void cthread_exit(int);
|
||||||
|
|
||||||
|
COSMOPOLITAN_C_END_
|
||||||
|
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||||
|
#endif /* COSMOPOLITAN_LIBC_THREAD_EXIT_H_ */
|
|
@ -1,79 +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/thread/thread.h"
|
|
||||||
#include "libc/linux/mmap.h"
|
|
||||||
#include "libc/linux/clone.h"
|
|
||||||
|
|
||||||
// sched.h
|
|
||||||
#define CLONE_VM 0x00000100
|
|
||||||
#define CLONE_FS 0x00000200
|
|
||||||
#define CLONE_FILES 0x00000400
|
|
||||||
#define CLONE_SIGHAND 0x00000800
|
|
||||||
#define CLONE_PARENT 0x00008000
|
|
||||||
#define CLONE_THREAD 0x00010000
|
|
||||||
#define CLONE_SETTLS 0x00080000
|
|
||||||
#define CLONE_IO 0x80000000
|
|
||||||
|
|
||||||
// sys/mman.h
|
|
||||||
#define MAP_GROWSDOWN 0x0100
|
|
||||||
#define MAP_ANONYMOUS 0x0020
|
|
||||||
#define MAP_PRIVATE 0x0002
|
|
||||||
#define PROT_READ 0x1
|
|
||||||
#define PROT_WRITE 0x2
|
|
||||||
#define PROT_EXEC 0x4
|
|
||||||
|
|
||||||
|
|
||||||
thread_descriptor_t* allocate_thread() {
|
|
||||||
const long pagesize = 4096;
|
|
||||||
long stacksize = 1024*pagesize;
|
|
||||||
// FIXME: properly count TLS size
|
|
||||||
long tlssize = 0;
|
|
||||||
long totalsize = stacksize + tlssize + sizeof(thread_descriptor_t);
|
|
||||||
|
|
||||||
// round-up totalsize to pagesize
|
|
||||||
totalsize = (totalsize + pagesize-1) & -pagesize;
|
|
||||||
|
|
||||||
intptr_t mem = LinuxMmap(NULL, totalsize, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
|
|
||||||
if (mem == -1) return NULL;
|
|
||||||
|
|
||||||
thread_descriptor_t* descriptor = (thread_descriptor_t*)(mem + totalsize - sizeof(thread_descriptor_t));
|
|
||||||
descriptor->self = descriptor; // setup TLS (System-V ABI)
|
|
||||||
descriptor->stack = (void*)(mem + stacksize);
|
|
||||||
|
|
||||||
// FIXME: properly copy .tdata
|
|
||||||
return descriptor;
|
|
||||||
}
|
|
||||||
long start_thread(thread_descriptor_t* descriptor, int (*func)(void*), void* arg) {
|
|
||||||
extern wontreturn void thread_spawn(int(*func)(void*), void* arg);
|
|
||||||
|
|
||||||
// Set-up thread stack
|
|
||||||
uintptr_t stack = (uintptr_t)(descriptor->stack) - 3*sizeof(void*);
|
|
||||||
*(void**)(stack + 2*sizeof(void*)) = func;
|
|
||||||
*(void**)(stack + 1*sizeof(void*)) = arg;
|
|
||||||
*(void**)(stack + 0*sizeof(void*)) = &thread_spawn;
|
|
||||||
|
|
||||||
long flags = CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_PARENT | CLONE_THREAD | CLONE_IO | CLONE_SETTLS;
|
|
||||||
|
|
||||||
// It is not necessary to check the return of the syscall here as the return address for the thread is setup to to `thread_spawn`.
|
|
||||||
// In case of success: the parent callee returns immediately to the caller forwarding the success to the callee
|
|
||||||
// the child return immediately to the entry point of `thread_spawn`
|
|
||||||
// In case of error: the parent callee returns immediately to the caller forwarding the error
|
|
||||||
// the child is never created (and so cannot returned in the wrong place)
|
|
||||||
return LinuxClone(flags, (void*)stack, NULL, NULL, descriptor);
|
|
||||||
}
|
|
|
@ -1,20 +0,0 @@
|
||||||
#ifndef COSMOPOLITAN_LIBC_THREAD_THREAD_H_
|
|
||||||
#define COSMOPOLITAN_LIBC_THREAD_THREAD_H_
|
|
||||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
|
||||||
COSMOPOLITAN_C_START_
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @fileoverview thread
|
|
||||||
*/
|
|
||||||
|
|
||||||
typedef struct thread_descriptor_t {
|
|
||||||
struct thread_descriptor_t* self; // mandatory for TLS
|
|
||||||
void* stack;
|
|
||||||
} thread_descriptor_t;
|
|
||||||
|
|
||||||
thread_descriptor_t* allocate_thread();
|
|
||||||
long start_thread(thread_descriptor_t*, int (*)(void*), void*);
|
|
||||||
|
|
||||||
COSMOPOLITAN_C_END_
|
|
||||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
|
||||||
#endif /* COSMOPOLITAN_LIBC_THREAD_THREAD_H_ */
|
|
|
@ -25,8 +25,12 @@ LIBC_THREAD_A_CHECKS = \
|
||||||
|
|
||||||
LIBC_THREAD_A_DIRECTDEPS = \
|
LIBC_THREAD_A_DIRECTDEPS = \
|
||||||
LIBC_STUBS \
|
LIBC_STUBS \
|
||||||
|
LIBC_CALLS \
|
||||||
LIBC_INTRIN \
|
LIBC_INTRIN \
|
||||||
LIBC_BITS \
|
LIBC_BITS \
|
||||||
|
LIBC_RUNTIME \
|
||||||
|
LIBC_SYSV \
|
||||||
|
LIBC_SYSV_CALLS \
|
||||||
LIBC_NEXGEN32E
|
LIBC_NEXGEN32E
|
||||||
|
|
||||||
LIBC_THREAD_A_DEPS := \
|
LIBC_THREAD_A_DEPS := \
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue