From defd6a9b58505bda1d568360bf76ed41f8dc47c1 Mon Sep 17 00:00:00 2001 From: Norman Feske Date: Thu, 22 Dec 2011 17:17:44 +0100 Subject: [PATCH] Use POSIX threads in Linux/Genode hybrids - Let hybrid Linux/Genode programs use POSIX threads for the implementation of the Thread API. - Prevent linkage of cxx library to hybrid Linux/Genode programs because the cxx functionality is covered by glibc. --- base-linux/include/base/native_types.h | 13 +- base-linux/lib/import/import-lx_hybrid.mk | 1 + base-linux/src/platform/lx_hybrid.cc | 201 +++++++++++++++++++++- base/include/base/thread.h | 4 + base/mk/prg.mk | 12 ++ 5 files changed, 229 insertions(+), 2 deletions(-) diff --git a/base-linux/include/base/native_types.h b/base-linux/include/base/native_types.h index 033eb53b9..c6ca8b865 100644 --- a/base-linux/include/base/native_types.h +++ b/base-linux/include/base/native_types.h @@ -56,6 +56,8 @@ namespace Genode { : tid(tid), pid(pid) { } }; + struct Thread_meta_data; + /** * Native thread contains more thread-local data than just the ID * @@ -71,7 +73,16 @@ namespace Genode { int client; /* socket used as IPC client */ int server; /* socket used as IPC server */ - Native_thread() : client(-1), server(-1) { } + /** + * Opaque pointer to additional thread-specific meta data + * + * This pointer is used by hybrid Linux/Genode program to maintain + * POSIX-thread-related meta data. For non-hybrid Genode programs, it + * remains unused. + */ + Thread_meta_data *meta_data; + + Native_thread() : client(-1), server(-1), meta_data(0) { } }; inline bool operator == (Native_thread_id t1, Native_thread_id t2) { diff --git a/base-linux/lib/import/import-lx_hybrid.mk b/base-linux/lib/import/import-lx_hybrid.mk index 4c61c5904..419bcec59 100644 --- a/base-linux/lib/import/import-lx_hybrid.mk +++ b/base-linux/lib/import/import-lx_hybrid.mk @@ -67,6 +67,7 @@ EXT_OBJECTS += $(shell cc -print-file-name=crtbegin.o) EXT_OBJECTS += $(shell cc -print-file-name=crtend.o) EXT_OBJECTS += $(shell cc -print-file-name=crtn.o) EXT_OBJECTS += -lgcc -lgcc_s -lsupc++ -lc +EXT_OBJECTS += -lpthread # # Some header files installed on GNU/Linux test for the GNU compiler. For diff --git a/base-linux/src/platform/lx_hybrid.cc b/base-linux/src/platform/lx_hybrid.cc index db8bf950c..be843b3a6 100644 --- a/base-linux/src/platform/lx_hybrid.cc +++ b/base-linux/src/platform/lx_hybrid.cc @@ -18,13 +18,16 @@ extern "C" int raw_write_str(const char *str); +enum { verbose_atexit = false }; + /** * Dummy for symbol that is normally provided by '_main.cc' */ int genode___cxa_atexit(void (*func)(void*), void *arg, void *dso) { - raw_write_str("genode___cxa_atexit called, not implemented\n"); + if (verbose_atexit) + raw_write_str("genode___cxa_atexit called, not implemented\n"); return 0; } @@ -45,3 +48,199 @@ __attribute__((constructor(101))) void lx_hybrid_init() main_thread_bootstrap(); lx_environ = environ; } + + +/************ + ** Thread ** + ************/ + +/* + * For hybrid Linux/Genode programs, Genode's thread API is implemented via + * POSIX threads. + * + * Hybrid Linux/Genode programs are linked against the glibc along with other + * native Linux libraries. Such libraries may use the 'pthread' API to spawn + * threads, which then may call Genode code. Vice versa, Genode threads may + * interact with code of a native Linux libraries. Hence, both worlds Genode + * and native Linux libraries should use the same underlying threading API. + * Furthermore, using the pthread API is a precondition to satisfy the glibc's + * assumption about thread-local storage, which is particularly important + * for the correct thread-local handling of 'errno'. As another benefit of + * using the pthread API over the normal Genode thread implementation, hybrid + * Linux/Genode programs comply with the GNU debugger's expectations. Such + * programs can be debugged as normal Linux programs. + * + * Genode's normal thread API for Linux was introduced to decouple Genode + * from the glibc. This is especially important when using Genode's libc + * Mixing both Genode's libc and glibc won't work. + */ + +/* Genode includes */ +#include + +/* libc includes */ +#include +#include +#include + +using namespace Genode; + + +/** + * Return TLS key used to storing the thread meta data + */ +static pthread_key_t tls_key() +{ + struct Tls_key + { + pthread_key_t key; + + Tls_key() + { + pthread_key_create(&key, 0); + } + }; + + static Tls_key inst; + return inst.key; +} + + +namespace Genode { + + struct Thread_meta_data + { + /** + * Used to block the constructor until the new thread has initialized + * 'id' + */ + Lock construct_lock; + + /** + * Used to block the new thread until 'start' is called + */ + Lock start_lock; + + /** + * Filled out by 'thread_start' function in the context of the new + * thread + */ + Thread_base * const thread_base; + + /** + * POSIX thread handle + */ + pthread_t pt; + + /** + * Constructor + * + * \param thread_base associated 'Thread_base' object + */ + Thread_meta_data(Thread_base *thread_base) + : + construct_lock(Lock::LOCKED), start_lock(Lock::LOCKED), + thread_base(thread_base) + { } + }; +} + + +static void empty_signal_handler(int) { } + + +static void *thread_start(void *arg) +{ + /* + * Set signal handler such that canceled system calls get not + * transparently retried after a signal gets received. + */ + lx_sigaction(LX_SIGUSR1, empty_signal_handler); + + /* assign 'Thread_meta_data' pointer to TLS entry */ + pthread_setspecific(tls_key(), arg); + + /* enable immediate cancellation when calling 'pthread_cancel' */ + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, 0); + + /* + * Initialize thread meta data + */ + Thread_meta_data *meta_data = (Thread_meta_data *)arg; + meta_data->thread_base->tid().tid = lx_gettid(); + meta_data->thread_base->tid().pid = lx_getpid(); + + /* unblock 'Thread_base' constructor */ + meta_data->construct_lock.unlock(); + + /* block until the 'Thread_base::start' gets called */ + meta_data->start_lock.lock(); + + Thread_base::myself()->entry(); + return 0; +} + + +Thread_base *Thread_base::myself() +{ + void *tls = pthread_getspecific(tls_key()); + + bool const is_main_thread = (tls == 0); + + return is_main_thread ? 0 : ((Thread_meta_data *)tls)->thread_base; +} + + +void Thread_base::start() +{ + /* + * Unblock thread that is supposed to slumber in 'thread_start'. + */ + _tid.meta_data->start_lock.unlock(); +} + + +Thread_base::Thread_base(const char *name, size_t stack_size) +: _list_element(this) +{ + _tid.meta_data = new Thread_meta_data(this); + + int const ret = pthread_create(&_tid.meta_data->pt, 0, thread_start, + _tid.meta_data); + if (ret) { + PERR("pthread_create failed (returned %d, errno=%d)", + ret, errno); + delete _tid.meta_data; + throw Context_alloc_failed(); + } + + _tid.meta_data->construct_lock.lock(); +} + + +void Thread_base::cancel_blocking() +{ + /* + * XXX implement interaction with CPU session + */ +} + + +Thread_base::~Thread_base() +{ + { + int const ret = pthread_cancel(_tid.meta_data->pt); + if (ret) + PWRN("pthread_cancel unexpectedly returned with %d", ret); + } + + { + int const ret = pthread_join(_tid.meta_data->pt, 0); + if (ret) + PWRN("pthread_join unexpectedly returned with %d (errno=%d)", + ret, errno); + } + + delete _tid.meta_data; + _tid.meta_data = 0; +} diff --git a/base/include/base/thread.h b/base/include/base/thread.h index d82368da5..cad4ccc27 100644 --- a/base/include/base/thread.h +++ b/base/include/base/thread.h @@ -277,6 +277,10 @@ namespace Genode { * \param name thread name for debugging * \param stack_size stack size * + * \throw Stack_too_large + * \throw Stack_alloc_failed + * \throw Context_alloc_failed + * * The stack for the new thread will be allocated from the RAM * session of the process environment. A small portion of the * stack size is internally used by the framework for storing diff --git a/base/mk/prg.mk b/base/mk/prg.mk index 570e210a2..55f4e182a 100644 --- a/base/mk/prg.mk +++ b/base/mk/prg.mk @@ -132,6 +132,18 @@ LD_CMD += $(addprefix $(LD_SCRIPT_PREFIX), $(LD_SCRIPTS)) STATIC_LIBS := $(foreach l,$(FILTER_DEPS),$(LIB_CACHE_DIR)/$l/$l.lib.a) STATIC_LIBS := $(sort $(wildcard $(STATIC_LIBS))) +# +# For hybrid Linux/Genode programs, prevent the linkage of the normal thread +# library. Because such programs use the glibc, we map Genode's thread API +# to 'pthreads' instead to improve inter-operability with native Linux +# libraries. Furthermore, we do not need Genode's cxx library for such +# programs because the cxx functionality is already provided by the glibc. +# +ifeq ($(USE_HOST_LD_SCRIPT),yes) +STATIC_LIBS := $(filter-out $(LIB_CACHE_DIR)/thread/thread.lib.a, $(STATIC_LIBS)) +STATIC_LIBS := $(filter-out $(LIB_CACHE_DIR)/cxx/cxx.lib.a, $(STATIC_LIBS)) +endif + # # We need the linker option '--whole-archive' to make sure that all library # constructors marked with '__attribute__((constructor))' end up int the