Hi,


First I would like to apologize if  what I am going to propose have already been
suggested,  discussed and  rejected. If  that is  the case,  please  ignore this
message.

I would like to suggest a new interface (and implementation) for boost:thread. A
problem with  the current version  (1.29.0), as  I see it,  is that it  does not
distinguish between  the two  sides of  a thread. On  the one  side we  have the
creation of a new thread and interaction with that thread, e.g.  joining. On the
other  side we have  the created  thread and  the operations  one might  want to
perform there, e.g. yielding.

True  that the  current implementation  of  boost:thread allows  you create  new
threads,  interact  with the  thread  and  perform  operations from  within  the
thread. The problem is that it is all mixed in a single interface.

If a created thread needs access to an instance of a boost::thread, then it must
be handled in either of two ways.

1. The creating  side and the created thread  must share a single  instance of a
   boost::thread. For example:

   boost::thread* thr ;

   void thr_func()
   {
        thr->yield() ; // Yes I know that boost::thread::yield() is static.
   }

   int main()
   {
        thr = new boost::thread(thr_func) ;
        thr->join() ;
        delete thr ;
   }

   This solution has the obvious problem  of having to share common data between
   two separate threads of execution.


2. The other solution is that the created thread gets its own unique instance of
   a boost::thread using the default ctor. 

   void thr_func()
   {
        boost::thread self ;
        self.yield() ; // Once again, yes I know that yield() is static.
   }

   int main()
   {
        boost::thr thr(thr_func) ;
        thr.join() ;
   }

   This  avoids the  problem of  having  to share  data between  two threads  of
   execution but  introduces another.  There is no  association between  the two
   instances of boost::thread. This  makes it impossible to transfer information
   from the creating side to the created thread (yes, I know this is not part of
   the current boost::thread implementation but I think it should be).


Another problem with a single interface  is the possibility of someone using the
wrong member operation in the wrong context, e.g. calling join() from within the
created thread. Yes, you would be stupid  if you made such a mistake but why not
prevent it all together by a new interface to boost::thread.


Yet  another problem with  the current  implementation is  that the  header file
exposes  implementation  specific  details.   This  makes it  very  likely  that
modification  to  the implementation  or  adding  support  for a  new  threading
environment will require a re-compilation  of all code using boost::thread. Such
implementation specific details  should, if at all possible,  be hidden behind a
pimple.



What I would  like to see is a new boost::thread  implementation which meets the
following requirements.

a. There shall be two interfaces to a thread. One for creation of a thread, from
   here on called  boost::thread. And, one for the created  thread, from here on
   called boost::thread::self.

b. There shall  exist a  single, unique  association between  an instance  of  a
   boost::thread and  an instance of a boost::thread::self.  This association is
   from here on called boost::thread::context.

c. It  shall be possible to  create instances of  boost::thread::self without an
   associated instance  of boost::thread. This is  mainly useful if  one wants a
   boost::thread::self instance for the very first thread of execution, i.e. the
   thread which main() runs in.

d. If one does not want to  perform any thread specific operations from within a
   thread,  then  it  shall  not  be  necessary  to  create  an  instance  of  a
   boost::thread::self.

e. There  shall  be  no  parent-child  relationship  between  an instance  of  a
   boost::thread and an instance of  a boost::thread::self, i.e.  a thread which
   starts yet another thread shall not be required to wait for that other thread
   and termination  of the  first thread  shall not lead  to termination  of the
   other thread.  This  means that an instance of a boost::thread  can go out of
   scope without affecting the associated instance of a boost::thread::self.

f. It shall be possible to send extra information, as an optional extra argument
   to the  boost::thread ctor, to the created  thread. boost::thread::self shall
   offer a method for retrieving this extra information. It is not required that
   this information be passed in a type-safe manner, i.e. void* is okay.

g. It shall  be possible for a thread  to exit with a return value.  It shall be
   possible for  the creating side to  retrieve, as a return  value from join(),
   that  value. It is  not required  that this  value be  passed in  a type-safe
   manner, i.e. void* is okay.

h. Explicit termination of a thread, i.e. by calling boost::thread::self::exit()
   shall not lead to any resource-leaks.

i. Creation of  a new thread of  execution shall not require calls  to any other
   method than the boost::thread ctor.

j. The header file shall not expose any implementation specific details.


Some additional features I would like to see.

k. It should be possible to control  the behavior of a new thread, e.g. schedule
   policy, scheduling priority and contention scope.

l. It should be  possible to cancel a thread and for a  thread to control how is
   shall respond to cancellations.

m. Failure  in any operation should  be reported by throwing  exceptions, not by
   assertions.



