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)

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