diff --git a/libpthread/Makefile b/libpthread/Makefile
index aaa209ac..a3d81e12 100644
--- a/libpthread/Makefile
+++ b/libpthread/Makefile
@@ -21,6 +21,7 @@ pthread_cond_init.o \
pthread_cond_signal.o \
pthread_cond_timedwait.o \
pthread_cond_wait.o \
+pthread_create.o \
pthread_equal.o \
pthread_exit.o \
pthread_getspecific.o \
diff --git a/libpthread/include/pthread.h b/libpthread/include/pthread.h
index 56ba4e48..0c62461d 100644
--- a/libpthread/include/pthread.h
+++ b/libpthread/include/pthread.h
@@ -138,6 +138,8 @@ typedef __pthread_t pthread_t;
struct pthread
{
struct uthread uthread;
+ void* (*entry_function)(void*);
+ void* entry_cookie;
void** keys;
size_t keys_length;
};
@@ -175,6 +177,10 @@ extern pthread_mutex_t __pthread_keys_lock;
extern struct pthread_key* __pthread_keys;
extern size_t __pthread_keys_used;
extern size_t __pthread_keys_length;
+extern pthread_mutex_t __pthread_num_threads_lock;
+extern size_t __pthread_num_threads;
+
+struct pthread* pthread_allocate_tls(void);
#endif
@@ -223,7 +229,10 @@ int pthread_condattr_getclock(const pthread_condattr_t* __restrict,
int pthread_condattr_init(pthread_condattr_t*);
int pthread_condattr_setclock(pthread_condattr_t*, clockid_t);
/* TODO: pthread_condattr_setpshared */
-/* TODO: pthread_create */
+int pthread_create(pthread_t* __restrict,
+ const pthread_attr_t* __restrict,
+ void* (*)(void*),
+ void* __restrict);
/* TODO: pthread_detach */
int pthread_equal(pthread_t, pthread_t);
__attribute__((__noreturn__))
diff --git a/libpthread/pthread_create.c++ b/libpthread/pthread_create.c++
new file mode 100644
index 00000000..afb6d81e
--- /dev/null
+++ b/libpthread/pthread_create.c++
@@ -0,0 +1,235 @@
+/*******************************************************************************
+
+ Copyright(C) Jonas 'Sortie' Termansen 2013, 2014.
+
+ This file is part of Sortix libpthread.
+
+ Sortix libpthread is free software: you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by the
+ Free Software Foundation, either version 3 of the License, or (at your
+ option) any later version.
+
+ Sortix libpthread is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with Sortix libpthread. If not, see .
+
+ pthread_create.c++
+ Creates a new thread.
+
+*******************************************************************************/
+
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+// Utility function that rounds size upwards to a multiple of alignment.
+static size_t align_size(size_t size, size_t alignment)
+{
+ return -(-size & ~(alignment-1));
+}
+
+__attribute__((noreturn))
+static void pthread_entrance(struct pthread* thread)
+{
+ pthread_exit(thread->entry_function(thread->entry_cookie));
+}
+
+#if defined(__i386__) || defined(__x86_64__)
+const unsigned long FLAGS_CARRY = 1 << 0; // 0x000001
+const unsigned long FLAGS_RESERVED1 = 1 << 1; // 0x000002, read as one
+const unsigned long FLAGS_PARITY = 1 << 2; // 0x000004
+const unsigned long FLAGS_RESERVED2 = 1 << 3; // 0x000008
+const unsigned long FLAGS_AUX = 1 << 4; // 0x000010
+const unsigned long FLAGS_RESERVED3 = 1 << 5; // 0x000020
+const unsigned long FLAGS_ZERO = 1 << 6; // 0x000040
+const unsigned long FLAGS_SIGN = 1 << 7; // 0x000080
+const unsigned long FLAGS_TRAP = 1 << 8; // 0x000100
+const unsigned long FLAGS_INTERRUPT = 1 << 9; // 0x000200
+const unsigned long FLAGS_DIRECTION = 1 << 10; // 0x000400
+const unsigned long FLAGS_OVERFLOW = 1 << 11; // 0x000800
+const unsigned long FLAGS_IOPRIVLEVEL = 1 << 12 | 1 << 13;
+const unsigned long FLAGS_NESTEDTASK = 1 << 14; // 0x004000
+const unsigned long FLAGS_RESERVED4 = 1 << 15; // 0x008000
+const unsigned long FLAGS_RESUME = 1 << 16; // 0x010000
+const unsigned long FLAGS_VIRTUAL8086 = 1 << 17; // 0x020000
+const unsigned long FLAGS_ALIGNCHECK = 1 << 18; // 0x040000
+const unsigned long FLAGS_VIRTINTR = 1 << 19; // 0x080000
+const unsigned long FLAGS_VIRTINTRPEND = 1 << 20; // 0x100000
+const unsigned long FLAGS_ID = 1 << 21; // 0x200000
+#endif
+
+#if defined(__i386__)
+static const unsigned long MINIMUM_STACK_SIZE = 4 * sizeof(unsigned long);
+static void setup_thread_state(struct pthread* thread, tforkregs_t* regs)
+{
+ assert(MINIMUM_STACK_SIZE <= thread->uthread.stack_size);
+
+ memset(regs, 0, sizeof(*regs));
+ regs->eip = (uintptr_t) pthread_entrance;
+ regs->eflags = FLAGS_RESERVED1 | FLAGS_INTERRUPT | FLAGS_ID;
+ regs->gsbase = (unsigned long) thread;
+
+ unsigned long* stack =
+ (unsigned long*) ((uint8_t*) thread->uthread.stack_mmap +
+ thread->uthread.stack_size);
+
+ *--stack = 0; // rip=0
+ *--stack = 0; // rbp=0
+
+ regs->ebp = (uintptr_t) stack;
+
+ *--stack = (unsigned long) thread;
+ *--stack = 0;
+
+ regs->esp = (uintptr_t) stack;
+}
+
+#endif
+
+#if defined(__x86_64__)
+static const unsigned long MINIMUM_STACK_SIZE = 2 * sizeof(unsigned long);
+static void setup_thread_state(struct pthread* thread, tforkregs_t* regs)
+{
+ assert(MINIMUM_STACK_SIZE <= thread->uthread.stack_size);
+
+ memset(regs, 0, sizeof(*regs));
+ regs->rip = (uintptr_t) pthread_entrance;
+ regs->rdi = (uintptr_t) thread;
+ regs->rflags = FLAGS_RESERVED1 | FLAGS_INTERRUPT | FLAGS_ID;
+ regs->fsbase = (unsigned long) thread;
+
+ unsigned long* stack =
+ (unsigned long*) ((uint8_t*) thread->uthread.stack_mmap +
+ thread->uthread.stack_size);
+
+ *--stack = 0; // rip=0
+ *--stack = 0; // rbp=0
+
+ regs->rsp = (uintptr_t) stack;
+ regs->rbp = regs->rsp;
+}
+#endif
+
+static const unsigned long DEFAULT_STACK_SIZE = 64 * 1024;
+
+extern "C"
+{
+ pthread_mutex_t __pthread_num_threads_lock = PTHREAD_MUTEX_INITIALIZER;
+ size_t __pthread_num_threads = 1;
+}
+
+extern "C"
+int pthread_create(pthread_t* restrict thread_ptr,
+ const pthread_attr_t* restrict /*attr*/,
+ void* (*entry_function)(void*),
+ void* restrict entry_cookie)
+{
+ assert(thread_ptr);
+
+ struct pthread* self = pthread_self();
+
+ // We can't create a thread local storage copy (and thus a new thread) if
+ // the kernel failed to allocate the thread local storage master copy.
+ if ( self->uthread.tls_master_size && !self->uthread.tls_master_mmap )
+ return errno = ENOMEM;
+
+ size_t raw_tls_size = self->uthread.tls_master_size;
+ size_t raw_tls_size_aligned = align_size(raw_tls_size, self->uthread.tls_master_align);
+ if ( raw_tls_size && raw_tls_size_aligned == 0 /* overflow */ )
+ return errno = EINVAL;
+
+ size_t tls_size = raw_tls_size_aligned + sizeof(struct pthread);
+ size_t tls_offset_tls = 0;
+ size_t tls_offset_pthread = raw_tls_size_aligned;
+ if ( self->uthread.tls_master_align < alignof(struct pthread) )
+ {
+ size_t more_aligned = align_size(raw_tls_size_aligned, alignof(struct pthread));
+ if ( raw_tls_size_aligned && more_aligned == 0 /* overflow */ )
+ return errno = EINVAL;
+ size_t difference = more_aligned - raw_tls_size_aligned;
+ tls_size += difference;
+ tls_offset_tls += difference;
+ tls_offset_pthread += difference;
+ }
+ assert((tls_offset_tls & (self->uthread.tls_master_align-1)) == 0);
+ assert((tls_offset_pthread & (alignof(struct pthread)-1)) == 0);
+
+ // Allocate a copy of the base thread-local storage area for use by the new
+ // thread, followed by the struct pthread for the new thread.
+ int prot = PROT_READ | PROT_WRITE;
+ int flags = MAP_PRIVATE | MAP_ANONYMOUS;
+ uint8_t* mapping = (uint8_t*) mmap(NULL, tls_size, prot, flags, -1, 0);
+ if ( (void*) mapping == MAP_FAILED )
+ return errno;
+
+ // We'll put the struct pthread nicely aligned in the new mapping.
+ struct pthread* thread =
+ (struct pthread*) (mapping + tls_offset_pthread);
+ assert((((uintptr_t) thread) & (alignof(struct pthread)-1)) == 0);
+
+ // Let's put the thread-local storage just before the struct pthread.
+ uint8_t* tls = mapping + tls_offset_tls;
+
+ // We will now initialize the thread-local storage of the new thread.
+ memcpy(tls, self->uthread.tls_master_mmap, self->uthread.tls_master_size);
+
+ // We'll clean up the new struct pthread before initializing it.
+ memset(thread, 0, sizeof(struct pthread));
+
+ // Initialize the thread object.
+ thread->uthread.uthread_pointer = &thread->uthread;
+ thread->uthread.uthread_size = sizeof(struct pthread);
+ thread->uthread.uthread_flags = 0;
+ thread->uthread.tls_master_mmap = self->uthread.tls_master_mmap;
+ thread->uthread.tls_master_size = self->uthread.tls_master_size;
+ thread->uthread.tls_master_align = self->uthread.tls_master_align;
+ thread->uthread.tls_mmap = mapping;
+ thread->uthread.tls_size = tls_size;
+ thread->uthread.arg_mmap = self->uthread.arg_mmap;
+ thread->uthread.arg_size = self->uthread.arg_size;
+ thread->entry_function = entry_function;
+ thread->entry_cookie = entry_cookie;
+
+ // Set up a stack for the new thread.
+ int stack_prot = PROT_READ | PROT_WRITE;
+ int stack_flags = MAP_PRIVATE | MAP_ANONYMOUS;
+ thread->uthread.stack_size = DEFAULT_STACK_SIZE;
+ thread->uthread.stack_mmap =
+ mmap(NULL, thread->uthread.stack_size, stack_prot, stack_flags, -1, 0);
+ if ( thread->uthread.stack_mmap == MAP_FAILED )
+ {
+ munmap(thread->uthread.tls_mmap, thread->uthread.tls_size);
+ return errno;
+ }
+
+ // Prepare the registers and initial stack for the new thread.
+ tforkregs_t regs;
+ setup_thread_state(thread, ®s);
+
+ // Create a new thread with the requested state.
+ pthread_mutex_lock(&__pthread_num_threads_lock);
+ if ( tfork(SFTHREAD, ®s) < 0 )
+ {
+ pthread_mutex_unlock(&__pthread_num_threads_lock);
+ munmap(thread->uthread.stack_mmap, thread->uthread.stack_size);
+ munmap(thread->uthread.tls_mmap, thread->uthread.tls_size);
+ return errno;
+ }
+ __pthread_num_threads++;
+ pthread_mutex_unlock(&__pthread_num_threads_lock);
+
+ *thread_ptr = thread;
+
+ return 0;
+}
diff --git a/libpthread/pthread_exit.c++ b/libpthread/pthread_exit.c++
index bb30871b..edb2348b 100644
--- a/libpthread/pthread_exit.c++
+++ b/libpthread/pthread_exit.c++
@@ -56,7 +56,9 @@ void pthread_exit(void* /*return_value*/)
thread->keys_length = 0;
pthread_mutex_unlock(&__pthread_keys_lock);
- size_t num_threads = 1;
+ pthread_mutex_lock(&__pthread_num_threads_lock);
+ size_t num_threads = __pthread_num_threads--;
+ pthread_mutex_unlock(&__pthread_num_threads_lock);
if ( num_threads == 1 )
exit(0);
struct exit_thread extended;