Join/Detach protocol

This commit is contained in:
Lemaitre 2021-10-03 11:55:12 +02:00
parent fb2dd3ff89
commit cf598d09e4
12 changed files with 242 additions and 66 deletions

View file

@ -9,19 +9,34 @@
#endif
#include "libc/stdio/stdio.h"
#include "libc/thread/create.h"
#include "libc/thread/self.h"
#include "libc/thread/detach.h"
#include "libc/thread/join.h"
#include "libc/time/time.h"
int worker(void* arg) {
cthread_t self = cthread_self();
int tid = self->tid;
sleep(1);
//sleep(10000);
//printf("[%p] %d\n", self, tid);
(void)arg;
return 0;
return 4;
}
int main() {
cthread_t thread;
int rc = cthread_create(&thread, NULL, &worker, NULL);
if (rc == 0) {
printf("thread created: %p\n", thread);
sleep(1000);
//printf("thread created: %p\n", thread);
sleep(1);
#if 1
cthread_join(thread, &rc);
#else
rc = cthread_detach(thread);
sleep(2);
#endif
//printf("thread joined: %p -> %d\n", thread, rc);
} else {
printf("ERROR: thread could not be started: %d\n", rc);
}

View file

@ -19,7 +19,7 @@
#include "libc/thread/create.h"
#include "libc/linux/clone.h"
#include "libc/runtime/runtime.h"
#include "libc/linux/mmap.h"
#include "libc/sysv/consts/nr.h"
#include "libc/sysv/consts/clone.h"
#include "libc/sysv/consts/map.h"
#include "libc/sysv/consts/prot.h"
@ -28,7 +28,7 @@
static cthread_t _thread_allocate(const cthread_attr_t* attr) {
size_t stacksize = attr->stacksize;
size_t guardsize = 0;//attr->guardsize;
size_t guardsize = attr->guardsize;
// FIXME: properly count TLS size
size_t tlssize = 0;
@ -59,7 +59,7 @@ static cthread_t _thread_allocate(const cthread_attr_t* attr) {
td->tls.bottom = tls_bottom;
td->alloc.top = alloc_top;
td->alloc.bottom = alloc_bottom;
td->tid = 0;
td->state = (attr->mode & CTHREAD_CREATE_DETACHED) ? cthread_detached : cthread_started;
return td;
}
@ -68,41 +68,41 @@ int cthread_create(cthread_t*restrict p, const cthread_attr_t*restrict attr, int
extern wontreturn void _thread_run(int(*func)(void*), void* arg);
cthread_attr_t default_attr;
if (!attr) cthread_attr_init(&default_attr);
cthread_attr_init(&default_attr);
cthread_t td = _thread_allocate(attr ? attr : &default_attr);
if (!attr) cthread_attr_destroy(&default_attr);
cthread_attr_destroy(&default_attr);
if (!td) return errno;
*p = td;
uintptr_t stack = (uintptr_t)(td->stack.top);
register cthread_t td_ asm("r8") = td;
register int* ptid_ asm("rdx") = &td->tid;
register int* ctid_ asm("r10") = &td->tid;
register int(*func_)(void*) asm("r12") = func;
register void* arg_ asm("r13") = arg;
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;
long flags = CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_PARENT | CLONE_THREAD | /*CLONE_IO |*/ CLONE_SETTLS | CLONE_PARENT_SETTID | CLONE_CHILD_CLEARTID;
int rc;
// asm ensures the (empty) stack of the child thread is not used
asm volatile(
"syscall\n\t" // clone
"test\t%0, %0\n\t" // if not child
"jne\t.L.cthread_create.%=\n\t" // jump to `parent` label
"xor\t%%rbp, %%rbp\n\t" // reset stack frame pointer
"mov\t%2, %%rdi\n\t"
"call\t*%1\n\t" // call `func(arg)`
"mov\t%%rax, %%rdi\n\t"
"jmp\tcthread_exit\n" // exit thread
".L.cthread_create.%=:"
: "=a"(rc)
: "r"(func_), "r"(arg_), "0"(__NR_clone), "D"(flags), "S"(td->stack.top), "r"(ptid_), "r"(ctid_), "r"(td_)
: "rcx", "r11", "cc", "memory"
);
if (__builtin_expect(rc < 0, 0)) {
// `clone` has failed. The thread must be deallocated.
size_t size = (intptr_t)(td->alloc.top) - (intptr_t)(td->alloc.bottom);
munmap(td->alloc.bottom, size);
return -rc;
}
if (rc < 0) return rc;
return 0;
}

View file

@ -1,15 +1,14 @@
#ifndef COSMOPOLITAN_LIBC_THREAD_CREATE_H_
#define COSMOPOLITAN_LIBC_THREAD_CREATE_H_
#include "libc/thread/attr.h"
#include "libc/thread/descriptor.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
/**
* @fileoverview thread
* @fileoverview Create a cosmopolitan 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);

View file

@ -7,12 +7,23 @@ COSMOPOLITAN_C_START_
* @fileoverview thread types
*/
enum cthread_state {
cthread_started = 0,
cthread_joining = 1,
cthread_finished = 2,
cthread_detached = 4,
};
struct cthread_descriptor_t {
struct cthread_descriptor_t* self; // mandatory for TLS
struct {
void *top, *bottom;
} stack, tls, alloc;
int tid;
struct cthread_descriptor_t* self; // mandatory for TLS
struct {
void *top, *bottom;
} stack, tls, alloc;
int state;
int tid;
int rc;
void* pthread_ret_ptr;
};
typedef struct cthread_descriptor_t* cthread_t;

31
libc/thread/detach.c Normal file
View file

@ -0,0 +1,31 @@
/*-*- 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/detach.h"
#include "libc/thread/descriptor.h"
#include "libc/runtime/runtime.h"
int cthread_detach(cthread_t td) {
int state;
asm volatile("lock xadd\t%1, %0" : "+m"(td->state), "=r"(state) : "1"(cthread_detached) : "cc");
if ((state & cthread_finished)) {
size_t size = (intptr_t)(td->alloc.top) - (intptr_t)(td->alloc.bottom);
munmap(td->alloc.bottom, size);
}
return 0;
}

16
libc/thread/detach.h Normal file
View file

@ -0,0 +1,16 @@
#ifndef COSMOPOLITAN_LIBC_THREAD_DETACH_H_
#define COSMOPOLITAN_LIBC_THREAD_DETACH_H_
#include "libc/thread/descriptor.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
/**
* @fileoverview detach a thread
*/
int cthread_detach(cthread_t);
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_THREAD_DETACH_H_ */

View file

@ -17,30 +17,28 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/thread/exit.h"
#include "libc/thread/self.h"
#include "libc/thread/descriptor.h"
#include "libc/linux/munmap.h"
#include "libc/sysv/consts/nr.h"
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
wontreturn void cthread_exit(int rc) {
cthread_t td = cthread_self();
td->rc = rc;
size_t size = (intptr_t)(td->alloc.top) - (intptr_t)(td->alloc.bottom);
int state;
asm volatile(
"mov $11, %%rax\n\t"
"syscall\n\t"
"mov %%rbx, %%rdi\n\t"
"mov $60, %%rax\n\t"
"syscall"
:
: "D"(p), "S"(s), "b"(r)
: "memory"
"lock xadd\t%1, %0\n\t" // mark thread as finished
"test\t%2, %b1\n\t" // test if thread was detached
"jz .L.cthread_exit.%=\n\t" // skip unmap if not detached
"syscall\n" // unmap thread
".L.cthread_exit.%=:\n\t"
"mov\t%%rbx, %%rdi\n\t" //rc
"mov\t$60, %%rax\n\t"
"syscall" // thread exit
: "+m"(td->state), "=&r"(state)
: "I"(cthread_detached), "1"(cthread_finished), "a"(__NR_munmap), "b"(rc), "D"(td->alloc.bottom), "S"(size)
: "rcx", "r11", "cc", "memory"
);
unreachable;
}
wontreturn void cthread_exit(int rc) {
// TODO: wait joiners
cthread_t td;
asm("mov %%fs:0, %0" : "=r"(td));
size_t size = (intptr_t)(td->alloc.top) - (intptr_t)(td->alloc.bottom);
_self_exit(td->alloc.top, size, rc);
}

View file

@ -4,7 +4,7 @@
COSMOPOLITAN_C_START_
/**
* @fileoverview internal function called after a thread exits
* @fileoverview exit the current thread
*/
wontreturn void cthread_exit(int);

49
libc/thread/join.c Normal file
View file

@ -0,0 +1,49 @@
/*-*- 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/join.h"
#include "libc/thread/descriptor.h"
#include "libc/runtime/runtime.h"
#include "libc/sysv/consts/nr.h"
#include "libc/sysv/consts/futex.h"
int cthread_join(cthread_t td, int* rc) {
int tid = td->tid; // tid must be loaded before lock xadd
// otherwise, tid could be set to 0 even though `state` is not finished
// mark thread as joining
int state;
asm volatile("lock xadd\t%1, %0" : "+m"(td->state), "=r"(state) : "1"(cthread_joining) : "cc");
if (!(state & cthread_finished)) {
int flags = FUTEX_WAIT; // PRIVATE makes it hang
register struct timespec* timeout asm("r10") = NULL;
asm volatile (
"syscall"
:
: "a"(__NR_futex), "D"(&td->tid), "S"(flags), "d"(tid), "r"(timeout)
: "rcx", "r11", "cc", "memory"
);
}
*rc = td->rc;
size_t size = (intptr_t)(td->alloc.top) - (intptr_t)(td->alloc.bottom);
munmap(td->alloc.bottom, size);
return 0;
}

16
libc/thread/join.h Normal file
View file

@ -0,0 +1,16 @@
#ifndef COSMOPOLITAN_LIBC_THREAD_JOIN_H_
#define COSMOPOLITAN_LIBC_THREAD_JOIN_H_
#include "libc/thread/descriptor.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
/**
* @fileoverview join a thread
*/
int cthread_join(cthread_t, int*);
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_THREAD_JOIN_H_ */

21
libc/thread/self.c Normal file
View file

@ -0,0 +1,21 @@
/*-*- 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/self.h"
extern inline cthread_t cthread_self(void);

20
libc/thread/self.h Normal file
View file

@ -0,0 +1,20 @@
#ifndef COSMOPOLITAN_LIBC_THREAD_SELF_H_
#define COSMOPOLITAN_LIBC_THREAD_SELF_H_
#include "libc/thread/descriptor.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
/**
* @fileoverview get the thread descriptor of the current thread
*/
inline cthread_t cthread_self(void) {
cthread_t self;
asm ("mov %%fs:0, %0" : "=r"(self));
return self;
}
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_THREAD_SELF_H_ */