Import thread/task_specific variables
This commit is contained in:
parent
d9e6a9e568
commit
8841f5e271
8 changed files with 230 additions and 1 deletions
|
|
@ -96,6 +96,7 @@ set( fc_sources
|
||||||
src/exception.cpp
|
src/exception.cpp
|
||||||
src/variant_object.cpp
|
src/variant_object.cpp
|
||||||
src/thread/thread.cpp
|
src/thread/thread.cpp
|
||||||
|
src/thread/thread_specific.cpp
|
||||||
src/thread/future.cpp
|
src/thread/future.cpp
|
||||||
src/thread/task.cpp
|
src/thread/task.cpp
|
||||||
src/thread/spin_lock.cpp
|
src/thread/spin_lock.cpp
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,25 @@ namespace fc {
|
||||||
struct context;
|
struct context;
|
||||||
class spin_lock;
|
class spin_lock;
|
||||||
|
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
struct specific_data_info
|
||||||
|
{
|
||||||
|
void* value;
|
||||||
|
void (*cleanup)(void*);
|
||||||
|
specific_data_info() :
|
||||||
|
value(0),
|
||||||
|
cleanup(0)
|
||||||
|
{}
|
||||||
|
specific_data_info(void* value, void (*cleanup)(void*)) :
|
||||||
|
value(value),
|
||||||
|
cleanup(cleanup)
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
void* get_task_specific_data(unsigned slot);
|
||||||
|
void set_task_specific_data(unsigned slot, void* new_value, void(*cleanup)(void*));
|
||||||
|
}
|
||||||
|
|
||||||
class task_base : virtual public promise_base {
|
class task_base : virtual public promise_base {
|
||||||
public:
|
public:
|
||||||
void run();
|
void run();
|
||||||
|
|
@ -23,6 +42,12 @@ namespace fc {
|
||||||
context* _active_context;
|
context* _active_context;
|
||||||
task_base* _next;
|
task_base* _next;
|
||||||
|
|
||||||
|
// support for task-specific data
|
||||||
|
std::vector<detail::specific_data_info> *_task_specific_data;
|
||||||
|
|
||||||
|
friend void* detail::get_task_specific_data(unsigned slot);
|
||||||
|
friend void detail::set_task_specific_data(unsigned slot, void* new_value, void(*cleanup)(void*));
|
||||||
|
|
||||||
task_base(void* func);
|
task_base(void* func);
|
||||||
// opaque internal / private data used by
|
// opaque internal / private data used by
|
||||||
// thread/thread_private
|
// thread/thread_private
|
||||||
|
|
@ -37,6 +62,8 @@ namespace fc {
|
||||||
void (*_run_functor)(void*, void* );
|
void (*_run_functor)(void*, void* );
|
||||||
|
|
||||||
void run_impl();
|
void run_impl();
|
||||||
|
|
||||||
|
void cleanup_task_specific_data();
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,15 @@ namespace fc {
|
||||||
class time_point;
|
class time_point;
|
||||||
class microseconds;
|
class microseconds;
|
||||||
|
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
void* get_thread_specific_data(unsigned slot);
|
||||||
|
void set_thread_specific_data(unsigned slot, void* new_value, void(*cleanup)(void*));
|
||||||
|
unsigned get_next_unused_task_storage_slot();
|
||||||
|
void* get_task_specific_data(unsigned slot);
|
||||||
|
void set_task_specific_data(unsigned slot, void* new_value, void(*cleanup)(void*));
|
||||||
|
}
|
||||||
|
|
||||||
class thread {
|
class thread {
|
||||||
public:
|
public:
|
||||||
thread( const std::string& name = "" );
|
thread( const std::string& name = "" );
|
||||||
|
|
@ -121,6 +130,11 @@ namespace fc {
|
||||||
friend class promise_base;
|
friend class promise_base;
|
||||||
friend class thread_d;
|
friend class thread_d;
|
||||||
friend class mutex;
|
friend class mutex;
|
||||||
|
friend void* detail::get_thread_specific_data(unsigned slot);
|
||||||
|
friend void detail::set_thread_specific_data(unsigned slot, void* new_value, void(*cleanup)(void*));
|
||||||
|
friend unsigned detail::get_next_unused_task_storage_slot();
|
||||||
|
friend void* detail::get_task_specific_data(unsigned slot);
|
||||||
|
friend void detail::set_task_specific_data(unsigned slot, void* new_value, void(*cleanup)(void*));
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
friend class non_preemptable_scope_check;
|
friend class non_preemptable_scope_check;
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
84
include/fc/thread/thread_specific.hpp
Normal file
84
include/fc/thread/thread_specific.hpp
Normal file
|
|
@ -0,0 +1,84 @@
|
||||||
|
#pragma once
|
||||||
|
#include "thread.hpp"
|
||||||
|
|
||||||
|
namespace fc
|
||||||
|
{
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
unsigned get_next_unused_thread_storage_slot();
|
||||||
|
unsigned get_next_unused_task_storage_slot();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
class thread_specific_ptr
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
unsigned slot;
|
||||||
|
public:
|
||||||
|
thread_specific_ptr() :
|
||||||
|
slot(detail::get_next_unused_thread_storage_slot())
|
||||||
|
{}
|
||||||
|
|
||||||
|
T* get() const
|
||||||
|
{
|
||||||
|
return static_cast<T*>(detail::get_thread_specific_data(slot));
|
||||||
|
}
|
||||||
|
T* operator->() const
|
||||||
|
{
|
||||||
|
return get();
|
||||||
|
}
|
||||||
|
T& operator*() const
|
||||||
|
{
|
||||||
|
return *get();
|
||||||
|
}
|
||||||
|
operator bool() const
|
||||||
|
{
|
||||||
|
return get() != static_cast<T*>(0);
|
||||||
|
}
|
||||||
|
static void cleanup_function(void* obj)
|
||||||
|
{
|
||||||
|
delete static_cast<T*>(obj);
|
||||||
|
}
|
||||||
|
void reset(T* new_value = 0)
|
||||||
|
{
|
||||||
|
detail::set_thread_specific_data(slot, new_value, cleanup_function);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
class task_specific_ptr
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
unsigned slot;
|
||||||
|
public:
|
||||||
|
task_specific_ptr() :
|
||||||
|
slot(detail::get_next_unused_task_storage_slot())
|
||||||
|
{}
|
||||||
|
|
||||||
|
T* get() const
|
||||||
|
{
|
||||||
|
return static_cast<T*>(detail::get_task_specific_data(slot));
|
||||||
|
}
|
||||||
|
T* operator->() const
|
||||||
|
{
|
||||||
|
return get();
|
||||||
|
}
|
||||||
|
T& operator*() const
|
||||||
|
{
|
||||||
|
return *get();
|
||||||
|
}
|
||||||
|
operator bool() const
|
||||||
|
{
|
||||||
|
return get() != static_cast<T*>(0);
|
||||||
|
}
|
||||||
|
static void cleanup_function(void* obj)
|
||||||
|
{
|
||||||
|
delete static_cast<T*>(obj);
|
||||||
|
}
|
||||||
|
void reset(T* new_value = 0)
|
||||||
|
{
|
||||||
|
detail::set_task_specific_data(slot, new_value, cleanup_function);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -20,6 +20,7 @@ namespace fc {
|
||||||
_posted_num(0),
|
_posted_num(0),
|
||||||
_active_context(nullptr),
|
_active_context(nullptr),
|
||||||
_next(nullptr),
|
_next(nullptr),
|
||||||
|
_task_specific_data(nullptr),
|
||||||
_promise_impl(nullptr),
|
_promise_impl(nullptr),
|
||||||
_functor(func){
|
_functor(func){
|
||||||
}
|
}
|
||||||
|
|
@ -63,6 +64,7 @@ namespace fc {
|
||||||
}
|
}
|
||||||
|
|
||||||
task_base::~task_base() {
|
task_base::~task_base() {
|
||||||
|
cleanup_task_specific_data();
|
||||||
_destroy_functor( _functor );
|
_destroy_functor( _functor );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -71,4 +73,17 @@ namespace fc {
|
||||||
_active_context = c;
|
_active_context = c;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void task_base::cleanup_task_specific_data()
|
||||||
|
{
|
||||||
|
if (_task_specific_data)
|
||||||
|
{
|
||||||
|
for (auto iter = _task_specific_data->begin(); iter != _task_specific_data->end(); ++iter)
|
||||||
|
if (iter->cleanup)
|
||||||
|
iter->cleanup(iter->value);
|
||||||
|
delete _task_specific_data;
|
||||||
|
_task_specific_data = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -201,6 +201,7 @@ namespace fc {
|
||||||
my->check_for_timeouts();
|
my->check_for_timeouts();
|
||||||
}
|
}
|
||||||
my->clear_free_list();
|
my->clear_free_list();
|
||||||
|
my->cleanup_thread_specific_data();
|
||||||
}
|
}
|
||||||
|
|
||||||
void thread::exec() {
|
void thread::exec() {
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,8 @@ namespace fc {
|
||||||
pt_head(0),
|
pt_head(0),
|
||||||
ready_head(0),
|
ready_head(0),
|
||||||
ready_tail(0),
|
ready_tail(0),
|
||||||
blocked(0)
|
blocked(0),
|
||||||
|
next_unused_task_storage_slot(0)
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
,non_preemptable_scope_count(0)
|
,non_preemptable_scope_count(0)
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -87,6 +88,15 @@ namespace fc {
|
||||||
fc::context* ready_tail;
|
fc::context* ready_tail;
|
||||||
|
|
||||||
fc::context* blocked;
|
fc::context* blocked;
|
||||||
|
|
||||||
|
// values for thread specific data objects for this thread
|
||||||
|
std::vector<detail::specific_data_info> thread_specific_data;
|
||||||
|
// values for task_specific data for code executing on a thread that's
|
||||||
|
// not a task launched by async (usually the default task on the main
|
||||||
|
// thread in a process)
|
||||||
|
std::vector<detail::specific_data_info> non_task_specific_data;
|
||||||
|
unsigned next_unused_task_storage_slot;
|
||||||
|
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
unsigned non_preemptable_scope_count;
|
unsigned non_preemptable_scope_count;
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -570,5 +580,17 @@ namespace fc {
|
||||||
|
|
||||||
check_fiber_exceptions();
|
check_fiber_exceptions();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void cleanup_thread_specific_data()
|
||||||
|
{
|
||||||
|
for (auto iter = non_task_specific_data.begin(); iter != non_task_specific_data.end(); ++iter)
|
||||||
|
if (iter->cleanup)
|
||||||
|
iter->cleanup(iter->value);
|
||||||
|
|
||||||
|
for (auto iter = thread_specific_data.begin(); iter != thread_specific_data.end(); ++iter)
|
||||||
|
if (iter->cleanup)
|
||||||
|
iter->cleanup(iter->value);
|
||||||
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
} // namespace fc
|
} // namespace fc
|
||||||
|
|
|
||||||
65
src/thread/thread_specific.cpp
Normal file
65
src/thread/thread_specific.cpp
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
#include <fc/log/logger.hpp>
|
||||||
|
#include <fc/thread/thread_specific.hpp>
|
||||||
|
#include "thread_d.hpp"
|
||||||
|
#include <boost/atomic.hpp>
|
||||||
|
|
||||||
|
namespace fc
|
||||||
|
{
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
boost::atomic<unsigned> thread_specific_slot_counter;
|
||||||
|
unsigned get_next_unused_thread_storage_slot()
|
||||||
|
{
|
||||||
|
return thread_specific_slot_counter.fetch_add(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void* get_specific_data(std::vector<detail::specific_data_info> *specific_data, unsigned slot)
|
||||||
|
{
|
||||||
|
return slot < specific_data->size() ?
|
||||||
|
(*specific_data)[slot].value : nullptr;
|
||||||
|
}
|
||||||
|
void set_specific_data(std::vector<detail::specific_data_info> *specific_data, unsigned slot, void* new_value, void(*cleanup)(void*))
|
||||||
|
{
|
||||||
|
if (slot + 1 > specific_data->size())
|
||||||
|
specific_data->resize(slot + 1);
|
||||||
|
(*specific_data)[slot] = std::move(detail::specific_data_info(new_value, cleanup));
|
||||||
|
}
|
||||||
|
|
||||||
|
void* get_thread_specific_data(unsigned slot)
|
||||||
|
{
|
||||||
|
return get_specific_data(&thread::current().my->thread_specific_data, slot);
|
||||||
|
}
|
||||||
|
void set_thread_specific_data(unsigned slot, void* new_value, void(*cleanup)(void*))
|
||||||
|
{
|
||||||
|
return set_specific_data(&thread::current().my->thread_specific_data, slot, new_value, cleanup);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned get_next_unused_task_storage_slot()
|
||||||
|
{
|
||||||
|
return thread::current().my->next_unused_task_storage_slot++;
|
||||||
|
}
|
||||||
|
void* get_task_specific_data(unsigned slot)
|
||||||
|
{
|
||||||
|
context* current_context = thread::current().my->current;
|
||||||
|
if (!current_context ||
|
||||||
|
!current_context->cur_task)
|
||||||
|
return get_specific_data(&thread::current().my->non_task_specific_data, slot);
|
||||||
|
if (current_context->cur_task->_task_specific_data)
|
||||||
|
return get_specific_data(current_context->cur_task->_task_specific_data, slot);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
void set_task_specific_data(unsigned slot, void* new_value, void(*cleanup)(void*))
|
||||||
|
{
|
||||||
|
context* current_context = thread::current().my->current;
|
||||||
|
if (!current_context ||
|
||||||
|
!current_context->cur_task)
|
||||||
|
set_specific_data(&thread::current().my->non_task_specific_data, slot, new_value, cleanup);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!current_context->cur_task->_task_specific_data)
|
||||||
|
current_context->cur_task->_task_specific_data = new std::vector<detail::specific_data_info>;
|
||||||
|
set_specific_data(current_context->cur_task->_task_specific_data, slot, new_value, cleanup);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // end namespace fc
|
||||||
Loading…
Reference in a new issue