[cpp-threads] Comments on N2324

Paul E. McKenney paulmck at linux.vnet.ibm.com
Mon Jul 16 22:23:26 BST 2007


Hello!

A few comments on N2324.

							Thanx, Paul

o	"Memory Model Interactions" -- need to call out support of
	existing practice as a goal that guides the standard.

o	"Memory Model Interactions" -- on data dependencies, specific
	comments on or objections to the proposals in N2260?

o	"Remaining Issues" -- "This issue as that any function will
	often be inappropriate": "as" should be "is".

o	"Atomic Operations" -- The swap, compare-and-swap, and fetch-and-*
	operations are defined to be "in the sense of synchronizes with".
	This should be the case only when they are non-relaxed.

o	"Atomic Operations" -- s/stive/strive/ or define the term
	"stive".  Ditto s/operatoins/operations/.

o	"Atomic Operations" -- "volatility is preserved when applying
	these operations to volatile objects. It does not mean that
	operatoins on non-volatile objects become volatile."  Does
	this mean that the compiler is permitted to merge successive
	atomic operations on a given atomic variable?  Or are atomics
	implicitly volatile?

o	"Atomic Operations"/"atomic_address" -- why not fetch-and-and,
	fetch-and-or, and fetch-and-xor?  I can see the point of avoiding
	these operation on the partial pointer specializations, but
	if you are working with raw addresses anyway...

o	"Atomic Types" -- the fence operation is just using the underlying
	object as an ID, it seems.  But...

	o	Does a fence operation on a given variable synchronize
		with a load_acquire or store_release operation on
		that same variable?  If so, what does this mean?

	o	x.fence(memory_order_relaxed) is a no-op, correct?

o	"Lock-Free" -- "The clear and test-and-set operations must be
	lock-free, and hence address-free."  What does it mean for
	a test-and-set operation to be address-free?  It has to do
	the test-and-set on -some- address or another, right?

	Also the "clear" in this sentence is shorthand for
	"test-and-clear"?  Or "atomic_flag_clear"?

o	"Synopsis" -- I confess, I have no idea what the "delete"
	means in the following syntax:

		atomic_flag& operator =( const atomic_flag& ) = delete;

	Force subtypes to define this member function?  Make it be
	the "delete" operator?  Prohibit subtypes from defining
	this operator?  Something else entirely?

o	"Flag" -- This isn't supposed to be a functional example that
	accomplishes some specific task, correct?

o	"Lazy Initialization" -- The "lazy_example_fence_cpp()" function
	seems to leave out a fence.  I believe that the code should
	instead be as follows:

	int lazy_example_fence_cpp( void )
	{
	    if ( lazy_ready.load( memory_order_relaxed ) )
		lazy_ready.fence( memory_order_acquire );
	    else if ( lazy_assigned.swap( true, memory_order_relaxed ) ) {
		while ( ! lazy_ready.load( memory_order_relaxed ) );
		lazy_ready.fence( memory_order_acquire );
	    } else {
		lazy_value = 42;
		lazy_ready.fence( memory_order_release );
		lazy_ready.store( true, memory_order_relaxed );
	    }
	    return lazy_value;
	}

	That said, the following from N2262 seems more straightforward
	to me:

	int lazy_example_fence_cpp( void )
	{
	    if ( lazy_ready.load( memory_order_relaxed ) )
		acquire_memory_fence();
	    else if ( lazy_assigned.swap( true, memory_order_relaxed ) ) {
		while ( ! lazy_ready.load( memory_order_relaxed ) );
		acquire_memory_fence();
	    } else {
		lazy_value = 42;
		release_memory_fence();
		lazy_ready.store( true, memory_order_relaxed );
	    }
	    return lazy_value;
	}

	Of course, in this case, the variable-based non-fence approach
	is simpler -- but converting existing code would benefit from
	variable-free fences.

o	"Integer" -- given that "taul" is non-volatile, is the compiler
	permitted to merge the last six statements of this function?

		taul = x;
		taul += 1;
		taul ^= 4;
		taul -= taul++;
		taul |= --taul;
		taul &= 7;

o	"List Insert" -- these functions fail when inserting concurrently
	because the value of the head pointer is never re-fetched.
	Here is a suggested modification:

	void list_example_strong( data* item )
	{
	    node* candidate = new node;

	    candidate->value = item;
	    do {
	        candidate->next = head;
	    } while ( ! head.compare_swap( candidate->next, candidate ) );
	}

	void list_example_weak( struct data* item )
	{
	    node* candidate = new node;

	    candidate->value = item;
	    do {
		    candidate->next = head.load( memory_order_relaxed );
	    } while ( ! head.compare_swap( candidate->next, candidate,
					   memory_order_release ) );
	}



More information about the cpp-threads mailing list