Boost logo

Boost :

From: Jeremy Siek (jsiek_at_[hidden])
Date: 2001-06-28 13:50:45


Interesting reading...

Alexander, would you mind outlining what you think is a good design?

Cheers,
Jeremy

On Thu, 28 Jun 2001, Alexander Terekhov wrote:
>
> ===== 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.
>
>
>
>
> Info: http://www.boost.org Unsubscribe: <mailto:boost-unsubscribe_at_[hidden]>
>
> Your use of Yahoo! Groups is subject to http://docs.yahoo.com/info/terms/
>
>
>

----------------------------------------------------------------------
 Jeremy Siek www: http://www.lsc.nd.edu/~jsiek/
 Ph.D. Candidate, IU B'ton email: jsiek_at_[hidden]
 Summer Manager, AT&T Research phone: (973) 360-8185
----------------------------------------------------------------------


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