mirror of
https://github.com/genodelabs/genode.git
synced 2025-06-18 15:18:20 +00:00
Rework the internal lock interface
With this patch, the 'futex' syscall gets used for blocking and unblocking of threads in the Linux-specific lock implementation. The 'Native_thread_id' type, which was previously used in the lock-internal 'Applicant' class to identify a thread to be woken up, was not suitable anymore for implementing this change. With this patch, the 'Thread_base*' type gets used instead, which also has the positive effect of making the public 'cancelable_lock.h' header file platform-independent. Fixes #646.
This commit is contained in:
committed by
Norman Feske
parent
7fef0ba931
commit
a99193ad90
@ -25,6 +25,9 @@
|
||||
#include <linux_syscalls.h>
|
||||
|
||||
|
||||
extern int main_thread_futex_counter;
|
||||
|
||||
|
||||
/**
|
||||
* Resolve 'Thread_base::myself' when not linking the thread library
|
||||
*
|
||||
@ -42,32 +45,16 @@ static inline void thread_yield()
|
||||
}
|
||||
|
||||
|
||||
static inline bool thread_check_stopped_and_restart(Genode::Native_thread_id tid)
|
||||
static inline bool thread_check_stopped_and_restart(Genode::Thread_base *thread_base)
|
||||
{
|
||||
lx_tgkill(tid.pid, tid.tid, LX_SIGUSR1);
|
||||
return true;
|
||||
const int *futex_counter_ptr = thread_base ?
|
||||
&thread_base->tid().futex_counter :
|
||||
&main_thread_futex_counter;
|
||||
return lx_futex(futex_counter_ptr, LX_FUTEX_WAKE, 1);
|
||||
}
|
||||
|
||||
|
||||
static inline Genode::Native_thread_id thread_get_my_native_id()
|
||||
{
|
||||
return Genode::Native_thread_id(lx_gettid(), lx_getpid());
|
||||
}
|
||||
|
||||
|
||||
static inline Genode::Native_thread_id thread_invalid_id()
|
||||
{
|
||||
return Genode::Native_thread_id();
|
||||
}
|
||||
|
||||
|
||||
static inline bool thread_id_valid(Genode::Native_thread_id tid)
|
||||
{
|
||||
return (tid.pid != 0);
|
||||
}
|
||||
|
||||
|
||||
static inline void thread_switch_to(Genode::Native_thread_id tid)
|
||||
static inline void thread_switch_to(Genode::Thread_base *thread_base)
|
||||
{
|
||||
thread_yield();
|
||||
}
|
||||
@ -75,6 +62,14 @@ static inline void thread_switch_to(Genode::Native_thread_id tid)
|
||||
|
||||
static inline void thread_stop_myself()
|
||||
{
|
||||
struct timespec ts = { 1000, 0 };
|
||||
while (lx_nanosleep(&ts, 0) == 0);
|
||||
/*
|
||||
* Just go to sleep without modifying the counter value. The
|
||||
* 'thread_check_stopped_and_restart()' function will get called
|
||||
* repeatedly until this thread has actually executed the syscall.
|
||||
*/
|
||||
Genode::Thread_base *myself = Genode::Thread_base::myself();
|
||||
const int *futex_counter_ptr = myself ?
|
||||
&myself->tid().futex_counter :
|
||||
&main_thread_futex_counter;
|
||||
lx_futex(futex_counter_ptr, LX_FUTEX_WAIT, 0);
|
||||
}
|
||||
|
@ -203,4 +203,20 @@ inline int lx_connect(int sockfd, const struct sockaddr *serv_addr,
|
||||
#endif /* SYS_socketcall */
|
||||
|
||||
|
||||
/******************************
|
||||
** Linux signal dispatching **
|
||||
******************************/
|
||||
|
||||
inline int lx_pipe(int pipefd[2])
|
||||
{
|
||||
return lx_syscall(SYS_pipe, pipefd);
|
||||
}
|
||||
|
||||
|
||||
inline int lx_read(int fd, void *buf, Genode::size_t count)
|
||||
{
|
||||
return lx_syscall(SYS_read, fd, buf, count);
|
||||
}
|
||||
|
||||
|
||||
#endif /* _CORE__INCLUDE__CORE_LINUX_SYSCALLS_H_ */
|
||||
|
@ -33,20 +33,53 @@ using namespace Genode;
|
||||
static char _core_mem[80*1024*1024];
|
||||
|
||||
|
||||
static Lock _wait_for_exit_lock(Lock::LOCKED); /* wakeup of '_wait_for_exit' */
|
||||
static bool _do_exit = false; /* exit condition */
|
||||
/*
|
||||
* Basic semaphore implementation based on the 'pipe' syscall.
|
||||
*
|
||||
* This alternative implementation is needed to be able to wake up the
|
||||
* blocked main thread from a signal handler executed by the same thread.
|
||||
*/
|
||||
class Pipe_semaphore
|
||||
{
|
||||
private:
|
||||
|
||||
int _pipefd[2];
|
||||
|
||||
public:
|
||||
|
||||
Pipe_semaphore()
|
||||
{
|
||||
lx_pipe(_pipefd);
|
||||
}
|
||||
|
||||
void down()
|
||||
{
|
||||
char dummy;
|
||||
while(lx_read(_pipefd[0], &dummy, 1) != 1);
|
||||
}
|
||||
|
||||
void up()
|
||||
{
|
||||
char dummy;
|
||||
while (lx_write(_pipefd[1], &dummy, 1) != 1);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
static Pipe_semaphore _wait_for_exit_sem; /* wakeup of '_wait_for_exit' */
|
||||
static bool _do_exit = false; /* exit condition */
|
||||
|
||||
|
||||
static void sigint_handler(int signum)
|
||||
{
|
||||
_wait_for_exit_lock.unlock();
|
||||
_do_exit = true;
|
||||
_wait_for_exit_sem.up();
|
||||
}
|
||||
|
||||
|
||||
static void sigchld_handler(int signnum)
|
||||
{
|
||||
_wait_for_exit_lock.unlock();
|
||||
_wait_for_exit_sem.up();
|
||||
}
|
||||
|
||||
|
||||
@ -82,11 +115,10 @@ void Platform::wait_for_exit()
|
||||
/*
|
||||
* Block until a signal occurs.
|
||||
*/
|
||||
try { _wait_for_exit_lock.lock(); }
|
||||
catch (Blocking_canceled) { };
|
||||
_wait_for_exit_sem.down();
|
||||
|
||||
/*
|
||||
* Each time, the '_wait_for_exit_lock' gets unlocked, we could have
|
||||
* Each time, the '_wait_for_exit_sem' gets unlocked, we could have
|
||||
* received either a SIGINT or SIGCHLD. If a SIGINT was received, the
|
||||
* '_exit' condition will be set.
|
||||
*/
|
||||
|
@ -25,6 +25,13 @@
|
||||
__attribute__((weak)) char **lx_environ = (char **)0;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Natively aligned memory location used in the lock implementation
|
||||
*/
|
||||
int main_thread_futex_counter __attribute__((aligned(sizeof(Genode::addr_t))));
|
||||
|
||||
|
||||
static inline void main_thread_bootstrap()
|
||||
{
|
||||
using namespace Genode;
|
||||
|
@ -33,6 +33,7 @@
|
||||
#endif
|
||||
|
||||
/* Linux includes */
|
||||
#include <linux/futex.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include <sched.h>
|
||||
@ -338,6 +339,16 @@ inline int lx_nanosleep(const struct timespec *req, struct timespec *rem)
|
||||
return lx_syscall(SYS_nanosleep, req, rem);
|
||||
}
|
||||
|
||||
enum {
|
||||
LX_FUTEX_WAIT = FUTEX_WAIT,
|
||||
LX_FUTEX_WAKE = FUTEX_WAKE,
|
||||
};
|
||||
|
||||
inline int lx_futex(const int *uaddr, int op, int val)
|
||||
{
|
||||
return lx_syscall(SYS_futex, uaddr, op, val, 0, 0, 0);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Signal set corrsponding to glibc's 'sigset_t'
|
||||
|
@ -130,27 +130,6 @@ 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'
|
||||
*/
|
||||
Barrier construct_lock;
|
||||
|
||||
/**
|
||||
* Used to block the new thread until 'start' is called
|
||||
*/
|
||||
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
|
||||
* thread
|
||||
@ -168,6 +147,128 @@ namespace Genode {
|
||||
* \param thread associated 'Thread_base' object
|
||||
*/
|
||||
Thread_meta_data(Thread_base *thread) : thread_base(thread) { }
|
||||
|
||||
/**
|
||||
* Used to block the constructor until the new thread has initialized
|
||||
* 'id'
|
||||
*/
|
||||
virtual void wait_for_construction() = 0;
|
||||
virtual void constructed() = 0;
|
||||
|
||||
/**
|
||||
* Used to block the new thread until 'start' is called
|
||||
*/
|
||||
virtual void wait_for_start() = 0;
|
||||
virtual void started() = 0;
|
||||
|
||||
/**
|
||||
* Used to block the 'join()' function until the 'entry()' is done
|
||||
*/
|
||||
virtual void wait_for_join() = 0;
|
||||
virtual void joined() = 0;
|
||||
};
|
||||
|
||||
/*
|
||||
* Thread meta data for a thread created by Genode
|
||||
*/
|
||||
class Thread_meta_data_created : public Thread_meta_data
|
||||
{
|
||||
private:
|
||||
|
||||
/**
|
||||
* 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'
|
||||
*/
|
||||
Barrier _construct_lock;
|
||||
|
||||
/**
|
||||
* Used to block the new thread until 'start' is called
|
||||
*/
|
||||
Barrier _start_lock;
|
||||
|
||||
/**
|
||||
* Used to block the 'join()' function until the 'entry()' is done
|
||||
*/
|
||||
Barrier _join_lock;
|
||||
|
||||
public:
|
||||
|
||||
Thread_meta_data_created(Thread_base *thread) : Thread_meta_data(thread) { }
|
||||
|
||||
void wait_for_construction()
|
||||
{
|
||||
_construct_lock.lock();
|
||||
}
|
||||
|
||||
void constructed()
|
||||
{
|
||||
_construct_lock.unlock();
|
||||
}
|
||||
|
||||
void wait_for_start()
|
||||
{
|
||||
_start_lock.lock();
|
||||
}
|
||||
|
||||
void started()
|
||||
{
|
||||
_start_lock.unlock();
|
||||
}
|
||||
|
||||
void wait_for_join()
|
||||
{
|
||||
_join_lock.lock();
|
||||
}
|
||||
|
||||
void joined()
|
||||
{
|
||||
_join_lock.unlock();
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Thread meta data for an adopted thread
|
||||
*/
|
||||
class Thread_meta_data_adopted : public Thread_meta_data
|
||||
{
|
||||
public:
|
||||
|
||||
Thread_meta_data_adopted(Thread_base *thread) : Thread_meta_data(thread) { }
|
||||
|
||||
void wait_for_construction()
|
||||
{
|
||||
PERR("wait_for_construction() called for an adopted thread");
|
||||
}
|
||||
|
||||
void constructed()
|
||||
{
|
||||
PERR("constructed() called for an adopted thread");
|
||||
}
|
||||
|
||||
void wait_for_start()
|
||||
{
|
||||
PERR("wait_for_start() called for an adopted thread");
|
||||
}
|
||||
|
||||
void started()
|
||||
{
|
||||
PERR("started() called for an adopted thread");
|
||||
}
|
||||
|
||||
void wait_for_join()
|
||||
{
|
||||
PERR("wait_for_join() called for an adopted thread");
|
||||
}
|
||||
|
||||
void joined()
|
||||
{
|
||||
PERR("joined() called for an adopted thread");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@ -224,14 +325,14 @@ static void *thread_start(void *arg)
|
||||
adopt_thread(meta_data);
|
||||
|
||||
/* unblock 'Thread_base' constructor */
|
||||
meta_data->construct_lock.unlock();
|
||||
meta_data->constructed();
|
||||
|
||||
/* block until the 'Thread_base::start' gets called */
|
||||
meta_data->start_lock.lock();
|
||||
meta_data->wait_for_start();
|
||||
|
||||
Thread_base::myself()->entry();
|
||||
|
||||
meta_data->join_lock.unlock();
|
||||
meta_data->joined();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -271,7 +372,7 @@ Thread_base *Thread_base::myself()
|
||||
*/
|
||||
Thread_base *thread = (Thread_base *)malloc(sizeof(Thread_base));
|
||||
memset(thread, 0, sizeof(*thread));
|
||||
Thread_meta_data *meta_data = new Thread_meta_data(thread);
|
||||
Thread_meta_data *meta_data = new Thread_meta_data_adopted(thread);
|
||||
|
||||
/*
|
||||
* Initialize 'Thread_base::_tid' using the default constructor of
|
||||
@ -290,13 +391,13 @@ void Thread_base::start()
|
||||
/*
|
||||
* Unblock thread that is supposed to slumber in 'thread_start'.
|
||||
*/
|
||||
_tid.meta_data->start_lock.unlock();
|
||||
_tid.meta_data->started();
|
||||
}
|
||||
|
||||
|
||||
void Thread_base::join()
|
||||
{
|
||||
_tid.meta_data->join_lock.lock();
|
||||
_tid.meta_data->wait_for_join();
|
||||
}
|
||||
|
||||
|
||||
@ -304,7 +405,7 @@ Thread_base::Thread_base(const char *name, size_t stack_size)
|
||||
:
|
||||
_list_element(this)
|
||||
{
|
||||
_tid.meta_data = new (env()->heap()) Thread_meta_data(this);
|
||||
_tid.meta_data = new (env()->heap()) Thread_meta_data_created(this);
|
||||
|
||||
int const ret = pthread_create(&_tid.meta_data->pt, 0, thread_start,
|
||||
_tid.meta_data);
|
||||
@ -315,7 +416,7 @@ Thread_base::Thread_base(const char *name, size_t stack_size)
|
||||
throw Context_alloc_failed();
|
||||
}
|
||||
|
||||
_tid.meta_data->construct_lock.lock();
|
||||
_tid.meta_data->wait_for_construction();
|
||||
|
||||
Linux_cpu_session *cpu = dynamic_cast<Linux_cpu_session *>(env()->cpu_session());
|
||||
|
||||
|
Reference in New Issue
Block a user