mirror of
https://github.com/genodelabs/genode.git
synced 2025-03-11 06:54:18 +00:00
parent
8d13121e84
commit
59459e60e7
@ -50,9 +50,8 @@ class Genode::Thread
|
|||||||
typedef Cpu_session::Name Name;
|
typedef Cpu_session::Name Name;
|
||||||
typedef Cpu_session::Weight Weight;
|
typedef Cpu_session::Weight Weight;
|
||||||
|
|
||||||
struct Stack_info { addr_t base; addr_t top; };
|
struct Stack_info { addr_t base; addr_t top;
|
||||||
|
addr_t libc_tls_pointer_offset; };
|
||||||
struct Tls { struct Base; Base *ptr; };
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
@ -152,17 +151,6 @@ class Genode::Thread
|
|||||||
*/
|
*/
|
||||||
static Trace::Logger *_logger();
|
static Trace::Logger *_logger();
|
||||||
|
|
||||||
/**
|
|
||||||
* Base pointer to thread-local storage
|
|
||||||
*
|
|
||||||
* The opaque pointer allows higher-level thread libraries (i.e.,
|
|
||||||
* pthread) to implement TLS. It should never be used outside such
|
|
||||||
* libraries.
|
|
||||||
*/
|
|
||||||
Tls _tls { };
|
|
||||||
|
|
||||||
friend class Tls::Base;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hook for platform-specific constructor supplements
|
* Hook for platform-specific constructor supplements
|
||||||
*
|
*
|
||||||
|
@ -86,6 +86,11 @@ class Genode::Stack
|
|||||||
*/
|
*/
|
||||||
addr_t _stack[1];
|
addr_t _stack[1];
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TLS pointer for libc pthreads
|
||||||
|
*/
|
||||||
|
addr_t _libc_tls_pointer { };
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Thread name, used for debugging
|
* Thread name, used for debugging
|
||||||
*/
|
*/
|
||||||
@ -152,6 +157,15 @@ class Genode::Stack
|
|||||||
*/
|
*/
|
||||||
addr_t base() const { return _base; }
|
addr_t base() const { return _base; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return libc TLS pointer offset relative to end of stack
|
||||||
|
*/
|
||||||
|
addr_t libc_tls_pointer_offset()
|
||||||
|
{
|
||||||
|
return (addr_t)this + sizeof(Stack) -
|
||||||
|
(addr_t)&_libc_tls_pointer;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ensure that the stack has a given minimum size
|
* Ensure that the stack has a given minimum size
|
||||||
*
|
*
|
||||||
|
@ -175,7 +175,8 @@ Thread::Stack_info Thread::mystack()
|
|||||||
{
|
{
|
||||||
addr_t base = Stack_allocator::addr_to_base(&base);
|
addr_t base = Stack_allocator::addr_to_base(&base);
|
||||||
Stack *stack = Stack_allocator::base_to_stack(base);
|
Stack *stack = Stack_allocator::base_to_stack(base);
|
||||||
return { stack->base(), stack->top() };
|
return { stack->base(), stack->top(),
|
||||||
|
stack_virtual_size() - stack->libc_tls_pointer_offset() };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -973,14 +973,10 @@ _ZN4Libc19Select_handler_baseC2Ev T
|
|||||||
_ZN4Libc19Select_handler_baseD1Ev T
|
_ZN4Libc19Select_handler_baseD1Ev T
|
||||||
_ZN4Libc19Select_handler_baseD2Ev T
|
_ZN4Libc19Select_handler_baseD2Ev T
|
||||||
_ZN4Libc10resume_allEv T
|
_ZN4Libc10resume_allEv T
|
||||||
|
_ZN4Libc7PthreadC2ERN6Genode6ThreadEPv T
|
||||||
_ZN4Libc7suspendERNS_15Suspend_functorEm T
|
_ZN4Libc7suspendERNS_15Suspend_functorEm T
|
||||||
_Z16pthread_registryv T
|
|
||||||
_ZN4Libc16Pthread_registry6insertERNS_7PthreadE T
|
|
||||||
_ZN4Libc16Pthread_registry6removeERNS_7PthreadE T
|
|
||||||
_ZN4Libc16Pthread_registry7cleanupEPNS_7PthreadE T
|
|
||||||
_ZN4Libc16Pthread_registry8containsERNS_7PthreadE T
|
|
||||||
_ZN4Libc14pthread_createEPP7pthreadPFPvS3_ES3_mPKcPN6Genode11Cpu_sessionENS8_8Affinity8LocationE T
|
_ZN4Libc14pthread_createEPP7pthreadPFPvS3_ES3_mPKcPN6Genode11Cpu_sessionENS8_8Affinity8LocationE T
|
||||||
_ZN4Libc14pthread_createEPP7pthreadRN6Genode6ThreadE T
|
_ZN4Libc14pthread_createEPP7pthreadRN6Genode6ThreadEPv T
|
||||||
|
|
||||||
#
|
#
|
||||||
# Libc plugin interface
|
# Libc plugin interface
|
||||||
|
@ -34,42 +34,28 @@
|
|||||||
namespace Libc {
|
namespace Libc {
|
||||||
|
|
||||||
struct Pthread;
|
struct Pthread;
|
||||||
struct Pthread_registry;
|
|
||||||
struct Pthread_blockade;
|
struct Pthread_blockade;
|
||||||
|
struct Pthread_cleanup;
|
||||||
struct Pthread_job;
|
struct Pthread_job;
|
||||||
struct Pthread_mutex;
|
struct Pthread_mutex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
class Libc::Pthread_cleanup
|
||||||
* Used by 'pthread_self()' to find out if the current thread is an alien
|
|
||||||
* thread.
|
|
||||||
*/
|
|
||||||
class Libc::Pthread_registry
|
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
|
|
||||||
enum { MAX_NUM_PTHREADS = 128 };
|
|
||||||
|
|
||||||
Pthread *_array[MAX_NUM_PTHREADS] = { 0 };
|
|
||||||
|
|
||||||
/* thread to be destroyed on next 'cleanup()' call */
|
/* thread to be destroyed on next 'cleanup()' call */
|
||||||
Pthread *_cleanup_thread { nullptr };
|
Pthread *_cleanup_thread { nullptr };
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
void insert(Pthread &thread);
|
|
||||||
|
|
||||||
void remove(Pthread &thread);
|
|
||||||
|
|
||||||
bool contains(Pthread &thread);
|
|
||||||
|
|
||||||
/* destroy '_cleanup_thread' and register another one if given */
|
/* destroy '_cleanup_thread' and register another one if given */
|
||||||
void cleanup(Pthread *new_cleanup_thread = nullptr);
|
void cleanup(Pthread *new_cleanup_thread = nullptr);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
Libc::Pthread_registry &pthread_registry();
|
Libc::Pthread_cleanup &pthread_cleanup();
|
||||||
|
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
@ -89,36 +75,7 @@ extern "C" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
struct Genode::Thread::Tls::Base
|
struct Libc::Pthread : Noncopyable
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Register thread-local-storage object at Genode thread
|
|
||||||
*/
|
|
||||||
static void tls(Thread &thread, Tls::Base &tls)
|
|
||||||
{
|
|
||||||
thread._tls = Tls { &tls };
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Undefined : Exception { };
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Obtain thread-local-storage object for the calling thread
|
|
||||||
*
|
|
||||||
* \throw Undefined
|
|
||||||
*/
|
|
||||||
static Tls::Base &tls()
|
|
||||||
{
|
|
||||||
Thread &myself = *Thread::myself();
|
|
||||||
|
|
||||||
if (!myself._tls.ptr)
|
|
||||||
throw Undefined();
|
|
||||||
|
|
||||||
return *myself._tls.ptr;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
struct Libc::Pthread : Noncopyable, Thread::Tls::Base
|
|
||||||
{
|
{
|
||||||
typedef void *(*start_routine_t) (void *);
|
typedef void *(*start_routine_t) (void *);
|
||||||
|
|
||||||
@ -132,6 +89,8 @@ struct Libc::Pthread : Noncopyable, Thread::Tls::Base
|
|||||||
void *&_stack_addr;
|
void *&_stack_addr;
|
||||||
size_t &_stack_size;
|
size_t &_stack_size;
|
||||||
|
|
||||||
|
Pthread *_pthread;
|
||||||
|
|
||||||
enum { WEIGHT = Cpu_session::Weight::DEFAULT_WEIGHT };
|
enum { WEIGHT = Cpu_session::Weight::DEFAULT_WEIGHT };
|
||||||
|
|
||||||
/* 'stack_addr_out' and 'stack_size_out' are written when the thread starts */
|
/* 'stack_addr_out' and 'stack_size_out' are written when the thread starts */
|
||||||
@ -139,11 +98,13 @@ struct Libc::Pthread : Noncopyable, Thread::Tls::Base
|
|||||||
Cpu_session *cpu,
|
Cpu_session *cpu,
|
||||||
Affinity::Location location,
|
Affinity::Location location,
|
||||||
start_routine_t start_routine, void *arg,
|
start_routine_t start_routine, void *arg,
|
||||||
void *&stack_addr_out, size_t &stack_size_out)
|
void *&stack_addr_out, size_t &stack_size_out,
|
||||||
|
Pthread *pthread)
|
||||||
:
|
:
|
||||||
Thread(WEIGHT, name, stack_size, Type::NORMAL, cpu, location),
|
Thread(WEIGHT, name, stack_size, Type::NORMAL, cpu, location),
|
||||||
_start_routine(start_routine), _arg(arg),
|
_start_routine(start_routine), _arg(arg),
|
||||||
_stack_addr(stack_addr_out), _stack_size(stack_size_out)
|
_stack_addr(stack_addr_out), _stack_size(stack_size_out),
|
||||||
|
_pthread(pthread)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
void entry() override;
|
void entry() override;
|
||||||
@ -166,13 +127,6 @@ struct Libc::Pthread : Noncopyable, Thread::Tls::Base
|
|||||||
*/
|
*/
|
||||||
Thread &_thread;
|
Thread &_thread;
|
||||||
|
|
||||||
void _associate_thread_with_pthread()
|
|
||||||
{
|
|
||||||
Thread::Tls::Base::tls(_thread, *this);
|
|
||||||
pthread_registry().cleanup();
|
|
||||||
pthread_registry().insert(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _exiting = false;
|
bool _exiting = false;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -212,6 +166,22 @@ struct Libc::Pthread : Noncopyable, Thread::Tls::Base
|
|||||||
|
|
||||||
List<Cleanup_handler> _cleanup_handlers;
|
List<Cleanup_handler> _cleanup_handlers;
|
||||||
|
|
||||||
|
/* TLS support */
|
||||||
|
|
||||||
|
/* mask to obtain stack virtual base from address of stack variable */
|
||||||
|
static size_t _stack_virtual_base_mask;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Offset of TLS pointer relative to base address of a thread's
|
||||||
|
* virtual stack area.
|
||||||
|
*/
|
||||||
|
static size_t _tls_pointer_offset;
|
||||||
|
|
||||||
|
/* initialize TLS pointer on given stack */
|
||||||
|
static void _tls_pointer(void *stack_address, Pthread *pthread);
|
||||||
|
|
||||||
|
void const *_tls_data[PTHREAD_KEYS_MAX] { };
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
int thread_local_errno = 0;
|
int thread_local_errno = 0;
|
||||||
@ -225,31 +195,32 @@ struct Libc::Pthread : Noncopyable, Thread::Tls::Base
|
|||||||
:
|
:
|
||||||
_thread(_construct_thread_object(name, stack_size, cpu, location,
|
_thread(_construct_thread_object(name, stack_size, cpu, location,
|
||||||
start_routine, arg,
|
start_routine, arg,
|
||||||
_stack_addr, _stack_size))
|
_stack_addr, _stack_size, this))
|
||||||
{
|
{
|
||||||
_associate_thread_with_pthread();
|
pthread_cleanup().cleanup();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor to create pthread object out of existing thread,
|
* Constructor to create pthread object out of existing thread,
|
||||||
* i.e., the main thread
|
* i.e., the main thread or a VirtualBox thread
|
||||||
|
*
|
||||||
|
* The 'stack_address' argument can be any address on the stack
|
||||||
|
* of 'existing_thread'. It is needed to locate the correct
|
||||||
|
* TLS pointer to initialize, because
|
||||||
|
*
|
||||||
|
* - the main thread uses a secondary stack, so
|
||||||
|
* 'existing_thread.stack_top()' would be the
|
||||||
|
* wrong stack for the main thread
|
||||||
|
*
|
||||||
|
* - VirtualBox EMT threads have this constructor called
|
||||||
|
* from a different thread than 'existing_thread', so
|
||||||
|
* the address of a local stack variable would belong to
|
||||||
|
* the wrong stack for those threads
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
Pthread(Thread &existing_thread)
|
Pthread(Thread &existing_thread, void *stack_address);
|
||||||
:
|
|
||||||
_thread(existing_thread)
|
|
||||||
{
|
|
||||||
/* obtain stack attributes of main thread */
|
|
||||||
Thread::Stack_info info = Thread::mystack();
|
|
||||||
_stack_addr = (void *)info.base;
|
|
||||||
_stack_size = info.top - info.base;
|
|
||||||
|
|
||||||
_associate_thread_with_pthread();
|
static void init_tls_support();
|
||||||
}
|
|
||||||
|
|
||||||
~Pthread()
|
|
||||||
{
|
|
||||||
pthread_registry().remove(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void start() { _thread.start(); }
|
void start() { _thread.start(); }
|
||||||
|
|
||||||
@ -276,13 +247,15 @@ struct Libc::Pthread : Noncopyable, Thread::Tls::Base
|
|||||||
|
|
||||||
_detach_blockade.block();
|
_detach_blockade.block();
|
||||||
|
|
||||||
pthread_registry().cleanup(this);
|
pthread_cleanup().cleanup(this);
|
||||||
sleep_forever();
|
sleep_forever();
|
||||||
}
|
}
|
||||||
|
|
||||||
void *stack_addr() const { return _stack_addr; }
|
void *stack_addr() const { return _stack_addr; }
|
||||||
size_t stack_size() const { return _stack_size; }
|
size_t stack_size() const { return _stack_size; }
|
||||||
|
|
||||||
|
static Pthread *myself();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Push a cleanup handler to the cancellation cleanup stack.
|
* Push a cleanup handler to the cancellation cleanup stack.
|
||||||
*/
|
*/
|
||||||
@ -312,6 +285,16 @@ struct Libc::Pthread : Noncopyable, Thread::Tls::Base
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setspecific(pthread_key_t key, void const *value)
|
||||||
|
{
|
||||||
|
_tls_data[key] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void const *getspecific(pthread_key_t key)
|
||||||
|
{
|
||||||
|
return _tls_data[key];
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@ namespace Libc {
|
|||||||
size_t stack_size, char const * name,
|
size_t stack_size, char const * name,
|
||||||
Cpu_session * cpu, Affinity::Location location);
|
Cpu_session * cpu, Affinity::Location location);
|
||||||
|
|
||||||
int pthread_create(pthread_t *, Thread &);
|
int pthread_create(pthread_t *, Thread &, void *stack_address);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* _LIBC__INTERNAL__THREAD_CREATE_H_ */
|
#endif /* _LIBC__INTERNAL__THREAD_CREATE_H_ */
|
||||||
|
@ -49,6 +49,8 @@ void Libc::init_pthread_support(Monitor &monitor, Timer_accessor &timer_accessor
|
|||||||
_main_thread_ptr = Thread::myself();
|
_main_thread_ptr = Thread::myself();
|
||||||
_monitor_ptr = &monitor;
|
_monitor_ptr = &monitor;
|
||||||
_timer_accessor_ptr = &timer_accessor;
|
_timer_accessor_ptr = &timer_accessor;
|
||||||
|
|
||||||
|
Pthread::init_tls_support();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -66,17 +68,73 @@ namespace { using Fn = Libc::Monitor::Function_result; }
|
|||||||
** Pthread **
|
** Pthread **
|
||||||
*************/
|
*************/
|
||||||
|
|
||||||
|
size_t Pthread::_stack_virtual_base_mask;
|
||||||
|
size_t Pthread::_tls_pointer_offset;
|
||||||
|
|
||||||
|
|
||||||
void Libc::Pthread::Thread_object::entry()
|
void Libc::Pthread::Thread_object::entry()
|
||||||
{
|
{
|
||||||
/* obtain stack attributes of new thread */
|
/*
|
||||||
|
* Obtain stack attributes of new thread for
|
||||||
|
* 'pthread_attr_get_np()'
|
||||||
|
*/
|
||||||
Thread::Stack_info info = Thread::mystack();
|
Thread::Stack_info info = Thread::mystack();
|
||||||
_stack_addr = (void *)info.base;
|
_stack_addr = (void *)info.base;
|
||||||
_stack_size = info.top - info.base;
|
_stack_size = info.top - info.base;
|
||||||
|
|
||||||
|
_tls_pointer(&info, _pthread);
|
||||||
|
|
||||||
pthread_exit(_start_routine(_arg));
|
pthread_exit(_start_routine(_arg));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Libc::Pthread::_tls_pointer(void *stack_address, Pthread *pthread)
|
||||||
|
{
|
||||||
|
addr_t stack_virtual_base = (addr_t)stack_address &
|
||||||
|
_stack_virtual_base_mask;
|
||||||
|
*(Pthread**)(stack_virtual_base + _tls_pointer_offset) = pthread;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Libc::Pthread::Pthread(Thread &existing_thread, void *stack_address)
|
||||||
|
:
|
||||||
|
_thread(existing_thread)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Obtain stack attributes for 'pthread_attr_get_np()'
|
||||||
|
*
|
||||||
|
* Note: the values might be incorrect for VirtualBox EMT threads,
|
||||||
|
* which have this constructor called from a different thread
|
||||||
|
* than 'existing_thread'.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
Thread::Stack_info info = Thread::mystack();
|
||||||
|
_stack_addr = (void *)info.base;
|
||||||
|
_stack_size = info.top - info.base;
|
||||||
|
|
||||||
|
_tls_pointer(stack_address, this);
|
||||||
|
|
||||||
|
pthread_cleanup().cleanup();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Libc::Pthread::init_tls_support()
|
||||||
|
{
|
||||||
|
Thread::Stack_info info = Thread::mystack();
|
||||||
|
_tls_pointer_offset = info.libc_tls_pointer_offset;
|
||||||
|
_stack_virtual_base_mask = ~(Thread::stack_virtual_size() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Pthread *Libc::Pthread::myself()
|
||||||
|
{
|
||||||
|
int stack_variable;
|
||||||
|
addr_t stack_virtual_base = (addr_t)&stack_variable &
|
||||||
|
_stack_virtual_base_mask;
|
||||||
|
return *(Pthread**)(stack_virtual_base + _tls_pointer_offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void Libc::Pthread::join(void **retval)
|
void Libc::Pthread::join(void **retval)
|
||||||
{
|
{
|
||||||
monitor().monitor([&] {
|
monitor().monitor([&] {
|
||||||
@ -110,50 +168,10 @@ void Libc::Pthread::cancel()
|
|||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Registry
|
* Cleanup
|
||||||
*/
|
*/
|
||||||
|
|
||||||
void Libc::Pthread_registry::insert(Pthread &thread)
|
void Libc::Pthread_cleanup::cleanup(Pthread *new_cleanup_thread)
|
||||||
{
|
|
||||||
/* prevent multiple insertions at the same location */
|
|
||||||
static Mutex insert_mutex;
|
|
||||||
Mutex::Guard guard(insert_mutex);
|
|
||||||
|
|
||||||
for (unsigned int i = 0; i < MAX_NUM_PTHREADS; i++) {
|
|
||||||
if (_array[i] == 0) {
|
|
||||||
_array[i] = &thread;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
error("pthread registry overflow, pthread_self() might fail");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void Libc::Pthread_registry::remove(Pthread &thread)
|
|
||||||
{
|
|
||||||
for (unsigned int i = 0; i < MAX_NUM_PTHREADS; i++) {
|
|
||||||
if (_array[i] == &thread) {
|
|
||||||
_array[i] = 0;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
error("could not remove unknown pthread from registry");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool Libc::Pthread_registry::contains(Pthread &thread)
|
|
||||||
{
|
|
||||||
for (unsigned int i = 0; i < MAX_NUM_PTHREADS; i++)
|
|
||||||
if (_array[i] == &thread)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void Libc::Pthread_registry::cleanup(Pthread *new_cleanup_thread)
|
|
||||||
{
|
{
|
||||||
static Mutex cleanup_mutex;
|
static Mutex cleanup_mutex;
|
||||||
Mutex::Guard guard(cleanup_mutex);
|
Mutex::Guard guard(cleanup_mutex);
|
||||||
@ -167,9 +185,9 @@ void Libc::Pthread_registry::cleanup(Pthread *new_cleanup_thread)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Libc::Pthread_registry &pthread_registry()
|
Libc::Pthread_cleanup &pthread_cleanup()
|
||||||
{
|
{
|
||||||
static Libc::Pthread_registry instance;
|
static Libc::Pthread_cleanup instance;
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -519,8 +537,100 @@ struct Libc::Pthread_mutex_recursive : pthread_mutex
|
|||||||
extern "C" int sem_set_clock(sem_t *sem, clockid_t clock_id);
|
extern "C" int sem_set_clock(sem_t *sem, clockid_t clock_id);
|
||||||
|
|
||||||
|
|
||||||
|
/* TLS */
|
||||||
|
|
||||||
|
class Key_allocator : public Genode::Bit_allocator<PTHREAD_KEYS_MAX>
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
|
||||||
|
Mutex _mutex;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
addr_t alloc_key()
|
||||||
|
{
|
||||||
|
Mutex::Guard guard(_mutex);
|
||||||
|
return alloc();
|
||||||
|
}
|
||||||
|
|
||||||
|
void free_key(addr_t key)
|
||||||
|
{
|
||||||
|
Mutex::Guard guard(_mutex);
|
||||||
|
free(key);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static Key_allocator &key_allocator()
|
||||||
|
{
|
||||||
|
static Key_allocator inst;
|
||||||
|
return inst;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef void (*key_destructor_func)(void*);
|
||||||
|
static key_destructor_func key_destructors[PTHREAD_KEYS_MAX];
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
|
||||||
|
int pthread_key_create(pthread_key_t *key, void (*destructor)(void*))
|
||||||
|
{
|
||||||
|
if (!key)
|
||||||
|
return EINVAL;
|
||||||
|
|
||||||
|
try {
|
||||||
|
*key = key_allocator().alloc_key();
|
||||||
|
key_destructors[*key] = destructor;
|
||||||
|
return 0;
|
||||||
|
} catch (Key_allocator::Out_of_indices) {
|
||||||
|
return EAGAIN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
typeof(pthread_key_create) _pthread_key_create
|
||||||
|
__attribute__((alias("pthread_key_create")));
|
||||||
|
|
||||||
|
|
||||||
|
int pthread_key_delete(pthread_key_t key)
|
||||||
|
{
|
||||||
|
if (key < 0 || key >= PTHREAD_KEYS_MAX)
|
||||||
|
return EINVAL;
|
||||||
|
|
||||||
|
key_destructors[key] = nullptr;
|
||||||
|
key_allocator().free_key(key);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
typeof(pthread_key_delete) _pthread_key_delete
|
||||||
|
__attribute__((alias("pthread_key_delete")));
|
||||||
|
|
||||||
|
|
||||||
|
int pthread_setspecific(pthread_key_t key, const void *value)
|
||||||
|
{
|
||||||
|
if (key < 0 || key >= PTHREAD_KEYS_MAX)
|
||||||
|
return EINVAL;
|
||||||
|
|
||||||
|
pthread_t pthread_myself = pthread_self();
|
||||||
|
pthread_myself->setspecific(key, value);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
typeof(pthread_setspecific) _pthread_setspecific
|
||||||
|
__attribute__((alias("pthread_setspecific")));
|
||||||
|
|
||||||
|
|
||||||
|
void *pthread_getspecific(pthread_key_t key)
|
||||||
|
{
|
||||||
|
if (key < 0 || key >= PTHREAD_KEYS_MAX)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
pthread_t pthread_myself = pthread_self();
|
||||||
|
return (void*)pthread_myself->getspecific(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
typeof(pthread_getspecific) _pthread_getspecific
|
||||||
|
__attribute__((alias("pthread_getspecific")));
|
||||||
|
|
||||||
|
|
||||||
/* Thread */
|
/* Thread */
|
||||||
|
|
||||||
int pthread_join(pthread_t thread, void **retval)
|
int pthread_join(pthread_t thread, void **retval)
|
||||||
@ -577,6 +687,24 @@ extern "C" {
|
|||||||
|
|
||||||
void pthread_exit(void *value_ptr)
|
void pthread_exit(void *value_ptr)
|
||||||
{
|
{
|
||||||
|
/* call TLS key destructors */
|
||||||
|
|
||||||
|
bool at_least_one_destructor_called;
|
||||||
|
|
||||||
|
do {
|
||||||
|
at_least_one_destructor_called = false;
|
||||||
|
for (pthread_key_t key = 0; key < PTHREAD_KEYS_MAX; key++) {
|
||||||
|
if (key_destructors[key]) {
|
||||||
|
void *value = pthread_getspecific(key);
|
||||||
|
if (value) {
|
||||||
|
pthread_setspecific(key, nullptr);
|
||||||
|
key_destructors[key](value);
|
||||||
|
at_least_one_destructor_called = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (at_least_one_destructor_called);
|
||||||
|
|
||||||
pthread_self()->exit(value_ptr);
|
pthread_self()->exit(value_ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -593,14 +721,10 @@ extern "C" {
|
|||||||
|
|
||||||
pthread_t pthread_self(void)
|
pthread_t pthread_self(void)
|
||||||
{
|
{
|
||||||
try {
|
pthread_t pthread_myself = static_cast<pthread_t>(Pthread::myself());
|
||||||
pthread_t pthread_myself =
|
|
||||||
static_cast<pthread_t>(&Thread::Tls::Base::tls());
|
|
||||||
|
|
||||||
if (pthread_registry().contains(*pthread_myself))
|
if (pthread_myself)
|
||||||
return pthread_myself;
|
return pthread_myself;
|
||||||
}
|
|
||||||
catch (Thread::Tls::Base::Undefined) { }
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We pass here if the main thread or an alien thread calls
|
* We pass here if the main thread or an alien thread calls
|
||||||
@ -620,7 +744,7 @@ extern "C" {
|
|||||||
* destruction of the pthread object would also destruct the 'Thread'
|
* destruction of the pthread object would also destruct the 'Thread'
|
||||||
* of the main thread.
|
* of the main thread.
|
||||||
*/
|
*/
|
||||||
return unmanaged_singleton<pthread>(*Thread::myself());
|
return unmanaged_singleton<pthread>(*Thread::myself(), &pthread_myself);
|
||||||
}
|
}
|
||||||
|
|
||||||
typeof(pthread_self) _pthread_self
|
typeof(pthread_self) _pthread_self
|
||||||
@ -1168,136 +1292,6 @@ extern "C" {
|
|||||||
__attribute__((alias("pthread_cond_broadcast")));
|
__attribute__((alias("pthread_cond_broadcast")));
|
||||||
|
|
||||||
|
|
||||||
/* TLS */
|
|
||||||
|
|
||||||
|
|
||||||
struct Key_element : List<Key_element>::Element
|
|
||||||
{
|
|
||||||
const void *thread_base;
|
|
||||||
const void *value;
|
|
||||||
|
|
||||||
Key_element(const void *thread_base, const void *value)
|
|
||||||
: thread_base(thread_base),
|
|
||||||
value(value) { }
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
static Mutex &key_list_mutex()
|
|
||||||
{
|
|
||||||
static Mutex inst { };
|
|
||||||
return inst;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
struct Keys
|
|
||||||
{
|
|
||||||
List<Key_element> key[PTHREAD_KEYS_MAX];
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
static Keys &keys()
|
|
||||||
{
|
|
||||||
static Keys inst { };
|
|
||||||
return inst;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int pthread_key_create(pthread_key_t *key, void (*destructor)(void*))
|
|
||||||
{
|
|
||||||
if (!key)
|
|
||||||
return EINVAL;
|
|
||||||
|
|
||||||
Mutex::Guard guard(key_list_mutex());
|
|
||||||
|
|
||||||
for (int k = 0; k < PTHREAD_KEYS_MAX; k++) {
|
|
||||||
/*
|
|
||||||
* Find an empty key slot and insert an element for the current
|
|
||||||
* thread to mark the key slot as used.
|
|
||||||
*/
|
|
||||||
if (!keys().key[k].first()) {
|
|
||||||
Libc::Allocator alloc { };
|
|
||||||
Key_element *key_element = new (alloc) Key_element(Thread::myself(), 0);
|
|
||||||
keys().key[k].insert(key_element);
|
|
||||||
*key = k;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return EAGAIN;
|
|
||||||
}
|
|
||||||
|
|
||||||
typeof(pthread_key_create) _pthread_key_create
|
|
||||||
__attribute__((alias("pthread_key_create")));
|
|
||||||
|
|
||||||
|
|
||||||
int pthread_key_delete(pthread_key_t key)
|
|
||||||
{
|
|
||||||
if (key < 0 || key >= PTHREAD_KEYS_MAX || !keys().key[key].first())
|
|
||||||
return EINVAL;
|
|
||||||
|
|
||||||
Mutex::Guard guard(key_list_mutex());
|
|
||||||
|
|
||||||
while (Key_element * element = keys().key[key].first()) {
|
|
||||||
keys().key[key].remove(element);
|
|
||||||
Libc::Allocator alloc { };
|
|
||||||
destroy(alloc, element);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
typeof(pthread_key_delete) _pthread_key_delete
|
|
||||||
__attribute__((alias("pthread_key_delete")));
|
|
||||||
|
|
||||||
|
|
||||||
int pthread_setspecific(pthread_key_t key, const void *value)
|
|
||||||
{
|
|
||||||
if (key < 0 || key >= PTHREAD_KEYS_MAX)
|
|
||||||
return EINVAL;
|
|
||||||
|
|
||||||
void *myself = Thread::myself();
|
|
||||||
|
|
||||||
Mutex::Guard guard(key_list_mutex());
|
|
||||||
|
|
||||||
for (Key_element *key_element = keys().key[key].first(); key_element;
|
|
||||||
key_element = key_element->next())
|
|
||||||
if (key_element->thread_base == myself) {
|
|
||||||
key_element->value = value;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* key element does not exist yet - create a new one */
|
|
||||||
Libc::Allocator alloc { };
|
|
||||||
Key_element *key_element = new (alloc) Key_element(Thread::myself(), value);
|
|
||||||
keys().key[key].insert(key_element);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
typeof(pthread_setspecific) _pthread_setspecific
|
|
||||||
__attribute__((alias("pthread_setspecific")));
|
|
||||||
|
|
||||||
|
|
||||||
void *pthread_getspecific(pthread_key_t key)
|
|
||||||
{
|
|
||||||
if (key < 0 || key >= PTHREAD_KEYS_MAX)
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
void *myself = Thread::myself();
|
|
||||||
|
|
||||||
Mutex::Guard guard(key_list_mutex());
|
|
||||||
|
|
||||||
for (Key_element *key_element = keys().key[key].first(); key_element;
|
|
||||||
key_element = key_element->next())
|
|
||||||
if (key_element->thread_base == myself)
|
|
||||||
return (void*)(key_element->value);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
typeof(pthread_getspecific) _pthread_getspecific
|
|
||||||
__attribute__((alias("pthread_getspecific")));
|
|
||||||
|
|
||||||
|
|
||||||
int pthread_once(pthread_once_t *once, void (*init_once)(void))
|
int pthread_once(pthread_once_t *once, void (*init_once)(void))
|
||||||
{
|
{
|
||||||
if (!once || ((once->state != PTHREAD_NEEDS_INIT) &&
|
if (!once || ((once->state != PTHREAD_NEEDS_INIT) &&
|
||||||
|
@ -104,10 +104,11 @@ int Libc::pthread_create(pthread_t *thread,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int Libc::pthread_create(pthread_t *thread, Thread &t)
|
int Libc::pthread_create(pthread_t *thread, Thread &t, void *stack_address)
|
||||||
{
|
{
|
||||||
Libc::Allocator alloc { };
|
Libc::Allocator alloc { };
|
||||||
pthread_t thread_obj = new (alloc) pthread(t);
|
|
||||||
|
pthread_t thread_obj = new (alloc) pthread(t, stack_address);
|
||||||
|
|
||||||
if (!thread_obj)
|
if (!thread_obj)
|
||||||
return EAGAIN;
|
return EAGAIN;
|
||||||
|
@ -456,7 +456,7 @@ struct Usb_ep : Genode::Entrypoint
|
|||||||
void _handle_pthread_registration()
|
void _handle_pthread_registration()
|
||||||
{
|
{
|
||||||
Genode::Thread *myself = Genode::Thread::myself();
|
Genode::Thread *myself = Genode::Thread::myself();
|
||||||
if (!myself || Libc::pthread_create(&_pthread, *myself)) {
|
if (!myself || Libc::pthread_create(&_pthread, *myself, &myself)) {
|
||||||
Genode::error("USB passthough will not work - thread for "
|
Genode::error("USB passthough will not work - thread for "
|
||||||
"pthread registration invalid");
|
"pthread registration invalid");
|
||||||
}
|
}
|
||||||
|
@ -186,7 +186,7 @@ class Nic_client
|
|||||||
void _handle_pthread_registration()
|
void _handle_pthread_registration()
|
||||||
{
|
{
|
||||||
Genode::Thread *myself = Genode::Thread::myself();
|
Genode::Thread *myself = Genode::Thread::myself();
|
||||||
if (!myself || Libc::pthread_create(&_pthread, *myself)) {
|
if (!myself || Libc::pthread_create(&_pthread, *myself, &myself)) {
|
||||||
Genode::error("network will not work - thread for pthread "
|
Genode::error("network will not work - thread for pthread "
|
||||||
"registration invalid");
|
"registration invalid");
|
||||||
return;
|
return;
|
||||||
|
@ -857,7 +857,7 @@ class Vcpu_handler : public Vmm::Vcpu_dispatcher<Genode::Thread>,
|
|||||||
:
|
:
|
||||||
Vmm::Vcpu_dispatcher<Genode::Thread>(env, stack_size, cpu_connection,
|
Vmm::Vcpu_dispatcher<Genode::Thread>(env, stack_size, cpu_connection,
|
||||||
location, name),
|
location, name),
|
||||||
_pthread(*this),
|
_pthread(*this, stack_top()),
|
||||||
_start_routine(start_routine),
|
_start_routine(start_routine),
|
||||||
_start_routine_arg(arg),
|
_start_routine_arg(arg),
|
||||||
_vcpu(cpu_connection, location, pd_vcpu),
|
_vcpu(cpu_connection, location, pd_vcpu),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user