Boost logo

Boost :

From: Alexander Terekhov (terekhov_at_[hidden])
Date: 2001-06-28 13:30:14


===== 1/7 =====

IMHO..

> class thread : boost::noncopyable
> {
> public:
> thread(detail::threadproc proc); // Starts the thread

that does not support inheritance. base class
should not start the thread until the most derived
class constructor completed. btw that is a good
example where post-constructor would really help.

> ~thread(); // If not detached, detaches the thread

thread object should exist at least as long as the
thread function is running. for a non-detached and
still running thread it (actually pre-destructor)
should first join. detached thread should [self-]
destroy associated thread object via TLS (TSD)
value destruction mechanism. thread object pointer
should be saved in TLS slot by the thread function
wrapper. self() should return that thread local
pointer.

> // Considering that this type is noncopyable, these
> // operations are probably not necessary, but I'll
> // leave them in for the sake of discussion.
> bool operator==(const thread& other) const;
> bool operator!=(const thread& other) const;

compare pointers to thread objects.

> // To allow us to not need self(), at least for
> // comparison.
> bool is_current() const;
>
> bool is_alive() const;

quite useless since the result is absolute.

> // Waits for the thread to finish and then calls detach()
> void join();

should be wait only -- there is nothing to detach
after join. also, the "join" interface should provide
an optional mechanism to communicate thread function
return value..

> // Seperates the object from the actual thread of execution
> // allowing the thread to just "evaporate" when finished
> // or when the main thread of execution terminates.
> void detach();

confusing comment.. detach is simply a mechanism to enable
self destruction upon completion of thread function.

> // 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();

should go away.

> // These two can probably become free standing methods,
> // though I like the encapsulation.
> static void sleep(const xtime& xt);
> static void yield();
> };
>
> As it stands, this design is very viable, and is the best of what's
> been discussed. My concerns are:
>
> 1) Will other operations that can be performed on threads create a
> very real need for a self() method?

yes.

> 2) Should there be a join_all(). I'm becoming convinced there

should go away.

> 3) Should there be comparison operators?

should go away.

> 4) Will the usage of this design make users opt for the native C

the design should match 100% of C (posix) interface and
provide some OO extras such as inheritance, integration
of thread cancellation with C++ exceptions, etc..

> 5) Are the join(), detach() and destructor semantics understandable
> and usable? For example, what happens if you call a method on a
> thread object that's been detached?

it depends. if I have a mechanism which communicates the
state of detached thread (e.g. "still running" flag with
mutex and condition variable), all but join/detach is
perfectly legal. w/o such a mechanism that is clearly a
bug since the thread may already disappear..

> I would expect an error. If
> that's the case should join() detach the thread, or simply wait for
> the thread to finish, being a noop if the thread is already
> finished?

simply wait. error/exception if already joined.

> 6) Should sleep() and yeild() be stand alone methods, and why?

yield() is quite useless (taking into account that neither posix
nor win32 program really need it)

> We'll need good rationale statements for this which ever route we
> go. The rationale for making them static methods is to give them
> access to thread internals, but both current target implementations
> do not have this need, and it's possible (probable?) that no
> implementation will need this.

what is the difference with respect to "thread internals" ??

regards,
alexander.

===== 2/7 =====

>IMHO..
>
> > class thread : boost::noncopyable
> > {
> > public:
> > thread(detail::threadproc proc); // Starts the thread
>
>that does not support inheritance. base class
>should not start the thread until the most derived
>class constructor completed. btw that is a good
>example where post-constructor would really help.

I don't intend thread to be inherited from. I personally find such
designs to be unwieldy, and rarely provide any benefit.

> > ~thread(); // If not detached, detaches the thread
>
>thread object should exist at least as long as the
>thread function is running. for a non-detached and
>still running thread it (actually pre-destructor)
>should first join. detached thread should [self-]
>destroy associated thread object via TLS (TSD)
>value destruction mechanism. thread object pointer
>should be saved in TLS slot by the thread function
>wrapper. self() should return that thread local
>pointer.

You've missed the point that started this whole thing. These semantics
lead to a blend between the thread concept and the thread_desc concept,
and the two should rightfully be seperate (though there may be no need
for a thread_desc and/or the thread may be an internal detail never
accessible by the user). Read Bemans original post on this.

> > // To allow us to not need self(), at least for
> > // comparison.
> > bool is_current() const;
> >
> > bool is_alive() const;
>
>quite useless since the result is absolute.

I don't follow you here.

> > // Waits for the thread to finish and then calls detach()
> > void join();
>
>should be wait only -- there is nothing to detach
>after join. also, the "join" interface should provide
>an optional mechanism to communicate thread function
>return value..

A "detached" thread can no longer have any methods called on it. It's
meaningless to call any method on a thread that's detached. However,
several of the methods *are* still meaningful after the thread of
execution has terminated but the thread object is still attached.

> > // Seperates the object from the actual thread of execution
> > // allowing the thread to just "evaporate" when finished
> > // or when the main thread of execution terminates.
> > void detach();
>
>confusing comment.. detach is simply a mechanism to enable
>self destruction upon completion of thread function.

Self destruction of what? It's a poor design idea to self destruct a
C++ object. The detach() method allows the thread of execution to
self destruct, but the thread object shall still exist.

> > // 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();
>
>should go away.

I agree. It did make several things easier to code, however.

> > // These two can probably become free standing methods,
> > // though I like the encapsulation.
> > static void sleep(const xtime& xt);
> > static void yield();
> > };
> >
> > As it stands, this design is very viable, and is the best of what's
> > been discussed. My concerns are:
> >
> > 1) Will other operations that can be performed on threads create a
> > very real need for a self() method?
>
>yes.

