|
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