With  all these  requirements taken  into account,  I have  come up  with  a new
implementation.  My implementation is limited  to pthread since that is the only
threading  environment I have  access to  and knowledge  about.  Though  I don't
think it  should be  difficult to add  support for other  threading environments
(since most of it can be re-used from the current boost::thread implementation).

If there exist any interest in the full implementation, please let me know and I
will make it publicly available.


Here is a striped down version:

// thread.hpp

namespace boost {

namespace detail {
struct thread_context ;
struct thread_attributes ;
} // namespace detail

class thread : private thread {
public:
        typedef detail::thread_context context ;
        typedef function<void* (const context&)> function ;
        class attributes ;
        class self ;
        explicit thread(const function&, void* xtra_arg= 0) ;
        thread(const attributes&, const function&, void* = 0) ;
        ~thread() ;
        void* join() ; // possibly const
        void cancel() ;
private:
        const scoped_ptr<context> context__ ;
public:
        class self : private noncopyable {
        public:
                self() ;
                explicit self(const context&) ;
                ~self() ;
                bool operator==(const self&) const ;
                bool operator!=(const self&) const ;
                void* arg() const ;
                void detach() ; // possibly const
                void exit(void* return_value = 0) ; // possibly const
                void yield() const ;
                void sleep(const xtime&) const ;
                enum cancel_state { ENABLE, DISABLE } ;
                enum cancel_type  { ASYNCHRONOUS, DEFERRED } ;
                void cancelstate(cancel_state) ; // possibly const
                void canceltype(cancel_type) ; // possibly const
        private:
                const scoped_ptr<context> context__ ;
        } ; // class self
public:
        // class attributes left out
} ; // class thread

} // namespace boost



Here is an short example of how it might be used.

void* hello(const boost::thread::context& context)
{
        const boost::thread::self self(context) ;
        const char* who = static_cast<const char*>(self.arg()) ;
        std::cout << "Hello " << who << "!" << std::endl ;
        return 0 ;
}

int main()
{
        boost::thread::function func(hello) ;
        boost::thread thr(func, static_cast<void*>("world")) ;
        thr.join() ;
}




Here are some of the most interesting implementation details.

Here is  the thread_context.  The reason for  the shared_ptr<pthread_t>  is that
pthread_t is not always copyable.


// thread.cpp
namespace boost {
namespace detail {

struct thread_context {
        thread_context(const boost::thread::function* function = 0, void* arg = 0)
            : function__(function), arg__(arg), thread__(new pthread_t)
        {}
        const boost::thread::function* function__ ;
        void* const arg__ ;
        const shared_ptr<pthread_t> thread__ ;
} ; // struct thread_context

} // namespace detail
} // namespace boost



A  new thread  starts in  thread_proxy(). The  argument to  thread_proxy()  is a
pointer to a thread_context (casted  to void*). The catch(thread_exit&) stuff is
being used  to catch an  exception thrown from  boost::thread::self::exit(). The
reason for that exception is to ensure proper stack-unwinding and destruction of
local  variables.  Please  note  that  thread_exit is  defined  in an  anonymous
namespace within  thread.cpp. Also note that  all other exception  types must be
caught in thread_proxy.

extern "C" {
static void* thread_proxy(void* param)
{
        void* rval = 0 ;
        try {
            boost::scoped_ptr<boost::thread::context> context(
                static_cast<boost::thread::context*>(param)
            ) ;
            rval = (*context->function__)(*context) ;
        }
        catch (thread_exit& x) {
              pthread_exit(x.rval__) ;
        }
        catch (...)
        {}
        return rval ;
}
} // extern "C"


To help in creation of a  new thread I have the function create_thread(). Please
note that the thread_context which thread_proxy receives as an argument is being
created  here.   Unless  creation  of   the  thread  fails,  ownership   of  the
thread_context  is  being  passed  to  thread_proxy  (hence  the  scoped_ptr  in
thread_proxy).

namespace boost {
namespace detail {

void create_thread(const thread_context& context,
                   const thread_attributes* attributes = 0)
{
        boost::thread::context* self_context =
                   new bost::thread::context(context) ;
       const int rc = pthread_create(
          &*context.thread__,
          attributes ? &attributes.attributes__ : 0,
          thread_proxy,
          self_context
       ) ;
       if (0 != rc) {
          delete self_context ;
          throw thread_resource_error() ;
       }
} // create_thread

} // namespace detail
} // namespace boost



Here are the boost::thread and boost::thread::self ctors.

namespace boost {

thread::thread(const function& thrfunc, void* arg)
    : context__(new context(&thrfunc,arg))
{
        detail::create_thread(*context__) ;
}

thread::self::self(const thread::context& context)
    : context__(new thread::context(context))
{}

} // namespace boost



As this posting has  already become too long, I will leave  the rest out until I
get some feedback.




/Ove Svensson

_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

Reply via email to