Evidence or proof, please. I _feel_ the same way, but with out proof
we won't win this one.

> > 4) Will the usage of this design make users opt for the native C
>
>the design should match 100% of C (posix) interface and
>provide some OO extras such as inheritance, integration
>of thread cancellation with C++ exceptions, etc..

Sorry, but posix isn't the defacto standard here. Matching it 100%
would be a mistake, plain and simple.

> > 5) Are the join(), detach() and destructor semantics
understandable
> > and usable? For example, what happens if you call a method on a
> > thread object that's been detached?
>
>it depends. if I have a mechanism which communicates the
>state of detached thread (e.g. "still running" flag with
>mutex and condition variable), all but join/detach is
>perfectly legal. w/o such a mechanism that is clearly a
>bug since the thread may already disappear..

Calls to such things as is_alive() and is_current() make no sense for a
detached thread. I think you're confusing pthread concepts with the
Boost.Thread concepts here.

> > I would expect an error. If
> > that's the case should join() detach the thread, or simply wait for
> > the thread to finish, being a noop if the thread is already
> > finished?
>
>simply wait. error/exception if already joined.

You've mixed things up here, and also subscribed to semantics that
differ from posix threads, which further confuses me since it's the
first time you've done so. Under posix, a call to pthread_join()
detaches the thread.

> > 6) Should sleep() and yeild() be stand alone methods, and why?
>
>yield() is quite useless (taking into account that neither posix
>nor win32 program really need it)

Win32 programs *frequently* need it. I can't believe that posix
threads are any different. Further, yield() is going to be necessary
for non-preemptive thread implementations.

> > We'll need good rationale statements for this which ever route we
> > go. The rationale for making them static methods is to give them
> > access to thread internals, but both current target implementations
> > do not have this need, and it's possible (probable?) that no
> > implementation will need this.
>
>what is the difference with respect to "thread internals" ??

Static methods have access to the internal (protected and private)
state of the object, while free standing methods do not.

Bill Kempf

===== 3/7 =====

> I don't intend thread to be inherited from. I personally find such
> designs to be unwieldy, and rarely provide any benefit.

well, sure it is a matter of taste but IMHO w/o inheritance there
is not much benefit of providing OO interface on top of plain C
interface.. examples of "reasonable" usage are the following:

- extend base threads with e.g. thread names for tracing/logging
- usage of thread local variables (simply as members of a thread
subclass) saving valuable/limited TLS/TSD key space (some impl.
have only 64 slots/keys).

> You've missed the point that started this whole thing. These
> semantics lead to a blend between the thread concept and the
> thread_desc concept, and the
> two should rightfully be seperate (though there may be no need for a
> thread_desc and/or the thread may be an internal detail never
> accessible by the user). Read Bemans original post on this.

IMHO C++ "thread class" should just have a private C "thread
descriptor" and completely hide it -- replace it via C++
"thread class" instance ptr/ref plus methods, i do not see a
need for both concepts.

> > > // To allow us to not need self(), at least for
> > > // comparison.
> > > bool is_current() const;
> > >
> > > bool is_alive() const;
> >
> >quite useless since the result is absolute.
>
> I don't follow you here.

sorry, i meant obsolete / out-of-date. that is why it is useless --
same story as with lock.islocked()

> > > 1) Will other operations that can be performed on threads create
a
> > > very real need for a self() method?
> >
> >yes.
>
> Evidence or proof, please. I _feel_ the same way, but with out proof
we
> won't win this one.

well.. e.g. to trace the _current_ thread cpuclockid, scheduling
parm(s):

int pthread_getcpuclockid(pthread_t thread_id, clockid_t * clock_id);
int pthread_getschedparam(pthread_t thread, int *restrict policy,
struct sched_param *restrict param);

