Add 'Thread_base::join()'

Using the new 'join()' function, the caller can explicitly block for the
completion of the thread's 'entry()' function. The test case for this
feature can be found at 'os/src/test/thread_join'. For hybrid
Linux/Genode programs, the 'Thread_base::join()' does not map directly
to 'pthread_join'. The latter function gets already called by the
destructor of 'Thread_base'. According to the documentation, subsequent
calls of 'pthread_join' for one thread may result in undefined behaviour.
So we use a 'Genode::Lock' on this platform, which is in line with the
other platforms.

Related to #194, #501
This commit is contained in:
Norman Feske
2012-11-16 13:53:37 +01:00
parent 2995011b34
commit bcabbe2c92
21 changed files with 193 additions and 24 deletions

View File

@ -40,7 +40,7 @@ static Lock &startup_lock()
static void thread_exit_signal_handler(int) { lx_exit(0); }
static void thread_start(void *arg)
void Thread_base::_thread_start()
{
/*
* Set signal handler such that canceled system calls get not
@ -53,7 +53,7 @@ static void thread_start(void *arg)
*/
lx_sigaction(LX_SIGCHLD, (void (*)(int))1);
Thread_base *thread = (Thread_base *)arg;
Thread_base * const thread = Thread_base::myself();
/* inform core about the new thread and process ID of the new thread */
Linux_cpu_session *cpu = dynamic_cast<Linux_cpu_session *>(env()->cpu_session());
@ -63,7 +63,11 @@ static void thread_start(void *arg)
/* wakeup 'start' function */
startup_lock().unlock();
Thread_base::myself()->entry();
thread->entry();
/* unblock caller of 'join()' */
thread->_join_lock.unlock();
sleep_forever();
}
@ -125,7 +129,7 @@ void Thread_base::start()
/* align initial stack to 16 byte boundary */
void *thread_sp = (void *)((addr_t)(_context->stack) & ~0xf);
_tid.tid = lx_create_thread(thread_start, thread_sp, this);
_tid.tid = lx_create_thread(Thread_base::_thread_start, thread_sp, this);
_tid.pid = lx_getpid();
/* wait until the 'thread_start' function got entered */

View File

@ -24,7 +24,7 @@ using namespace Genode;
static void empty_signal_handler(int) { }
static void thread_start(void *)
void Thread_base::_thread_start()
{
/*
* Set signal handler such that canceled system calls get not
@ -52,7 +52,7 @@ void Thread_base::start()
{
/* align initial stack to 16 byte boundary */
void *thread_sp = (void *)((addr_t)(_context->stack) & ~0xf);
_tid.tid = lx_create_thread(thread_start, thread_sp, this);
_tid.tid = lx_create_thread(Thread_base::_thread_start, thread_sp, this);
_tid.pid = lx_getpid();
}

View File

@ -306,7 +306,7 @@ inline int lx_tgkill(int pid, int tid, int signal)
}
inline int lx_create_thread(void (*entry)(void *), void *stack, void *arg)
inline int lx_create_thread(void (*entry)(), void *stack, void *arg)
{
int flags = CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND
| CLONE_THREAD | CLONE_SYSVSEM;

View File

@ -130,16 +130,26 @@ namespace Genode {
struct Thread_meta_data
{
/**
* Lock with the initial state set to LOCKED
*/
struct Barrier : Lock { Barrier() : Lock(Lock::LOCKED) { } };
/**
* Used to block the constructor until the new thread has initialized
* 'id'
*/
Lock construct_lock;
Barrier construct_lock;
/**
* Used to block the new thread until 'start' is called
*/
Lock start_lock;
Barrier start_lock;
/**
* Used to block the 'join()' function until the 'entry()' is done
*/
Barrier join_lock;
/**
* Filled out by 'thread_start' function in the context of the new
@ -155,13 +165,9 @@ namespace Genode {
/**
* Constructor
*
* \param thread_base associated 'Thread_base' object
* \param thread associated 'Thread_base' object
*/
Thread_meta_data(Thread_base *thread_base)
:
construct_lock(Lock::LOCKED), start_lock(Lock::LOCKED),
thread_base(thread_base)
{ }
Thread_meta_data(Thread_base *thread) : thread_base(thread) { }
};
}
@ -224,6 +230,8 @@ static void *thread_start(void *arg)
meta_data->start_lock.lock();
Thread_base::myself()->entry();
meta_data->join_lock.unlock();
return 0;
}
@ -286,8 +294,15 @@ void Thread_base::start()
}
void Thread_base::join()
{
_tid.meta_data->join_lock.lock();
}
Thread_base::Thread_base(const char *name, size_t stack_size)
: _list_element(this)
:
_list_element(this)
{
_tid.meta_data = new (env()->heap()) Thread_meta_data(this);
@ -325,13 +340,9 @@ void Thread_base::cancel_blocking()
Thread_base::~Thread_base()
{
{
int const ret = pthread_cancel(_tid.meta_data->pt);
if (ret)
PWRN("pthread_cancel unexpectedly returned with %d", ret);
}
bool const needs_join = (pthread_cancel(_tid.meta_data->pt) == 0);
{
if (needs_join) {
int const ret = pthread_join(_tid.meta_data->pt, 0);
if (ret)
PWRN("pthread_join unexpectedly returned with %d (errno=%d)",