[cpp-threads] N2800's C/C++MM and read-write locking

Paul E. McKenney paulmck at linux.vnet.ibm.com
Thu Jan 8 16:50:35 GMT 2009


On Thu, Jan 08, 2009 at 04:01:28PM +0200, Peter Dimov wrote:
> >> I also often tend to think of "rwlocks" as shared/exclusive locks.  If 
> >> you really
> >> want to use weaker fences for readers, I think you would need to split 
> >> them
> >> into two variantes: shared/exclusive and read/write.
> >
> > Care to elaborate?
> 
> Something like:
> 
> take shared lock
> try STM transaction in a lock-free manner
> unlock
> 
> if failed
>     take write lock
>     use ordinary ops
>     unlock
> 
> Here the lock is not used as a pure read lock since the transaction contains 
> writes. 

I agree with Peter.  In fact, I have come across situations where one
read-acquires the lock to protect an update and write-acquires the lock
to protect a read.  What, you don't believe me?  ;-)  Read on, then...

Here is such an example, which assumes per-thread variables that I
represent as arrays, as I cannot remember what if anything C++0x does
with thread-local storage.  I also invent a me() function that returns
a small integer uniquely identifying the current thread, and I steal
the Linux-kernel syntax for reader-writer locks.

Yes, this sort of thing really has been used in production in real life.
Explanation after example.

	long count[N_THREADS] = 0;
	DEFINE_RWLOCK(rwl);

	void get_reference(void)
	{
		read_lock(&rwl);
		count[me()]++;
		read_unlock(&rwl);
	}

	void put_reference(void)
	{
		read_lock(&rwl);
		count[me()]--;
		read_unlock(&rwl);
	}

	/*
	 * Caller must write-hold rwl.
	 */
	int no_references(void)
	{
		int i;

		for (i = 0; i < N_THREADS; i++)
			if (count[i] != 0)
				return 0;
		return 1;
	}

	/* Sample use. */

	write_lock(&rwl);
	if (no_references())
		perform_action_requiring_there_be_no_references();
	write_unlock(&rwl);

One place I have seen this used is for hot-plugging disk drives.
When starting a disk read or write, one invokes get_reference().
When that read/write completes, one invokes put_reference().  When one
wants to remove a disk from service, one uses no_references() to test
whether there is an outstanding read or write to a disk.  One normally
has some mechanism to prevent new reads or writes from starting on a
given volume, and one normally also has a separate instance of count[]
for each disk drive.

In this case, the read-side primatives really do need heavy memory
fences, as the "readers" are updating the data structure.

						Thanx, Paul



More information about the cpp-threads mailing list