etc.. also, sometime it is needed to _change_ something associated with
_current_ thread:

int pthread_setschedparam(pthread_t thread, int policy,
const struct sched_param * param);

or just remove the _current_ thread from some registry (e.g. thread
pool).

> You've mixed things up here, and also subscribed to semantics that
differ
> from posix threads, which further confuses me since it's the first
time
> you've done so. Under posix, a call to pthread_join() detaches the
thread.
[...]
> Calls to such things as is_alive() and is_current() make no sense for
a
> detached thread. I think you're confusing pthread concepts with the
> Boost.Thread concepts here.

i am not so sure with respect to "pthread concepts" /
"Boost.Thread concepts" but we certainly have a HUGELY
different understanding of "join" and "detach" concepts.
please read the info attached bellow and then re-read
my comments please... detach IS a mechanism to force
_auto_ cleanup/self-reclamation of internal thread related
storage(ids/..) --> self-destruction which would otherwise
(w/o detach) would need to be done via join (which in addition
provides a mechanism to communicate thread function return
value)

regards,
alexander.

----
2103 Although implementations may have thread IDs that are unique in a
system, applications
2104 should only assume that thread IDs are usable and unique within a
single process. The effect of
2105 calling any of the functions defined in this volume of IEEE Std
1003.1-200x and passing as an
2106 argument the thread ID of a thread from another process is
unspecified. A conforming
2107 implementation is free to reuse a thread ID after the thread
terminates if it was created with the
2108 detachstate attribute set to PTHREAD_CREATE_DETACHED or if
pthread_detach() or
2109 pthread_join ( ) has been called for that thread. If a thread is
detached, its thread ID is invalid for
2110 use as an argument in a call to pthread_detach() or pthread_join
().
33260 The pthread_detach( ) function shall indicate to the
implementation
that storage for the thread
33261 thread can be reclaimed when that thread terminates. If thread
has
not terminated,
33262 pthread_detach( ) shall not cause it to terminate. The effect of
multiple pthread_detach( ) calls on
33263 the same target thread is unspecified.
33279 The pthread_join ()or pthread_detach( ) functions should
eventually
be called for every thread that
33280 is created so that storage associated with the thread may be
reclaimed.
33281 It has been suggested that a ''detach'' function is not
necessary;
the detachstate thread creation
33282 attribute is sufficient, since a thread need never be dynamically
detached. However, need arises
33283 in at least two cases:
33284 1. In a cancelation handler for a pthread_join ( ) it is nearly
essential to have a pthread_detach()
33285 function in order to detach the thread on which pthread_join ( )
was
waiting. Without it, it
33286 would be necessary to have the handler do another pthread_join (
) to
attempt to detach the
33287 thread, which would both delay the cancelation processing for an
unbounded period and
33288 introduce a new call to pthread_join ( ), which might itself need
a
cancelation handler. A
33289 dynamic detach is nearly essential in this case.
33290 2. In order to detach the ''initial thread'' (as may be desirable
in
processes that set up server
33291 threads).
33626 The pthread_join ( ) function shall suspend execution of the
calling
thread until the target thread
33627 terminates, unless the target thread has already terminated. On
return from a successful
33628 pthread_join ( ) call with a non-NULL value_ptr argument, the
value
passed to pthread_exit() by
33629 the terminating thread shall be made available in the location
referenced by value_ptr. When a
33630 pthread_join ( ) returns successfully, the target thread has been
terminated. The results of multiple
33631 simultaneous calls to pthread_join ( ) specifying the same target
thread are undefined. If the
33632 thread calling pthread_join ( ) is canceled, then the target
thread
shall not be detached.
33678 The pthread_join ( ) function is a convenience that has proven
useful
in multi-threaded
33679 applications. It is true that a programmer could simulate this
function if it were not provided by
33680 passing extra state as part of the argument to the start_routine(
).
The terminating thread would
33681 set a flag to indicate termination and broadcast a condition that
is
part of that state; a joining
33682 thread would wait on that condition variable. While such a
technique
would allow a thread to
33683 wait on more complex conditions (for example, waiting for
multiple
threads to terminate),
33684 waiting on individual thread termination is considered widely
useful.
Also, including the
33685 pthread_join ( ) function in no way precludes a programmer from
coding such complex waits.
33686 Thus, while not a primitive, including pthread_join ( ) in this
volume of IEEE Std 1003.1-200x was
33687 considered valuable.
33688 The pthread_join ( ) function provides a simple mechanism
allowing an
application to wait for a
33689 thread to terminate. After the thread terminates, the application
may
then choose to clean up
33690 resources that were used by the thread. For instance, after
pthread_join ( ) returns, any
33691 application-provided stack storage could be reclaimed.
33692 The pthread_join ()orpthread_detach( ) function should eventually
be
called for every thread that
33693 is created with the detachstate attribute set to
PTHREAD_CREATE_JOINABLE so that storage
33694 associated with the thread may be reclaimed.
33695 The interaction between pthread_join ( ) and cancelation is
well-defined for the following reasons:
33696 · The pthread_join ( ) function, like all other
non-async-cancel-safe
functions, can only be called
33697 with deferred cancelability type.
33698 · Cancelation cannot occur in the disabled cancelability state.
33699 Thus, only the default cancelability state need be considered. As
specified, either the
33700 pthread_join ( ) call is canceled, or it succeeds, but not both.
The
difference is obvious to the
33701 application, since either a cancelation handler is run or
pthread_join ( ) returns. There are no race
33702 conditions since pthread_join ( ) was called in the deferred
cancelability state.
===== 4/7 =====
> > I don't intend thread to be inherited from.  I personally find such
>designs
> > to be unwieldy, and rarely provide any benefit.
>
>well, sure it is a matter of taste but IMHO w/o inheritance there
>is not much benefit of providing OO interface on top of plain C
>interface.. examples of "reasonable" usage are the following:
There's much more to OO than polymorphism.  That said...
>- extend base threads with e.g. thread names for tracing/logging
Extension can as easily (often easier) be done through aggregation.
>- usage of thread local variables (simply as members of a thread
>subclass) saving valuable/limited TLS/TSD key space (some impl.
>have only 64 slots/keys).
A polymorphic thread type is hardly needed for this.
> > You've missed the point that started this whole thing.  These
semantics
>lead
> > to a blend between the thread concept and the thread_desc concept,
and
>the
> > two should rightfully be seperate (though there may be no need for
a
> > thread_desc and/or the thread may be an internal detail never
accessible
>by
> > the user).  Read Bemans original post on this.
>
>IMHO C++ "thread class" should just have a private C "thread
descriptor"
>and completely hide it -- replace it via C++ "thread class" instance
>ptr/ref plus methods, i do not see a need for both concepts.
The concepts exist, regardless of the public interface.  A public
interface
that blends the two concepts just leads to confusion and is a poor OO
design.
> > > >        // To allow us to not need self(), at least for
> > > >        // comparison.
> > > >        bool is_current() const;
> > > >
> > > >        bool is_alive() const;
> > >
> > >quite useless since the result is absolute.
> >
> > I don't follow you here.
>
>sorry, i meant obsolete / out-of-date. that is why it is useless --
>same story as with lock.islocked()
How is it obsolete/out-of-date?  is_alive() is questionable in usage...
generally busy polling is bad and you can assume the thread is still
alive by the time we return from is_alive() regardless of the result.
However, it is useful for some busy wait operations.  If you're
arguing for removal I may be persuaded, but you'll need convincing
arguments since some applications *DO* make effective use of a busy
polling scheme.
As for lock.is_locked(), this is simply a necessity given the
Boost.Threads design.
> > > > 1)  Will other operations that can be performed on threads
create a
> > > > very real need for a self() method?
> > >
> > >yes.
> >
> > Evidence or proof, please.  I _feel_ the same way, but with out
proof
we
> > won't win this one.
>
>well.. e.g. to trace the _current_ thread cpuclockid, scheduling
parm(s):
>
>int pthread_getcpuclockid(pthread_t thread_id, clockid_t * clock_id);
>int pthread_getschedparam(pthread_t thread, int *restrict policy,
>struct sched_param *restrict param);
And these are useful/needed why?  Why would you need to use them on
self() and not on an explicit thread object?  These are the proofs
needed.
>etc.. also, sometime it is needed to _change_ something associated
with
>_current_ thread:
>
>int pthread_setschedparam(pthread_t thread, int policy,
>const struct sched_param * param);
>
>or just remove the _current_ thread from some registry (e.g. thread
pool).
A *slightly* better example, though this can be handled with out a
self() method.  Especially given the is_current() method.
> > You've mixed things up here, and also subscribed to semantics that
>differ
> > from posix threads, which further confuses me since it's the first
time
> > you've done so.  Under posix, a call to pthread_join() detaches the
>thread.
>[...]
> > Calls to such things as is_alive() and is_current() make no sense
for a
> > detached thread.  I think you're confusing pthread concepts with
the
> > Boost.Thread concepts here.
>
>i am not so sure with respect to "pthread concepts" /
>"Boost.Thread concepts" but we certainly have a HUGELY
>different understanding of "join" and "detach" concepts.
>please read the info attached bellow and then re-read
>my comments please... detach IS a mechanism to force
>_auto_ cleanup/self-reclamation of internal thread related
>storage(ids/..) --> self-destruction which would otherwise
>(w/o detach) would need to be done via join (which in addition
>provides a mechanism to communicate thread function return
>value)
It's *NOT* self destruction.  The state continues to exist beyond the
call to detach(), provided the thread is still running.  What occurs
is that ownership is transferred from the caller to the callee, and
the thread destroys this state information when it terminates.
>2103 Although implementations may have thread IDs that are unique in a
>system, applications
>2104 should only assume that thread IDs are usable and unique within a
>single process. The effect of
>2105 calling any of the functions defined in this volume of IEEE Std
>1003.1-200x and passing as an
>2106 argument the thread ID of a thread from another process is
>unspecified. A conforming
>2107 implementation is free to reuse a thread ID after the thread
>terminates if it was created with the
>2108 detachstate attribute set to PTHREAD_CREATE_DETACHED or if
>pthread_detach() or
>2109 pthread_join ( ) has been called for that thread. If a thread is
>detached, its thread ID is invalid for
>2110 use as an argument in a call to pthread_detach() or pthread_join
().
Note that the id can be reused only after *both* a detach call and the
thread termination.
>33260 The pthread_detach( ) function shall indicate to the
implementation
>that storage for the thread
>33261 thread can be reclaimed when that thread terminates. If thread
has
>not terminated,
>33262 pthread_detach( ) shall not cause it to terminate. The effect of
>multiple pthread_detach( ) calls on
>33263 the same target thread is unspecified.
Again, note that reclamation occurs only after *both* a detach call and
the thread termination.
>33626 The pthread_join ( ) function shall suspend execution of the
calling
>thread until the target thread
>33627 terminates, unless the target thread has already terminated. On
>return from a successful
>33628 pthread_join ( ) call with a non-NULL value_ptr argument, the
value
>passed to pthread_exit() by
>33629 the terminating thread shall be made available in the location
>referenced by value_ptr. When a
>33630 pthread_join ( ) returns successfully, the target thread has
been
>terminated. The results of multiple
>33631 simultaneous calls to pthread_join ( ) specifying the same
target
>thread are undefined. If the
>33632 thread calling pthread_join ( ) is canceled, then the target
thread
>shall not be detached.
Sure reads as if a join() does a detach() to me, and this is the very
description of the semantics given in "Programming with POSIX Threads",
Butenhof.
Bill Kempf
===== 5/7 =====
> Extension can as easily (often easier) be done through aggregation.
could you please provide an example for a program which would:
  trace( ((MyThread)Thread::self())->getName(),__FUNCTION__ );
