mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-08-06 09:50:28 +00:00
Thread creation
This commit is contained in:
parent
7521bf9e73
commit
149b1901b0
9 changed files with 253 additions and 0 deletions
3
Makefile
3
Makefile
|
@ -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 # │
|
||||
|
@ -284,6 +285,7 @@ COSMOPOLITAN_OBJECTS = \
|
|||
LIBC_NT_ADVAPI32 \
|
||||
LIBC_FMT \
|
||||
THIRD_PARTY_COMPILER_RT \
|
||||
LIBC_THREAD \
|
||||
LIBC_TINYMATH \
|
||||
LIBC_STR \
|
||||
LIBC_SYSV \
|
||||
|
@ -311,6 +313,7 @@ COSMOPOLITAN_HEADERS = \
|
|||
LIBC_STDIO \
|
||||
LIBC_STR \
|
||||
LIBC_SYSV \
|
||||
LIBC_THREAD \
|
||||
LIBC_TIME \
|
||||
LIBC_TINYMATH \
|
||||
LIBC_UNICODE \
|
||||
|
|
|
@ -61,6 +61,7 @@ EXAMPLES_DIRECTDEPS = \
|
|||
LIBC_SYSV \
|
||||
LIBC_SYSV_CALLS \
|
||||
LIBC_TESTLIB \
|
||||
LIBC_THREAD \
|
||||
LIBC_TIME \
|
||||
LIBC_TINYMATH \
|
||||
LIBC_UNICODE \
|
||||
|
|
28
examples/thread.c
Normal file
28
examples/thread.c
Normal file
|
@ -0,0 +1,28 @@
|
|||
#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/thread.h"
|
||||
|
||||
int worker(void* arg) {
|
||||
(void)arg;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main() {
|
||||
thread_descriptor_t* thread = allocate_thread();
|
||||
|
||||
if (thread) {
|
||||
long rc = start_thread(thread, &worker, NULL);
|
||||
printf("thread created: %ld\n", rc);
|
||||
} else {
|
||||
printf("ERROR: thread stack could not be allocated\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
|
@ -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 \
|
||||
|
|
17
libc/linux/clone.h
Normal file
17
libc/linux/clone.h
Normal file
|
@ -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_ */
|
79
libc/thread/thread.c
Normal file
79
libc/thread/thread.c
Normal file
|
@ -0,0 +1,79 @@
|
|||
/*-*- 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);
|
||||
}
|
20
libc/thread/thread.h
Normal file
20
libc/thread/thread.h
Normal file
|
@ -0,0 +1,20 @@
|
|||
#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_ */
|
51
libc/thread/thread.mk
Normal file
51
libc/thread/thread.mk
Normal file
|
@ -0,0 +1,51 @@
|
|||
#-*-mode:makefile-gmake;indent-tabs-mode:t;tab-width:8;coding:utf-8-*-┐
|
||||
#───vi: set et ft=make ts=8 tw=8 fenc=utf-8 :vi───────────────────────┘
|
||||
|
||||
PKGS += LIBC_THREAD
|
||||
|
||||
LIBC_THREAD_ARTIFACTS += LIBC_THREAD_A
|
||||
LIBC_THREAD = $(LIBC_THREAD_A_DEPS) $(LIBC_THREAD_A)
|
||||
LIBC_THREAD_A = o/$(MODE)/libc/thread/thread.a
|
||||
LIBC_THREAD_A_FILES := $(wildcard libc/thread/*)
|
||||
LIBC_THREAD_A_HDRS = $(filter %.h,$(LIBC_THREAD_A_FILES))
|
||||
LIBC_THREAD_A_SRCS_S = $(filter %.S,$(LIBC_THREAD_A_FILES))
|
||||
LIBC_THREAD_A_SRCS_C = $(filter %.c,$(LIBC_THREAD_A_FILES))
|
||||
|
||||
LIBC_THREAD_A_SRCS = \
|
||||
$(LIBC_THREAD_A_SRCS_S) \
|
||||
$(LIBC_THREAD_A_SRCS_C)
|
||||
|
||||
LIBC_THREAD_A_OBJS = \
|
||||
$(LIBC_THREAD_A_SRCS_S:%.S=o/$(MODE)/%.o) \
|
||||
$(LIBC_THREAD_A_SRCS_C:%.c=o/$(MODE)/%.o)
|
||||
|
||||
LIBC_THREAD_A_CHECKS = \
|
||||
$(LIBC_THREAD_A).pkg \
|
||||
$(LIBC_THREAD_A_HDRS:%=o/$(MODE)/%.ok)
|
||||
|
||||
LIBC_THREAD_A_DIRECTDEPS = \
|
||||
LIBC_STUBS \
|
||||
LIBC_INTRIN \
|
||||
LIBC_BITS \
|
||||
LIBC_NEXGEN32E
|
||||
|
||||
LIBC_THREAD_A_DEPS := \
|
||||
$(call uniq,$(foreach x,$(LIBC_THREAD_A_DIRECTDEPS),$($(x))))
|
||||
|
||||
$(LIBC_THREAD_A): libc/thread/ \
|
||||
$(LIBC_THREAD_A).pkg \
|
||||
$(LIBC_THREAD_A_OBJS)
|
||||
|
||||
$(LIBC_THREAD_A).pkg: \
|
||||
$(LIBC_THREAD_A_OBJS) \
|
||||
$(foreach x,$(LIBC_THREAD_A_DIRECTDEPS),$($(x)_A).pkg)
|
||||
|
||||
LIBC_THREAD_LIBS = $(foreach x,$(LIBC_THREAD_ARTIFACTS),$($(x)))
|
||||
LIBC_THREAD_SRCS = $(foreach x,$(LIBC_THREAD_ARTIFACTS),$($(x)_SRCS))
|
||||
LIBC_THREAD_HDRS = $(foreach x,$(LIBC_THREAD_ARTIFACTS),$($(x)_HDRS))
|
||||
LIBC_THREAD_CHECKS = $(foreach x,$(LIBC_THREAD_ARTIFACTS),$($(x)_CHECKS))
|
||||
LIBC_THREAD_OBJS = $(foreach x,$(LIBC_THREAD_ARTIFACTS),$($(x)_OBJS))
|
||||
$(LIBC_THREAD_OBJS): $(BUILD_FILES) libc/thread/thread.mk
|
||||
|
||||
.PHONY: o/$(MODE)/libc/thread
|
||||
o/$(MODE)/libc/thread: $(LIBC_THREAD_CHECKS)
|
53
libc/thread/threadspawn.S
Normal file
53
libc/thread/threadspawn.S
Normal file
|
@ -0,0 +1,53 @@
|
|||
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
|
||||
│vi: set et ft=asm ts=8 tw=8 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/macros.internal.h"
|
||||
|
||||
// wontreturn void thread_spawn(int(*func)(void*), void* arg);
|
||||
//
|
||||
// This function is the root function of a new thread.
|
||||
// It calls `func` with `arg`.
|
||||
// The return value of `func` is passed as argument to the `exit` syscall.
|
||||
//
|
||||
// All arguments are passed onto the (newly created) stack.
|
||||
// The stack must have been set as followed:
|
||||
// Top
|
||||
// +------------+
|
||||
// | func |
|
||||
// +------------+
|
||||
// | arg |
|
||||
// %rsp -> +------------+
|
||||
//
|
||||
|
||||
thread_spawn:
|
||||
.cfi_startproc
|
||||
/* stop stack trace from going deeper than this function */
|
||||
.cfi_undefined rip
|
||||
xor %rbp,%rbp
|
||||
/* pop arguments */
|
||||
pop %rdi
|
||||
pop %rax
|
||||
/* call function */
|
||||
call *%rax
|
||||
/* thread exit */
|
||||
mov %rax, %rdi
|
||||
mov 60, %rax
|
||||
syscall
|
||||
.cfi_endproc
|
||||
.endfn thread_spawn,globl
|
||||
.source __FILE__
|
Loading…
Add table
Add a link
Reference in a new issue