mirror of
https://github.com/genodelabs/genode.git
synced 2024-12-18 21:27:56 +00:00
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.
This commit is contained in:
parent
f904c2be09
commit
defd6a9b58
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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 <base/thread.h>
|
||||
|
||||
/* libc includes */
|
||||
#include <pthread.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
|
||||
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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user