through aggregation without self()??
> >- usage of thread local variables (simply as members of a thread
> >subclass) saving valuable/limited TLS/TSD key space (some impl.
> >have only 64 slots/keys).
>
> A polymorphic thread type is hardly needed for this.
could you please provide an example for a program which would:
class MyThread1 : public Thread {
private:
  char thread_local_buffer1[ XXXX ];
};
class MyThread2 : public MyThread1 {
private:
  char thread_local_buffer2[ YYYY ];
};
w/o polymorphism (and easy to understand/read)??
> As for lock.is_locked(), this is simply a necessity given the
Boost.Threads
> design.
the problem is that right after the check for lock ownership
inside is_locked() (which would need to lock and _unlock_ some
internal mutex) the ownership might have already changed!!! --
another thread might obtain the mutex right after unlock) the
function should be actually called as "was_locked()" and is
hardly needed in _any_ program. the only situation when it
would provides reliable results is with _two_ (or more) locks:
threads A:
lock1.lock()
if ( lock2.is_locked() ) ...
lock1.unlock()
other threads:
lock1.lock()
...
lock2.lock()
...
lock2.unlock()
...
lock1.unlock()
> A *slightly* better example, though this can be handled with out a
self()
> method.  Especially given the is_current() method.
could you please provide an example for a program which would:
// need to boost priority??
if ( ... ) Thread::self()->setPrty( HIGH_PRTY ); ...
// Go back to normal prty
//if ( ... )
Thread::self()->setPrty( NORMAL_PRTY );
> It's *NOT* self destruction.  The state continues to exist beyond the
call
> to detach(), provided the thread is still running.  What occurs is
that
> ownership is transferred from the caller to the callee, and the
thread
> destroys this state information when it terminates.
well, it is an instruction for a _target_ thread to perform
a self destruction (with respect to state -- id/etc) _when_it_
_terminates_ (thread function completes).
it is NOT a transfer of ownership..  i may still have a mechanism
to _control_the_termination_ of _target_ thread (e.g. mutex +
flag + cv, target threads runs until flag becomes TRUE). in this
case all functions except join/detach could be called _safely_
_until_ i turn the termination switch on (set flag to true)!!
C++ "thread object" is a thread state (OO layer on top of non-OO
low level thread state)!! which should be deleted in a similar manner
as non-OO state -- either by a detached thread itself (TLS/TSD
destruction) or by a "delete" from another thread _after_ join (join
is a _pre_-destruction operation for non-detached still running
threads).
> Sure reads as if a join() does a detach() to me, and this is the very
> description of the semantics given in "Programming with POSIX
Threads",
> Butenhof.
i have that book.. could you please give a page number..
> >yield() is quite useless (taking into account that neither posix
> >nor win32 program really need it)
>
> Win32 programs *frequently* need it.
could you please provide an example?
> I can't believe that posix threads are
> any different.
in posix, yield() does not have to do anything.
it works only if:
a) program is running on uniprocessor
b) all threads are realtime SCHED_FIFO/RR
hence it works only in fairly non-portable programs.
well, POSIX programs for fairly restrictive environments.
> Further, yield() is going to be necessary for non-preemptive
> thread implementations.
yup. however, i do not think that you will tell to every
user of boost.threads library to design their programs such
that all threads would need to call yield() just for the case
to make them running "well" on some archaic platform(s)
with non-preemptive scheduling.
I would suggest to make it a part of some
"scheduler" class -- not thread; in posix it
is even called sched_yield() !!
regards,
alexander.
===== 6/7 =====
> > Extension can as easily (often easier) be done through aggregation.
>
>could you please provide an example for a program which would:
>
>   trace( ((MyThread)Thread::self())->getName(),__FUNCTION__ );
>
>through aggregation without self()??
The existance of self() is still in question.  In any event, the above is
not safe because the type of self() may not be "MyThread".  Further, stuff
like this is often better handled through other mechanisms any way.  TLS
and/or data passed into the thread are probably better mechanisms for
almost
anything you can dream up than derivation is.  Derivation should be chosen
only when polymorphis is needed, and the above uses no polymorphism at all
(i.e. there are no virtual methods).
> > >- usage of thread local variables (simply as members of a thread
> > >subclass) saving valuable/limited TLS/TSD key space (some impl.
> > >have only 64 slots/keys).
> >
> > A polymorphic thread type is hardly needed for this.
>
>could you please provide an example for a program which would:
>
>class MyThread1 : public Thread {
>private:
>
>   char thread_local_buffer1[ XXXX ];
>
>};
>
>class MyThread2 : public MyThread1 {
>private:
>
>   char thread_local_buffer2[ YYYY ];
>
>};
>
>w/o polymorphism (and easy to understand/read)??
Simple.  Use a single TLS slot for the buffer.  I've long considered doing
this to allow TLS to be open ended in Boost.Threads instead of
implementation defined with several unreasonable limits.  In other words,
the Boost.Threads library would use one native TLS slot to reference a
std::vector<> that would be used for all TLS slots exposed to users.
The other solution is to pass data into the thread proc for this state
information, or to encapsulate it in the function object used as the thread
proc.  Both of these are conceptually and programmatically no different
than
the use of derivation.
> > As for lock.is_locked(), this is simply a necessity given the
>Boost.Threads
> > design.
>
>the problem is that right after the check for lock ownership
>inside is_locked() (which would need to lock and _unlock_ some
>internal mutex) the ownership might have already changed!!! --
>another thread might obtain the mutex right after unlock) the
>function should be actually called as "was_locked()" and is
>hardly needed in _any_ program. the only situation when it
>would provides reliable results is with _two_ (or more) locks:
You fail to comprehend the semantics and rules for lock objects.  Lock
objects are not thread safe and should never be access across thread
boundaries.  In fact the documentation clearly states that they should only
be used within function block scope.
Regardless, if the rules weren't what they are one could still argue for an
is_locked() method (renaming was_locked() might make you feel better, but
usage would be identical, and would still be valid).  This allows for busy
waits, which can be valid concepts.  Mutexes happen to define a better
interface, namely try locks, so an is_locked on a mutex is pointless (note
that I said on a mutex, not on a lock object!), but the same can not be
said
for the current design of thread and the is_alive() method (though a
try_join() could accomplish the same thing, it's semantically identical and
therefore a poor choice in name).
>threads A:
>
>lock1.lock()
>if ( lock2.is_locked() ) ...
>lock1.unlock()
>
>other threads:
>
>lock1.lock()
>...
>lock2.lock()
>...
>lock2.unlock()
>...
>lock1.unlock()
Again, lock objects MUST NEVER BE SHARED ACROSS THREAD BOUNDARIES.  They
are
not thread safe.  They are intended to be used within block scope only, and
the documentation clearly states this.
> > A *slightly* better example, though this can be handled with out a
>self()
> > method.  Especially given the is_current() method.
>
>could you please provide an example for a program which would:
>
>// need to boost priority??
>if ( ... ) Thread::self()->setPrty( HIGH_PRTY ); ...
>// Go back to normal prty
>//if ( ... )
>Thread::self()->setPrty( NORMAL_PRTY );
I can't, and this is the argument I gave.  However, can *you* show why the
above would be necessary.  Why would a thread ever change it's own
priority?
  Couldn't it change it's own priority through a reference passed into the
threadproc?  Again, I _feel_ that this is a valid reason for self() but
can't prove that it's necessary.
> > It's *NOT* self destruction.  The state continues to exist beyond the
>call
> > to detach(), provided the thread is still running.  What occurs is that
> > ownership is transferred from the caller to the callee, and the thread
> > destroys this state information when it terminates.
>
>well, it is an instruction for a _target_ thread to perform
>a self destruction (with respect to state -- id/etc) _when_it_
>_terminates_ (thread function completes).
The words you use here just illustrate Beman's point that two concepts are
involved... a thread and a thread id/descriptor/handle/ptr/whatever.  It
also shows why we must use slightly different (but compatible) meanings for
join() and detach() in an OO design, especially if we expose the thread
concept instead of or in addition to the thread_desc concept.
>it is NOT a transfer of ownership..  i may still have a mechanism
>to _control_the_termination_ of _target_ thread (e.g. mutex +
>flag + cv, target threads runs until flag becomes TRUE). in this
>case all functions except join/detach could be called _safely_
>_until_ i turn the termination switch on (set flag to true)!!
But you can't insure that the thread terminates after setting the flag.
The
flag is nothing more than a hint, and the "ownership" has been transferred
to the thread.  Think of it this way:
desc create()
   The thread descriptor owns the thread state here.
join(desc*)
   The thread waits for the thread referenced by desc to terminate and then
deletes the thread state.
Conversely:
desc create()
   The thread descriptor owns the thread state here.
detach(desc)
   The thread referenced by desc now owns the thread state and deletes it
when the thread terminates.
This is *slightly* different from posix since the join example under posix
could be the following and also be compliant:
desc create()
   The thread owns the thread state.
join(desc)
   The thread waits for the thread referenced by desc to terminate and
destroy the thread state if it is still running.
Conceptually, though, both are identical and for our purposes the first is
a
better view of the model.
>C++ "thread object" is a thread state (OO layer on top of non-OO
>low level thread state)!! which should be deleted in a similar manner
>as non-OO state -- either by a detached thread itself (TLS/TSD
>destruction) or by a "delete" from another thread _after_ join (join
>is a _pre_-destruction operation for non-detached still running threads).
You assume the objects are created on the free store, which is *only* going
to work with the ref-counted variant.  In that case, what the user actually
uses is a thread_desc, not a thread object.  The thread object should
likely
be a hidden implementation detail.
I don't think you're grasping the design decisions and questions being
asked
in this thread.
> > Sure reads as if a join() does a detach() to me, and this is the very
> > description of the semantics given in "Programming with POSIX Threads",
> > Butenhof.
>
>i have that book.. could you please give a page number..
Pg. 315.
> > >yield() is quite useless (taking into account that neither posix
> > >nor win32 program really need it)
> >
> > Win32 programs *frequently* need it.
>
>could you please provide an example?
A simple spin lock:
while (InterlockedCompareExchange(&lock, 1, 0) != 0)
   yield(); // Relinquish CPU time slice
