mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-08-06 09:50:28 +00:00
Join/Detach protocol
This commit is contained in:
parent
fb2dd3ff89
commit
cf598d09e4
12 changed files with 242 additions and 66 deletions
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
||||
|
|
|
@ -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
31
libc/thread/detach.c
Normal 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
16
libc/thread/detach.h
Normal 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_ */
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
49
libc/thread/join.c
Normal 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
16
libc/thread/join.h
Normal 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
21
libc/thread/self.c
Normal 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
20
libc/thread/self.h
Normal 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_ */
|
Loading…
Add table
Add a link
Reference in a new issue