From: William E. Kempf (wekempf_at_[hidden])
Date: 2003-02-05 15:04:58
> 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.
You can't "yield" another thread. You can only "yield" the current
thread. This is why yield() is static.
> 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.
Which I don't think is a problem, and is what users would expect.
> 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
> 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).
No, it doesn't make it "impossible". Passing data is accounted for in the
documentation, and is accomplished through the use of function objects.
The Boost.Bind or Boost.Lambda libraries even make this trivial:
void my_thread(int val)
boost::thread thrd(bind(&my_thread, 10));
> 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.
Seperating the interfaces won't prevent mistakes like this.
> 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.
Discussed to death. The PIMPL idiom has costs that are deamed too costly.
> 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.
Self? Why? If it's on operation that can *only* be made on the current
thread, then a static method is a better approach. Otherwise, I could
make a "self" instance and pass it to another thread, which could then
attempt an operation that's not valid for calling on another thread.
> 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.
If this is what I think it is, it's an implementation detail that
shouldn't be exposed to the user.
> 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.
I really don't comprehend how this would be useful.
> 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
> 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.
No, void* is NOT okay. And again, the current design facilities passing
data in a _typesafe_ manner, and with no restrictions on the number of
"parameters". When combined with Boost.Bind it makes parameter passing
much simpler than would be possible with a void* design, not to mention
the typesafety. (Have I mentioned that typesafety is important?)
> 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.
Again, I fully disagree with any void* design. And return values are
possible today, via the same ideas used for passing data, though there's
no "convenience" library like Boost.Bind to help here. Simplifying this
is planned, however.
> h. Explicit termination of a thread, i.e. by calling
> shall not lead to any resource-leaks.
Only possible by throwing an exception, and you don't need library support
for this. I'm not saying that I won't add an exit(), only that there's
not a compelling reason for it, other than completeness of the library.
> i. Creation of a new thread of execution shall not require calls to
> any other
> method than the boost::thread ctor.
True today, so I don't know why you have this requirement here.
> j. The header file shall not expose any implementation specific details.
It doesn't, today. I realize you're referring to PIMPL here, but that
doesn't mean that the current implementation gives users access to
implementation details. And again, PIMPL has been discussed and
> Some additional features I would like to see.
> k. It should be possible to control the behavior of a new thread, e.g.
> policy, scheduling priority and contention scope.
Initial design in the thread_dev branch.
> l. It should be possible to cancel a thread and for a thread to
> control how is
> shall respond to cancellations.
Again, thread_dev branch.
> m. Failure in any operation should be reported by throwing
> exceptions, not by
*chuckles* Hot topic. You really don't want to open that can of worms.
But the current library does throw exceptions. Assertions are used only
for debugging diagnostics on things that simply should never happen unless
there's a bug in the Boost.Threads library. (That said, it's possible
I've missed some error conditions that should be exceptions, especially on
the Win32 platforms where error conditions aren't documented.)
-- William E. Kempf wekempf_at_[hidden]
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk