Subject: Re: [Boost-bugs] [Boost C++ Libraries] #12657: thread local storage from thread in base object is not available in derived object (TLS_OUT_OF_INDEXES)
From: Boost C++ Libraries (noreply_at_[hidden])
Date: 2017-02-19 11:11:38
#12657: thread local storage from thread in base object is not available in
derived object (TLS_OUT_OF_INDEXES)
---------------------------------+----------------------
Reporter: monfollet.romain@⦠| Owner: anthonyw
Type: Bugs | Status: new
Milestone: To Be Determined | Component: thread
Version: Boost 1.62.0 | Severity: Problem
Resolution: | Keywords:
---------------------------------+----------------------
Description changed by viboes:
Old description:
> Introduction:
> I have a class A. It implements the Active Object design pattern.
> Class A has a thread that pops tasks one at a time ande executes it in
> the thread.
>
> Some class B, inheriting from A, can send tasks to the underlying A
> active object mechanism.
> When piping a boost::this_thread::sleep_for(....) into a task, and when
> finally at the same time the task is being executing in A's thread, and
> you interrupt A's thread, the call to boost::this_thread::sleep_for()
> does not throw any boost::thread_interrupted, thus, i'm waiting for the
> passed time to sleep_for to destroy the object.
> There's no reason the same should not apply to
> boost::condition_varible::wait , which leads to program blocking for the
> destruction of object of Type B.
>
> preconditions:
>
> //////////////////////////////////////////////////////////////////
> class active_object
> {
> public:
> using message = boost::function<void()>;
>
> FOUNDATION_DLL_API active_object();
> virtual FOUNDATION_DLL_API ~active_object();
> virtual FOUNDATION_DLL_API void stop();
> virtual FOUNDATION_DLL_API void start();
>
> protected:
> virtual FOUNDATION_DLL_API void put_message(message const&);
> virtual FOUNDATION_DLL_API void process_messages();
> FOUNDATION_DLL_API boost::thread::id get_id() const;
> FOUNDATION_DLL_API void interrupt();
> private:
> using default_thread =
> boost::scoped_thread<boost::interrupt_and_join_if_joinable>;
> blocking_queue<message> _messages;
> default_thread _worker;
>
> //////////////////////////////////////////////////////////////////
> active_object::active_object() :
> _messages(3000),
> _continue(true)
> {
> _worker = std::move(default_thread([this](){
> process_messages();
> }));
>
> }
>
> active_object::~active_object()
> {
> _continue = false;
> _worker.interrupt();
> }
>
> void active_object::put_message(message const& message){
> _messages.push(message);
> }
> void active_object::interrupt(){
> boost::this_thread::sleep_for(boost::chrono::seconds(10));
> }
>
> void active_object::process_messages(){
>
> try{
> while (_continue){
> try{
> auto message = _messages.pop();
> message();
> }
> catch (std::exception const&){
> }
> }
> }
> catch (boost::thread_interrupted const&){
> std::clog << std::endl << __FUNCTION__ << " thread interrupted"
> << std::endl;
> }
> }
>
> void active_object::start(){
> if (!_continue){
> _continue = true;
> _worker = std::move(default_thread([this](){process_messages();
> }));
> }
> }
>
> void active_object::stop(){
> _continue = false;
> _worker.interrupt();
> }
>
> boost::thread::id active_object::get_id() const{
> return _worker.get_id();
> }
>
> //////////////////////////////////////////////////////////////////
> class interrupting_active_object: public foundation::active_object{
> public:
> //virtual ~mock_active_object(){}
> void spend_time(){
> put_message([this](){
> active_object::interrupt();
> });
> }
> //////////////////////////////////////////////////////////////////
> class failing_active_object: public foundation::active_object{
> public:
> //virtual ~mock_active_object(){}
> void spend_time(){
> put_message([this]({
> failing_active_object::wait_impl(boost::chrono::milliseconds(5000));
> });
> }
> private:
> void wait_impl(boost::chrono::milliseconds time){
> boost::this_thread::sleep_for(time);
> }
> }
>
> //////////////////////////////////////////////////////////////////
> //////////////////////////////////////////////////////////////////
> Observations:
>
> When calling active_object::interrupt(), the interruption works because
> the sleep_for is in the same memory space defined by object of class A.
> when calling failing_active_object::wait_impl(boost::chrono::milliseconds
> time), sleep_for from the derived class object memory, the thread local
> storage is not available (TLS_OUT_OF_INDEXES).
> Thus, it's not possible to interrupt the sleep_for call, leading to
> waiting. If sleep_for were to be replaced by
> boost::condition_varible::wait, i might very well wait for ever for the
> object destruction.
>
> Conclusion
> calls to sleep_for from the object memory space of a derived class,
> knowing the executing thread is in the base class object memory, leads
> to Thread Local Storage from boost not being available. interruption
> behavior is broken through inheritance.
> calls to sleep_for from the object memory space of the base class,
> knowing the executing thread is in the base class object memory, leads
> to Thread Local Storage from boost being available. interruption behavior
> is preserved.
New description:
Introduction:
I have a class A. It implements the Active Object design pattern.
Class A has a thread that pops tasks one at a time ande executes it in the
thread.
Some class B, inheriting from A, can send tasks to the underlying A active
object mechanism.
When piping a boost::this_thread::sleep_for(....) into a task, and when
finally at the same time the task is being executing in A's thread, and
you interrupt A's thread, the call to boost::this_thread::sleep_for() does
not throw any boost::thread_interrupted, thus, i'm waiting for the passed
time to sleep_for to destroy the object.
There's no reason the same should not apply to
boost::condition_varible::wait , which leads to program blocking for the
destruction of object of Type B.
preconditions:
//////////////////////////////////////////////////////////////////
{{{
class active_object
{
public:
using message = boost::function<void()>;
FOUNDATION_DLL_API active_object();
virtual FOUNDATION_DLL_API ~active_object();
virtual FOUNDATION_DLL_API void stop();
virtual FOUNDATION_DLL_API void start();
protected:
virtual FOUNDATION_DLL_API void put_message(message const&);
virtual FOUNDATION_DLL_API void process_messages();
FOUNDATION_DLL_API boost::thread::id get_id() const;
FOUNDATION_DLL_API void interrupt();
private:
using default_thread =
boost::scoped_thread<boost::interrupt_and_join_if_joinable>;
blocking_queue<message> _messages;
default_thread _worker;
//////////////////////////////////////////////////////////////////
active_object::active_object() :
_messages(3000),
_continue(true)
{
_worker = std::move(default_thread([this](){
process_messages();
}));
}
active_object::~active_object()
{
_continue = false;
_worker.interrupt();
}
void active_object::put_message(message const& message){
_messages.push(message);
}
void active_object::interrupt(){
boost::this_thread::sleep_for(boost::chrono::seconds(10));
}
void active_object::process_messages(){
try{
while (_continue){
try{
auto message = _messages.pop();
message();
}
catch (std::exception const&){
}
}
}
catch (boost::thread_interrupted const&){
std::clog << std::endl << __FUNCTION__ << " thread interrupted"
<< std::endl;
}
}
void active_object::start(){
if (!_continue){
_continue = true;
_worker = std::move(default_thread([this](){process_messages();
}));
}
}
void active_object::stop(){
_continue = false;
_worker.interrupt();
}
boost::thread::id active_object::get_id() const{
return _worker.get_id();
}
//////////////////////////////////////////////////////////////////
class interrupting_active_object: public foundation::active_object{
public:
//virtual ~mock_active_object(){}
void spend_time(){
put_message([this](){
active_object::interrupt();
});
}
//////////////////////////////////////////////////////////////////
class failing_active_object: public foundation::active_object{
public:
//virtual ~mock_active_object(){}
void spend_time(){
put_message([this]({
failing_active_object::wait_impl(boost::chrono::milliseconds(5000));
});
}
private:
void wait_impl(boost::chrono::milliseconds time){
boost::this_thread::sleep_for(time);
}
}
}}}
//////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////
Observations:
When calling active_object::interrupt(), the interruption works because
the sleep_for is in the same memory space defined by object of class A.
when calling failing_active_object::wait_impl(boost::chrono::milliseconds
time), sleep_for from the derived class object memory, the thread local
storage is not available (TLS_OUT_OF_INDEXES).
Thus, it's not possible to interrupt the sleep_for call, leading to
waiting. If sleep_for were to be replaced by
boost::condition_varible::wait, i might very well wait for ever for the
object destruction.
Conclusion
calls to sleep_for from the object memory space of a derived class,
knowing the executing thread is in the base class object memory, leads to
Thread Local Storage from boost not being available. interruption behavior
is broken through inheritance.
calls to sleep_for from the object memory space of the base class, knowing
the executing thread is in the base class object memory, leads to Thread
Local Storage from boost being available. interruption behavior is
preserved.
-- -- Ticket URL: <https://svn.boost.org/trac/boost/ticket/12657#comment:2> Boost C++ Libraries <http://www.boost.org/> Boost provides free peer-reviewed portable C++ source libraries.
This archive was generated by hypermail 2.1.7 : 2017-02-19 11:45:20 UTC