Skip to content

Commit

Permalink
[WIP] Threading (#282)
Browse files Browse the repository at this point in the history
* Thread creation
* Proper thread creation and exit
* Join/Detach protocol
* Added semaphore with futex (hopefully fast)
  • Loading branch information
lemaitre authored Oct 13, 2021
1 parent d852640 commit a0b39f8
Show file tree
Hide file tree
Showing 30 changed files with 792 additions and 12 deletions.
3 changes: 3 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ include third_party/gdtoa/gdtoa.mk # │ You can finally call malloc()
include libc/time/time.mk #
include libc/alg/alg.mk #
include libc/stdio/stdio.mk #
include libc/thread/thread.mk #
include net/net.mk #
include libc/log/log.mk #
include third_party/bzip2/bzip2.mk #
Expand Down Expand Up @@ -284,6 +285,7 @@ COSMOPOLITAN_OBJECTS = \
LIBC_NT_ADVAPI32 \
LIBC_FMT \
THIRD_PARTY_COMPILER_RT \
LIBC_THREAD \
LIBC_TINYMATH \
LIBC_STR \
LIBC_SYSV \
Expand Down Expand Up @@ -311,6 +313,7 @@ COSMOPOLITAN_HEADERS = \
LIBC_STDIO \
LIBC_STR \
LIBC_SYSV \
LIBC_THREAD \
LIBC_TIME \
LIBC_TINYMATH \
LIBC_UNICODE \
Expand Down
1 change: 1 addition & 0 deletions examples/examples.mk
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ EXAMPLES_DIRECTDEPS = \
LIBC_SYSV \
LIBC_SYSV_CALLS \
LIBC_TESTLIB \
LIBC_THREAD \
LIBC_TIME \
LIBC_TINYMATH \
LIBC_UNICODE \
Expand Down
54 changes: 54 additions & 0 deletions examples/thread.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#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/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/thread/nativesem.h"
#include "libc/time/time.h"

cthread_native_sem_t semaphore;

int worker(void* arg) {
cthread_native_sem_signal(&semaphore);

cthread_t self = cthread_self();
int tid = self->tid;
sleep(1);
//sleep(10000);
//printf("[%p] %d\n", self, tid);
(void)arg;
return 4;
}

int main() {
cthread_native_sem_init(&semaphore, 0);

cthread_t thread;
int rc = cthread_create(&thread, NULL, &worker, NULL);
if (rc == 0) {
cthread_native_sem_wait(&semaphore, 0, 0, NULL);
//printf("thread created: %p\n", thread);
sleep(1);
#if 1
cthread_join(thread, &rc);
#else
rc = cthread_detach(thread);
sleep(2);
#endif
cthread_native_sem_signal(&semaphore);
cthread_native_sem_wait(&semaphore, 0, 0, NULL);
//printf("thread joined: %p -> %d\n", thread, rc);
} else {
printf("ERROR: thread could not be started: %d\n", rc);
}
return 0;
}
1 change: 1 addition & 0 deletions libc/libc.mk
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ o/$(MODE)/libc: o/$(MODE)/libc/alg \
o/$(MODE)/libc/stubs \
o/$(MODE)/libc/sysv \
o/$(MODE)/libc/testlib \
o/$(MODE)/libc/thread \
o/$(MODE)/libc/time \
o/$(MODE)/libc/tinymath \
o/$(MODE)/libc/unicode \
Expand Down
17 changes: 17 additions & 0 deletions libc/linux/clone.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#ifndef COSMOPOLITAN_LIBC_LINUX_CLONE_H_
#define COSMOPOLITAN_LIBC_LINUX_CLONE_H_
#if !(__ASSEMBLER__ + __LINKER__ + 0)

forceinline long LinuxClone(unsigned long flags, void* stack, int* parent_tid, int* child_tid, void* tls) {
long rc;
register int* child_tid_ asm("r10") = child_tid;
register void* tls_ asm("r8") = tls;
asm volatile("syscall"
: "=a"(rc)
: "0"(56), "D"(flags), "S"(stack), "d"(parent_tid), "r"(child_tid_), "r"(tls_)
: "rcx", "r11", "memory");
return rc;
}

#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_LINUX_MMAP_H_ */
12 changes: 6 additions & 6 deletions libc/sysv/consts.sh
Original file line number Diff line number Diff line change
Expand Up @@ -1928,13 +1928,13 @@ syscon misc DAY_5 0x02000b 11 11 10 10 0
syscon misc DAY_6 0x02000c 12 12 11 11 0
syscon misc DAY_7 0x02000d 13 13 12 12 0

syscon misc FUTEX_PRIVATE_FLAG 0 0 0 0x80 0x80 0
syscon misc FUTEX_REQUEUE 0 0 0 3 3 0
syscon misc FUTEX_REQUEUE_PRIVATE 0 0 0 131 131 0
syscon misc FUTEX_PRIVATE_FLAG 128 0 0 0x80 0x80 0
syscon misc FUTEX_REQUEUE 3 0 0 3 3 0
syscon misc FUTEX_REQUEUE_PRIVATE 131 0 0 131 131 0
syscon misc FUTEX_WAIT 0 0 0 1 1 0
syscon misc FUTEX_WAIT_PRIVATE 0 0 0 129 129 0
syscon misc FUTEX_WAKE 0 0 0 2 2 0
syscon misc FUTEX_WAKE_PRIVATE 0 0 0 130 130 0
syscon misc FUTEX_WAIT_PRIVATE 128 0 0 129 129 0
syscon misc FUTEX_WAKE 1 0 0 2 2 0
syscon misc FUTEX_WAKE_PRIVATE 129 0 0 130 130 0

syscon misc HOST_NOT_FOUND 1 1 1 1 1 0x2af9 # unix consensus
syscon misc HOST_NAME_MAX 0x40 0 0 255 255 0
Expand Down
2 changes: 1 addition & 1 deletion libc/sysv/consts/FUTEX_PRIVATE_FLAG.S
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon misc,FUTEX_PRIVATE_FLAG,0,0,0,0x80,0x80,0
.syscon misc,FUTEX_PRIVATE_FLAG,128,0,0,0x80,0x80,0
2 changes: 1 addition & 1 deletion libc/sysv/consts/FUTEX_REQUEUE.S
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon misc,FUTEX_REQUEUE,0,0,0,3,3,0
.syscon misc,FUTEX_REQUEUE,3,0,0,3,3,0
2 changes: 1 addition & 1 deletion libc/sysv/consts/FUTEX_REQUEUE_PRIVATE.S
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon misc,FUTEX_REQUEUE_PRIVATE,0,0,0,131,131,0
.syscon misc,FUTEX_REQUEUE_PRIVATE,131,0,0,131,131,0
2 changes: 1 addition & 1 deletion libc/sysv/consts/FUTEX_WAIT_PRIVATE.S
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon misc,FUTEX_WAIT_PRIVATE,0,0,0,129,129,0
.syscon misc,FUTEX_WAIT_PRIVATE,128,0,0,129,129,0
2 changes: 1 addition & 1 deletion libc/sysv/consts/FUTEX_WAKE.S
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon misc,FUTEX_WAKE,0,0,0,2,2,0
.syscon misc,FUTEX_WAKE,1,0,0,2,2,0
2 changes: 1 addition & 1 deletion libc/sysv/consts/FUTEX_WAKE_PRIVATE.S
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon misc,FUTEX_WAKE_PRIVATE,0,0,0,130,130,0
.syscon misc,FUTEX_WAKE_PRIVATE,129,0,0,130,130,0
67 changes: 67 additions & 0 deletions libc/thread/attr.c
Original file line number Diff line number Diff line change
@@ -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 changes: 36 additions & 0 deletions libc/thread/attr.h
Original file line number Diff line number Diff line change
@@ -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 changes: 108 additions & 0 deletions libc/thread/create.c
Original file line number Diff line number Diff line change
@@ -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/sysv/consts/nr.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 = 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->state = (attr->mode & CTHREAD_CREATE_DETACHED) ? cthread_detached : cthread_started;

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;
cthread_attr_init(&default_attr);
cthread_t td = _thread_allocate(attr ? attr : &default_attr);
cthread_attr_destroy(&default_attr);
if (!td) return errno;

*p = td;

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;

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;
}
return 0;
}
17 changes: 17 additions & 0 deletions libc/thread/create.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#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 Create a cosmopolitan thread
*/

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_ */
Loading

0 comments on commit a0b39f8

Please sign in to comment.