|
Boost : |
From: williamkempf_at_[hidden]
Date: 2001-06-29 10:59:31
--- In boost_at_y..., John Max Skaller <skaller_at_o...> wrote:
> williamkempf_at_h... wrote:
>
> > Considering all of the discussion so far, I think this slightly
> > modified interface is the best design so far:
> >
> > class thread : boost::noncopyable
> > {
>
> What? only one kind of thread allowed?
> Shouldn't this class use pure virtual methods?
Others have argued this. Mostly, I assume, because of influence from
other OO thread libraries such as Java threads, J/Threads and MFC
threads. Frankly, having used such designs extensively myself, I
don't like them. They add complexity and overhead that often make my
own coding using them less than elegant, while adding no benefit at
all. This is akin to making a class such as std::vector a base
class, which others have made, but most think would have been a
mistake. There's nothing in thread that's polymorphic. Designs that
make thread a base class add an artifical run() method as the
polymorphic operation and this is the actual "threadproc". Harnesses
have to be used to make this run() entry point do what we really
wanted in the first place... run some arbitrary method outside of the
thread class.
If you truly want a thread_base class you can easily build one from a
non-derivable thread class, adding overhead and complexity *ONLY* to
objects that need/want this sort of thing. On the other hand if we
start with a thread class meant to be derived from we're stuck with
the complexity and overhead for every use.
> > public:
> > thread(detail::threadproc proc); // Starts the thread
>
> Prevents wrapping native thread handle.
Not necessarily, but we don't want to expose this sort of
functionality on the interface any way. Such an extension would be a
non-standard, non-portable extension.
> Doesn't allow any data to be passed to the thread.
Simply wrong. The thread proc is a function object, and function
objects, unlike function pointers, can have state.
> > ~thread(); // If not detached, detaches the thread
>
> > // To allow us to not need self(), at least for
> > // comparison.
> > bool is_current() const;
>
> Semantics?
Given in other posts.
> > bool is_alive() const;
>
> Semantics? This is slippery. A return value of 'true' is
useless.
> Only a return value of 'false' is useful, and 'true' must be
> returned even if the thread is not started yet.
*sigh* Several people have tried to argue this, but the reality is
that a polling operation such as this can be, and has been, put to
effective use. You're correct, a return value of true doesn't
gaurantee the thread is still running by the next instruction so you
can only rely on return values of false, but that's enough
information to be put to effective use in "busy waiting operations".
> > // Waits for the thread to finish and then calls detach()
> > void join();
>
> No. Ensures the thread is detached.
> ^^^^^^^
>
> If the thread is already detached, just return.
Neither my wording nor yours is 100% accurate, but the general idea
is understandable enough for now. We'll nail down the "standards
speak" if and when we go with such a design.
> > // I have a feeling this one should go away with this
> > // design, but again I'll leave it in for discussion.
> > static void join_all();
>
> I agree with your feeling. Normative semantics
> are questionable: I guess it must refer to threads
> which have 'thread' class objects as wrappers.
>
> > // These two can probably become free standing methods,
> > // though I like the encapsulation.
> > static void sleep(const xtime& xt);
> > static void yield();
>
> Friends have the same status.
With a slightly more fragile design. Regardless, both of these may
not need to be either static or friends. In fact, for the two
implementations so far they don't have to be. The point of the
concept is that the encapsulation clearly ties the operations to
a "thread concept". Using a namespace would do the same thing, with
the same syntactical use (for the most part).
> > 1) Will other operations that can be performed on threads create
a
> > very real need for a self() method?
>
> Of course. [I'd call it 'get_native_id()']
Uhmm... nope. I'm not exposing the "native" descriptor here.
> > 2) Should there be a join_all().
> >I'm becoming convinced there
> > shouldn't be for various reasons,
>
> I agree.
>
> > 3) Should there be comparison operators? With out copy semantics
> > you'd always be comparing to yourself, so I'd guess not.
>
> Correct with the current design.
>
> > 4) Will the usage of this design make users opt for the native C
> > APIs instead of the Boost.Threads library.
>
> In some cases. This is unavoidable. The worst problem
> is thread creation.
Thread creation is *NOT* a problem here, unless I'm missing some
subtle meaning that you have here. As for it being unavoidable... I
don't agree with that either.
> > 5) Are the join(), detach() and destructor semantics
understandable
> > and usable?
>
> join and detach should NEVER give an error.
> They should 'ensure' that IF they return, THEN a certain
> invariant holds (namely, the designated thread is not
> in a non-detached state: it is detached, dead, or
> otherwise beyond control of this class)
>
> It is important to note I think that IF the designated
> thread has not started, they wait first until it starts,
> THEN wait until it is detached.
We're not on the same wave length here, so there's not much for me to
comment on.
Bill Kempf
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk