[cpp-threads] Alternatives to SC

Paul E. McKenney paulmck at linux.vnet.ibm.com
Mon Jan 15 06:16:56 GMT 2007


On Sun, Jan 14, 2007 at 11:00:12AM -0500, Doug Lea wrote:
> Peter Dimov wrote:
> >Doug Lea wrote:
> >Speaking of hazard pointers... as an aside, the two main problems with
> >turning an academic paper into a real C++ implementations are that (1)
> >most papers assume SC when giving the pseudocode, (2) most papers either
> >assume garbage collection, or don't mention the issue of memory
> >management at all. Even given SC atomics, the second problem remains.
> >Yet the discussion doesn't mention it at all. Do the proponents of "SC
> >atomics for the masses" also assume that they will only be usable in a
> >GC environment? :-) I simply don't see how a non-expert can reasonably
> >handle the memory management of a typical lock-free container, no matter
> >how sequentially consistent the atomics are.
> 
> Continuing your aside-mode: I agree that for C++, it would be great
> to have a pre-supplied library component or two to handle hazard
> pointers, because they are very hard to program. And secondarily
> because vendors might be able to supply special versions that exploit
> platform-level tricks (like cheats relying on pointer representations).
> Maybe you could propose a little API for these?

When I was beating my head against the wall attempting to construct an
RCU implementation suitable for Ingo Molnar's realtime Linux patchset,
one of the things I tried was hazard pointers.  This came pretty close
to working, and might actually work in other environments.  Here was the
mapping:

	rcu_read_lock() -- create a new context for hazard-pointer lifetime.

	rcu_dereference() -- allocate and point a hazard pointer at the
		referenced object.

	rcu_read_unlock() -- free up all hazard pointers created since
		the matching rcu_read_lock().

	call_rcu() -- queue the object for destruction once no hazard
		pointers reference it (perhaps destroy immediately).

	synchronize_rcu() -- wait for all current hazard pointers to
		be freed up.  This was needed in the Linux kernel, but
		might be possible to just leave out of a new API.

But this ran aground, and I eventually came up with the current per-CPU
counters approach.  Why did it run aground?

1.	Hazard pointers would have artificially long lifetimes, since
	they cannot be freed until the enclosing rcu_read_unlock() is
	reached.  This was a showstopper for small-memory embedded
	uses of Linux.

	In application space, one might be able to provide an
	rcu_donereference() or some such that frees individual hazard
	pointers, doing so would have posed ugly software-engineering
	problems in the kernel.  (I just removed this node from a
	global list and, in common code, am putting it onto another
	list.  Do I keep the hazard pointer?  Well, that depends on
	whether the destination list is public or private -- and that
	means that the public/private nature of the list must be wound
	down through multiple layers of code.  It just got too ugly.

2.	What if the hazard-pointer allocation in rcu_dereference() fails?
	Having rcu_dereference() be able to return an error instead
	of the pointer would greatly complicate code using RCU, so I
	rejected this approach.  One might instead require the caller
	to pass in the memory be be used to record the hazard pointer,
	but there are places where dynamic allocation is absolutely
	necessary -- and then the caller is once again stuck with handling
	memory-allocation failure.

3.	The hazard pointers themselves require memory, hence would not
	bring joy to the hearts of those running Linux on
	memory-constrained systems.

It is possible that some application environments may permit different
choices.

For whatever it is worth!!!

						Thanx, Paul



More information about the cpp-threads mailing list