Granted, this is an optimization and not a theoretical requirement, but
these optimizations are essential to properly running applications.
> > I can't believe that posix threads are
> > any different.
>
>in posix, yield() does not have to do anything.
>it works only if:
>
>a) program is running on uniprocessor
>b) all threads are realtime SCHED_FIFO/RR
>
>hence it works only in fairly non-portable programs.
>well, POSIX programs for fairly restrictive environments.
It works no matter what.  It does something *useful* only under the
conditions you imply.  You can *not* do with out this functionality in
portable code, however, since it may very well be true that the conditions
apply.  BTW, I don't understand why the need for condition (a).  Unless you
have the same number of threads running as the number of processors, the
need to relinquish your timeslice remains.  As for (b) I don't know enough
about thread scheduling to comment on that either.  Our Win32 threads have
only one scheduling policy.
> > Further, yield() is going to be necessary for non-preemptive
> > thread implementations.
>
>yup. however, i do not think that you will tell to every
>user of boost.threads library to design their programs such
>that all threads would need to call yield() just for the case
>to make them running "well" on some archaic platform(s)
>with non-preemptive scheduling.
No, but the interface *HAS* to exist to allow programmers on these systems
to use Boost.Threads.
Bill Kempf
===== 7/7 =====
> > > Extension can as easily (often easier) be done through aggregation.
> >
> >could you please provide an example for a program which would:
> >
> >   trace( ((MyThread)Thread::self())->getName(),__FUNCTION__ );
> >
> >through aggregation without self()??
>
> The existance of self() is still in question.  In any event, the above is
> not safe because the type of self() may not be "MyThread".
well, you could make it safe if you really want.. however,
i am taking about _application_ threads and _application_
functions -- not about "general" libraries. in my application
i am able to insure that my function X is called on my thread
A,B,.. only -- "MyThread" threads only --> no extra checking
needed.
> Further, stuff
> like this is often better handled through other mechanisms any way.  TLS
> and/or data passed into the thread are probably better mechanisms for
almost
> anything you can dream up than derivation is.
self() should be implemented via TLS. no extra key or sub-key
is needed with inheritance (thread fields) for extra thread
locals. and it is more readable and _efficient_ -- it saves an
extra lookup (cast is MUCH faster).
> Derivation should be chosen
> only when polymorphis is needed, and the above uses no polymorphism at
all
> (i.e. there are no virtual methods).
please show me some code with ThreadLocal class/subclasses for
things such as thread names... i do not think that it will look
better than "MyThread" with thread name as thread field.
> Simple.  Use a single TLS slot for the buffer.  I've long considered
doing
> this to allow TLS to be open ended in Boost.Threads instead of
> implementation defined with several unreasonable limits.  In other words,
> the Boost.Threads library would use one native TLS slot to reference a
> std::vector<> that would be used for all TLS slots exposed to users.
native TLS slot should be used for Thread object which
should have a container for second level thread locals.
however, second level thread locals are still less
efficient than thread fields!
> The other solution is to pass data into the thread proc for this state
> information, or to encapsulate it in the function object used as the
thread
> proc.  Both of these are conceptually and programmatically no different
than
> the use of derivation.
it is different. you would have to pass the state information on
nearly every function call (to make it available). Thread field
solution does not require that.
> Again, lock objects MUST NEVER BE SHARED ACROSS THREAD BOUNDARIES.  They
are
> not thread safe.
not thread safe lock objects ???!!!
well, that is really something new.
> They are intended to be used within block scope only, and
> the documentation clearly states this.
scoped locking is ok. as for locks.. sorry, the only
thing they protect is themselves -- they SHOULD BE
thread safe.
> I can't, and this is the argument I gave.  However, can *you* show why
the
> above would be necessary.  Why would a thread ever change it's own
priority?
>   Couldn't it change it's own priority through a reference passed into
the
> threadproc?  Again, I _feel_ that this is a valid reason for self() but
> can't prove that it's necessary.
well, how would you e.g. detach (or do something else)
the _initial_ thread w/o self()??
> You assume the objects are created on the free store,
not every object -- objects for detached threads
should be created on the free store. however,
objects for joined threads could be created on auto
(and even static) store.
> which is *only* going to work with the ref-counted variant.
i see no need for ref-counting with respect to thread
objects.
> In that case, what the user actually
> uses is a thread_desc, not a thread object.
> The thread object should likely
> be a hidden implementation detail.
i think that a thread identifier should be
a hidden implementation detail of the thread
object.
> > > Sure reads as if a join() does a detach() to me, and this is the very
> > > description of the semantics given in "Programming with POSIX
Threads",
> > > Butenhof.
> >
> >i have that book.. could you please give a page number..
>
> Pg. 315.
umm.. see Pg.37 "Detaching a thread tells the system..."
and "Calling pthread_join..." -- he is talking about
"reclaim the resources", not about pthread_detach
which simply tells the system to _automatically_ free
the storage ("reclaim the resources") on _thread_
_termination_ (Pg. 315 "pthread_detach Hint:...")
> > > >yield() is quite useless (taking into account that neither posix
> > > >nor win32 program really need it)
> > >
> > > Win32 programs *frequently* need it.
> >
> >could you please provide an example?
>
> A simple spin lock:
>
> while (InterlockedCompareExchange(&lock, 1, 0) != 0)
>    yield(); // Relinquish CPU time slice
IMHO, very bad example. the spinlock above is
generally unsuitable for any appl using priority
scheduling (FIFO/RR) -- lock could be held by some
thread of lower priority, and since the waiting
thread never _blocks_, only threads of equal or
higher priority will ever run --> the lock can
not be unlocked. _deadlock_!
regards,
alexander.

Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk