Commit 7dfa2a34 by FritzFlorian

Move thread local state OUT of thread object.

This requires the user to explicitly manage the storage duration/place of the thread local state, making a fine grained memory allocation simpler.
parent 73ed4960
Pipeline #1098 passed with stages
in 1 minute 35 seconds
......@@ -8,7 +8,6 @@ int global = 0;
int main() {
// Try to use every feature, to trigger the prohibited use of new if found somewhere
auto t1 = create_thread([] (){}, 0);
t1.start();
auto t1 = start_thread([] (){});
t1.join();
}
......@@ -25,14 +25,28 @@ namespace pls {
pthread_yield();
}
/**
* Retrieves the local state pointer.
*
* @tparam T The type of the state that is stored.
* @return The state pointer hold for this thread.
*/
template<typename T>
static T* state() {
return reinterpret_cast<T*>(pthread_getspecific(local_storage_key_));
}
/**
* Stores a pointer to the thread local state object.
* The memory management for this has to be done by the user,
* we only keep the pointer.
*
* @tparam T The type of the state that is stored.
* @param state_pointer A pointer to the threads state object.
*/
template<typename T>
static void set_state(const T& state) {
*reinterpret_cast<T*>(pthread_getspecific(local_storage_key_)) = state;
static void set_state(T* state_pointer) {
pthread_setspecific(this_thread::local_storage_key_, (void*)state_pointer);
}
};
......@@ -41,26 +55,24 @@ namespace pls {
friend class this_thread;
// Keep a copy of the function (lambda) in this object to make sure it is valid when called!
Function function_;
// Keep the local state we hold in here
State state_;
State* state_pointer_;
// Keep handle to native implementation
pthread_t pthread_thread_;
static void* start_pthread_internal(void* thread_pointer) {
auto my_thread = reinterpret_cast<thread*>(thread_pointer);
pthread_setspecific(this_thread::local_storage_key_, (void*)&my_thread->state_);
this_thread::set_state(my_thread->state_pointer_);
my_thread->function_();
pthread_exit(nullptr);
}
public:
explicit thread(const Function& function, const State& state):
function_{function},
state_{state},
pthread_thread_{} {}
explicit thread(const Function& function, State* state_pointer):
function_{function},
state_pointer_{state_pointer},
pthread_thread_{} {
void start() {
if (!this_thread::local_storage_key_initialized_) {
pthread_key_create(&this_thread::local_storage_key_, nullptr);
this_thread::local_storage_key_initialized_ = true;
......@@ -82,10 +94,14 @@ namespace pls {
};
template<typename Function, typename State>
thread<Function, State> create_thread(const Function& function, const State& state) {
return thread<Function, State>(function, state);
thread<Function, State> start_thread(const Function& function, State* state_pointer) {
return thread<Function, State>(function, state_pointer);
}
template<typename Function>
thread<Function, void> start_thread(const Function& function) {
return thread<Function, void>(function, nullptr);
}
}
}
......
......@@ -12,18 +12,18 @@ static vector<int> local_value_two;
TEST_CASE( "thread creation and joining", "[internal/base/thread.h]") {
visited = false;
auto t1 = create_thread([]() { visited = true; }, 0);
t1.start();
auto t1 = start_thread([]() { visited = true; });
t1.join();
REQUIRE(visited);
}
TEST_CASE( "thread state", "[internal/base/thread.h]") {
auto t1 = create_thread([]() { local_value_1 = *this_thread::state<int>(); }, 1);
auto t2 = create_thread([]() { local_value_two = *this_thread::state<vector<int>>(); }, vector<int>{1, 2});
t1.start();
t2.start();
int state_one = 1;
vector<int> state_two{1, 2};
auto t1 = start_thread([]() { local_value_1 = *this_thread::state<int>(); }, &state_one);
auto t2 = start_thread([]() { local_value_two = *this_thread::state<vector<int>>(); }, &state_two);
t1.join();
t2.join();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment