tool chain: TLS support

Fixes #3418
This commit is contained in:
Christian Prochaska
2019-06-14 23:14:32 +02:00
committed by Christian Helmuth
parent 2c47cd5c94
commit ab017607a2
19 changed files with 357 additions and 3 deletions

View File

@ -47,6 +47,7 @@ namespace Genode {
void cxx_demangle(char const*, char*, size_t);
void cxx_current_exception(char *out, size_t size);
void cxx_free_tls(void *thread);
Id_space<Parent::Client> &env_session_id_space();
Env &internal_env();

View File

@ -251,6 +251,8 @@ Thread::~Thread()
_deinit_platform_thread();
_free_stack(_stack);
cxx_free_tls(this);
/*
* We have to detach the trace control dataspace last because
* we cannot invalidate the pointer used by the Trace::Logger

View File

@ -0,0 +1,196 @@
/*
* \brief TLS support ('emutls')
* \author Christian Prochaska
* \date 2019-06-13
*/
/*
* Copyright (C) 2019 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
/* Genode includes */
#include <base/heap.h>
#include <base/log.h>
#include <base/thread.h>
/* base-internal includes */
#include <base/internal/globals.h>
using namespace Genode;
/* implemented in 'malloc_free.cc' */
extern Heap &cxx_heap();
static constexpr bool _verbose = false;
/*
* An emutls object describes the properties of a thread-local variable.
* Structure layout as defined in libgcc's 'emutls.c'.
*/
struct __emutls_object
{
size_t const size; /* size of the variable */
size_t const align; /* alignment of the variable */
void *ptr; /* used in this implementation for an AVL tree with
references to all the thread-local instances */
void const *templ; /* template for initializing a thread-local instance */
};
/*
* AVL node referencing the thread-local variable instance of a specific thread
*/
class Tls_node : public Avl_node<Tls_node>
{
private:
void *_thread; /* key */
void *_address; /* value */
/* Noncopyable */
Tls_node(const Tls_node&);
void operator=(const Tls_node&);
public:
Tls_node(void *thread, void *address)
: _thread(thread), _address(address) { }
void *address() { return _address; }
bool higher(Tls_node *other)
{
return (other->_thread > _thread);
}
Tls_node *find_by_thread(void *thread)
{
if (thread == _thread) return this;
Tls_node *c = child(thread > _thread);
return c ? c->find_by_thread(thread) : nullptr;
}
};
struct Tls_tree : Avl_tree<Tls_node>, List<Tls_tree>::Element { };
/*
* List referencing all AVL trees.
* Needed for freeing all allocated variable instances of a thread.
*/
static List<Tls_tree> &_tls_tree_list()
{
static List<Tls_tree> instance;
return instance;
}
static Lock &_emutls_lock()
{
static Lock instance;
return instance;
}
/*
* Free all thread-local variable instances of the given thread
*/
void Genode::cxx_free_tls(void *thread)
{
Lock::Guard lock_guard(_emutls_lock());
for (Tls_tree *tls_tree = _tls_tree_list().first();
tls_tree; tls_tree = tls_tree->next()) {
if (tls_tree->first()) {
Tls_node *tls_node = tls_tree->first()->find_by_thread(thread);
if (tls_node) {
tls_tree->remove(tls_node);
cxx_heap().free(tls_node->address(), 0);
destroy(cxx_heap(), tls_node);
}
}
}
}
/*
* This function is called when a thread-local variable is accessed.
* It returns the address of the variable for the current thread and
* allocates and initializes the variable when it is accessed for the
* first time by this thread.
*/
extern "C" void *__emutls_get_address(void *obj)
{
Lock::Guard lock_guard(_emutls_lock());
__emutls_object *emutls_object = reinterpret_cast<__emutls_object*>(obj);
if (_verbose)
log(__func__, ": emutls_object: ", emutls_object,
", size: ", emutls_object->size,
", align: ", emutls_object->align,
", ptr: ", emutls_object->ptr,
", templ: ", emutls_object->templ);
if (!emutls_object->ptr) {
/*
* The variable is accessed for the first time by any thread.
* Create an AVL tree which keeps track of all instances of this
* variable.
*/
Tls_tree *tls_tree = new (cxx_heap()) Tls_tree;
_tls_tree_list().insert(tls_tree);
emutls_object->ptr = tls_tree;
}
Tls_tree *tls_tree = static_cast<Tls_tree*>(emutls_object->ptr);
Thread *myself = Thread::myself();
Tls_node *tls_node = nullptr;
if (tls_tree->first())
tls_node = tls_tree->first()->find_by_thread(myself);
if (!tls_node) {
/*
* The variable is accessed for the first time by this thread.
* Allocate and initialize a new variable instance and store a
* reference in the AVL tree.
*/
/* the heap allocates 16-byte aligned */
if ((16 % emutls_object->align) != 0)
Genode::warning(__func__, ": cannot ensure alignment of ",
emutls_object->align, " bytes");
void *address = nullptr;
if (!cxx_heap().alloc(emutls_object->size, &address)) {
Genode::error(__func__,
": could not allocate thread-local variable instance");
return nullptr;
}
if (emutls_object->templ)
memcpy(address, emutls_object->templ, emutls_object->size);
else
memset(address, 0, emutls_object->size);
tls_node = new (cxx_heap()) Tls_node(myself, address);
tls_tree->insert(tls_node);
}
return tls_node->address();
}

View File

@ -30,7 +30,7 @@ using namespace Genode;
static Heap *cxx_heap_ptr;
static Heap &cxx_heap()
Heap &cxx_heap()
{
class Cxx_heap_uninitialized : Exception { };
if (!cxx_heap_ptr)

View File

@ -0,0 +1,62 @@
/*
* \brief Test TLS support
* \author Christian Prochaska
* \date 2019-06-13
*/
#include <base/component.h>
#include <base/log.h>
#include <base/thread.h>
static thread_local int thread_local_x = -1;
static thread_local int thread_local_y;
struct Test_thread : Genode::Thread
{
int _thread_id;
int _x;
int _y;
Test_thread(Genode::Env &env, int thread_id, int x, int y)
: Genode::Thread(env, "test_thread", 16*1024),
_thread_id(thread_id), _x(x), _y(y)
{
start();
}
void entry() override
{
Genode::log("thread ", _thread_id,
" initial: x: ", thread_local_x,
", y: ", thread_local_y);
thread_local_x = _x;
thread_local_y = _y;
Genode::log("thread ", _thread_id,
" : x: ", thread_local_x,
", y: ", thread_local_y);
}
};
void Component::construct(Genode::Env &env)
{
using namespace Genode;
Genode::log("main initial: x: ", thread_local_x,
", y: ", thread_local_y);
thread_local_x = 5;
thread_local_y = 6;
for (int i = 0; i < 2; i++) {
Test_thread test_thread(env, i, (i * 2) + 1, (i * 2) + 2);
test_thread.join();
}
Genode::log("main : x: ", thread_local_x,
", y: ", thread_local_y);
}

View File

@ -0,0 +1,3 @@
TARGET = test-tls
SRC_CC = main.cc
LIBS = base