Yahoo Groups archive

AVR-Chat

Index last updated: 2026-04-28 22:41 UTC

Message

Re: [AVR-Chat] Volatile modifier (was: gcc compiler bad behaviour)

2012-04-15 by Bob Paddock

> Bob Paddock is incorrect to assert that "Volatile is never the solution to a problem with threads", whether by "problem" we are talking about a normal design challenge or something that has gone wrong. The use of volatile variables to communicate between threads is a perfectly valid design technique and, if your threads are misbehaving, then the lack of a volatile qualifier somewhere should definitely be checked.

I stand by what I said.  Volatile always needs to be used
appropriately, threads or no threads.

> [Bob] would be correct to argue that the use of the volatile qualifier is not always a sufficient technique to ensure the orderly passage of data between threads.

Exactly.

> You also need to ensure atomicity and to worry about thread synchronization. But Don has already mentioned this. Possibly, Bob is trying to say that the simple use of the volatile qualifier, without explicit execution barriers >and handling of atomicity, is *never* a workable solution (this appears to be John Regehr's conjecture). In this he would also be incorrect.

Obviously I fail to see how John and company are wrong?  We may be
having problems in our terminology.
A ISR()  changing a value in the background of a foreground task, to
me is not a 'thread'.

> The definition of the volatile qualifier in the C language specification is in terms of the "virtual machine" and "sequence points" and how the implementation of a compiler may, for reasons of efficiency, deviate from the virtual machine as long as the result is "the same". This is all incomprehensible to most of us,

ISO/IEC 9899:201x
http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf

6.7.3 Type qualifiers: 134) A volatile declaration may be used to
describe an object corresponding to a memory-mapped
input/output port or an object accessed by an asynchronously
interrupting function. Actions on
objects so declared shall not be ''optimized out'' by an
implementation or reordered except as
permitted by the rules for evaluating expressions.

Search that document for 'volatile'.  There are places where applying
'volatile' blindly will produce code that is broken.

There is more than just adding 'volatile' on a variable or register to
make bug free code.
Issues such as:

* Locking / or Lock Free Atomic Access.
[Pure message passing like in Erlang is one way around locks/atomic
access, in that *nothing* is shared.]

* Code Motion

> It may also be essential when dealing with a hardware register that is locally defined. If the compiler can determine that the definition of the variable is invisible outside of the translation unit and it cannot see any reads of the >variable, it may remove writes, thus eliminating the hardware side-effect you are trying to achieve. So, Don's rule is a good one. Use volatile with hardware registers and variables modified and inspected in different threads, with >a suitably broad definition of threads.

So the argument is to blindly apply volatile?  Which leads to slow and
bloated code size.
I do not think any of us buy into that.

6.5.16 Assignment operators 3 (111): The *mplementation* is permitted
to read the object to determine the value but is not required to, even
when the object has volatile-qualified type.

Lets hope our AVR *implementation* is correct for Embedded use...

The standard points out several places that where volatile is
redundant or does not do what one expects from its usage.

> I will now give an example of passing data between threads of execution using *only* the volatile qualifier, without synchronization primitives.

Herb Sutter covers exactly this example in a multi-part series
starting in the September issue of Dr Dobb's journal:

Lock-Free Code: A False Sense of Security
http://www.drdobbs.com/cpp/210600279

> Data is queued in a buffer supported by read and write access methods and also by unsigned read and write counters. The counters must be at least as large as the number of entries in the buffer and must be of a type such >that access by the source and sink of data is inherently atomic (for example, they are one memory word).

No, it must be *explicitly* atomic, unless the counters are uint8_t on
our 8-Bit AVR's.

As we are discussing the AVR here, which is an 8-bit part, where the C
standard requires Int to be at lease 16 bits (a word), simply
declaring a 16 bit counter variable 'volatile', that is written inside
an ISR, and read outside of a ISR is now a bug,  if 'volatile' is the
sole thing being used.  My example shows exactly this problem, and its
solutions.  The problem shows up when the lower 8-bit value causes a
change to the upper 8-bit value (carry or borrow depending on the
implementation), when the ISR interrupts the read, and the ISR changes
the value that is being read.  There are two possible outcomes, one
that *may* be of no consequence (depends on the implementation), and
one that has the counter value off by a factor of 256.

> The integrity of the data passing is ensured by good algorithm design, not by indifferent algorithm design and fancy thread synchronization.

The implementation of the algorithm is of paramount importance.  A
good algorithm implemented badly, as Herb shows, produces buggy
products.

> The buffers are circular buffers and the access methods are read and write indexes into the buffer

A circular buffer that is limited to the size of the natural width of
the processor (8-bit for the AVR)  in question, that does not use
counters is the classic Head/Tail, may be found here:

http://websvn.hylands.org/filedetails.php?repname=Projects&path=%2Fcommon%2FCBUF.h&sc=1

On an 8-bit AVR trying to use a buffer larger than 256 once again
brings back the race condition that using volatile does not solve.


--
http://blog.softwaresafety.net/
http://www.designer-iii.com/
http://www.wearablesmartsensors.com/

Attachments

Move to quarantaine

This moves the raw source file on disk only. The archive index is not changed automatically, so you still need to run a manual refresh afterward.