[cpp-threads] Re: Thread API interface tweaks

Boehm, Hans hans.boehm at hp.com
Thu Aug 31 00:00:10 BST 2006


I agree with Peter.  Reading _id while another thread is changing it,
even if it is between irrelevant values, is a data race, and hence
invokes undefined behavior.  And that makes sense: consider the case in
which a thread_id is bigger than something I can read and write
atomically, or might not be sufficiently aligned for atomicity.  I might
read two inconsistent half thread_ids, the concatenation of which might
be my id, causing very bad things to happen.

The code here really is broken under some slightly uncommon conditions;
we're not just dealing in technicalities.   You do need atomics.

I think this is actually one of the relatively rare cases in which no
ordering is required, and a <raw> atomic access to id_ makes sense.
Either the next operation acquires a lock, or it accesses data that's
temporarily private to me.

I don't see why count has to be atomic or needs release semantics.  I
think we can arrange to only access it by the lock holder.

Hans

> -----Original Message-----
> From: cpp-threads-bounces at decadentplace.org.uk 
> [mailto:cpp-threads-bounces at decadentplace.org.uk] On Behalf 
> Of Peter Dimov
> Sent: Wednesday, August 30, 2006 3:43 PM
> To: Howard Hinnant
> Cc: C++ threads standardisation; David Miller; Alisdair 
> Meredith; Terrence.Miller at Sun.COM; David Miller; Bill 
> Seymour; P.J. Plauger (Dinkumware Ltd); Bjarne Stroustrup; 
> Bronis R. de Supinski; Eric Niebler; Pete Becker
> Subject: Re: [cpp-threads] Re: Thread API interface tweaks
> 
> Howard Hinnant wrote:
> > I appreciate the free analysis. :-)  Here's my understanding:
> >
> > On Aug 30, 2006, at 4:20 PM, Peter Dimov wrote:
> >
> >>> template <class Mutex>
> >>> class recursive_mutex
> >>> {
> >>> private:
> >>>     Mutex& mut_;
> >>>     unsigned count_;
> >>>     thread_id id_;
> >>> public:
> >>>     explicit recursive_mutex(Mutex& mut)
> >>>         : mut_(mut), count_(0) {}  // id_ has "not a thread" value
> >>>
> >>>     void lock()
> >>>     {
> >>>         thread_id self = thread_id::current();
> >>>         if (id_ == self)
> >>
> >> Here...
> >
> > If id_ != self, then obviously this branch isn't taken.  
> Also no other 
> > thread is capable of setting id_ to self.  Some other thread might 
> > change id_, but only from one value that is not self to 
> another value 
> > that is not self.  If id_ == self, then no other thread is 
> capable of 
> > changing the value of id_ at all.  And in this case, no 
> other thread 
> > has the ability to change count_ at all.
> 
> I'm not saying that there are correctness problems with the 
> test. My point is that
> 
>     if( id_ == self )
> 
> reads id_, and ...
> 
> >>>             ++count_;
> >>>         else
> >>>         {
> >>>             mut_.lock();
> >>>             id_ = self;
> 
> ... these ...
> 
> >>>             store_release(&count_, 1);
> >>>         }
> >>>     }
> >>>
> >>>     void unlock()
> >>>     {
> >>>         if (--count_ == 0)
> >>>         {
> >>>             id_ = thread_id();  // id_ = "not a thread"
> 
> ... two statements write to id_. It seems to me that there is 
> a legitimate possibility for the read to overlap one of the 
> writes. Therefore, the type of id_ must be atomic. I think. 
> 
> 
> --
> cpp-threads mailing list
> cpp-threads at decadentplace.org.uk
> http://www.decadentplace.org.uk/cgi-bin/mailman/listinfo/cpp-threads
> 



More information about the cpp-threads mailing list