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