#include <stdio.h>
struct InterruptData
{
int numberOfInterrupts;
volatile int timer;
int numberOfTimesInterruptsMasked;
} InterruptMonitor;
/*
* Interrupt handler code.
*/
int main(void)
{
InterruptMonitor = InterruptMonitor;
printf("\n LOG - InterruptMonitor.numberOfInterrupts : %d \n",
InterruptMonitor.numberOfInterrupts);
printf("\n LOG - InterruptMonitor.timer : %d \n",
InterruptMonitor.timer);
printf("\n LOG - InterruptMonitor.numberOfTimesInterruptsMasked : %d \n",
InterruptMonitor.numberOfTimesInterruptsMasked);
return 0;
}
-------------
InterruptMonitor = InterruptMonitor; - Should this be a candidate for compile-time optimization in spite of having a field qualified as volatile?
Cheers,
Raghavan Santhanam
|
|
0
|
|
|
|
Reply
|
raghavanil4m (37)
|
7/5/2012 5:44:46 PM |
|
Myth__Buster wrote:
>
> #include <stdio.h>
>
> struct InterruptData
> {
> int numberOfInterrupts;
> volatile int timer;
> int numberOfTimesInterruptsMasked;
> } InterruptMonitor;
>
> /*
> * Interrupt handler code.
> */
>
> int main(void)
> {
> InterruptMonitor = InterruptMonitor;
> InterruptMonitor = InterruptMonitor;
> - Should this be a candidate for compile-time optimization
> in spite of having a field qualified as volatile?
I don't think so.
Merely accessing a volatile object,
is considered to be a side effect.
--
pete
|
|
0
|
|
|
|
Reply
|
pfiland (6613)
|
7/5/2012 6:59:49 PM
|
|
Myth__Buster <raghavanil4m@gmail.com> writes:
> #include <stdio.h>
>
> struct InterruptData
> {
> int numberOfInterrupts;
> volatile int timer;
> int numberOfTimesInterruptsMasked;
> } InterruptMonitor;
>
>
> /*
> * Interrupt handler code.
> */
>
> int main(void)
> {
> InterruptMonitor = InterruptMonitor;
<snip>
> return 0;
> }
>
> -------------
>
> InterruptMonitor = InterruptMonitor; - Should this be a candidate for
> compile-time optimization in spite of having a field qualified as
> volatile?
I fear that your example may be one "a bit like" the code you really
care about, but I have to talk about what you post...
To a very large extent, the C standard washes it hands of volatile
objects. Sure, it says a fair bit about what *may* happen but it also
says "[w]hat constitutes an access to an object that has volatile-
qualified type is implementation-defined" (6.7.3 p7) and "[a]n actual
implementation need not evaluate part of an expression if it can deduce
that [...] no needed side effects are produced (including any caused by
[...] accessing a volatile object)" (5.1.2.3 p4).
Together, these give an implementation so much leeway that you often
need to refer to the implementation, not the standard. For example, an
implementation may well be able to deduce that the assignment is not a
"needed side effect", coming as it does at the start of main. Equally,
an implementation may treat all accesses to volatile-qualified objects
with kid gloves.
--
Ben.
|
|
0
|
|
|
|
Reply
|
ben.usenet (6515)
|
7/5/2012 10:22:49 PM
|
|
On Thu, Jul 05, 2012 at 11:22:49PM +0100, Ben Bacarisse wrote:
> Myth__Buster <raghavanil4m@gmail.com> writes:
>
> > #include <stdio.h>
> >
> > struct InterruptData
> > {
> > int numberOfInterrupts;
> > volatile int timer;
> > int numberOfTimesInterruptsMasked;
> > } InterruptMonitor;
> >
> >
> > /*
> > * Interrupt handler code.
> > */
> >
> > int main(void)
> > {
> > InterruptMonitor = InterruptMonitor;
> <snip>
> > return 0;
> > }
> >
> > -------------
> >
> > InterruptMonitor = InterruptMonitor; - Should this be a candidate for
> > compile-time optimization in spite of having a field qualified as
> > volatile?
>
> I fear that your example may be one "a bit like" the code you really
> care about, but I have to talk about what you post...
>
> To a very large extent, the C standard washes it hands of volatile
> objects. Sure, it says a fair bit about what *may* happen but it also
> says "[w]hat constitutes an access to an object that has volatile-
> qualified type is implementation-defined" (6.7.3 p7) and "[a]n actual
> implementation need not evaluate part of an expression if it can deduce
> that [...] no needed side effects are produced (including any caused by
> [...] accessing a volatile object)" (5.1.2.3 p4).
>
> Together, these give an implementation so much leeway that you often
> need to refer to the implementation, not the standard. For example, an
> implementation may well be able to deduce that the assignment is not a
> "needed side effect", coming as it does at the start of main. Equally,
> an implementation may treat all accesses to volatile-qualified objects
> with kid gloves.
This is a concern in my mind right now as I'm writing some posix
threads code, and so obviously there are a large number of variables
which ought to be declared volatile. I don't have a copy of the
standard, so I was wondering whether the volatile attribute is
inherited by structures contained within a structure declared
volatile. Example:
struct substruct {
int a, b;
};
struct container {
int n_coord;
struct substruct coord[];
};
volatile struct container c;
The question is, does c.coord[i].a have the volatile attribute?
Regards,
Uncle Steve
--
The moon has never been closer.
|
|
0
|
|
|
|
Reply
|
stevet810 (127)
|
7/6/2012 5:08:08 PM
|
|
On Sat, Jul 07, 2012 at 09:20:57AM +1200, Ian Collins wrote:
> On 07/ 7/12 05:08 AM, Uncle Steve wrote:
> >On Thu, Jul 05, 2012 at 11:22:49PM +0100, Ben Bacarisse wrote:
> >>
> >>To a very large extent, the C standard washes it hands of volatile
> >>objects. Sure, it says a fair bit about what *may* happen but it also
> >>says "[w]hat constitutes an access to an object that has volatile-
> >>qualified type is implementation-defined" (6.7.3 p7) and "[a]n actual
> >>implementation need not evaluate part of an expression if it can deduce
> >>that [...] no needed side effects are produced (including any caused by
> >>[...] accessing a volatile object)" (5.1.2.3 p4).
> >>
> >>Together, these give an implementation so much leeway that you often
> >>need to refer to the implementation, not the standard. For example, an
> >>implementation may well be able to deduce that the assignment is not a
> >>"needed side effect", coming as it does at the start of main. Equally,
> >>an implementation may treat all accesses to volatile-qualified objects
> >>with kid gloves.
> >
> >This is a concern in my mind right now as I'm writing some posix
> >threads code, and so obviously there are a large number of variables
> >which ought to be declared volatile.
>
> What leads you to that conclusion? That line of though usually implies
> you are misunderstanding the relationship between volatile and threads.
>
> What are you trying to achieve with volatile?
Well, I have some data structures that will be accessed from arbitrary
threads, so I want to make sure there's no funny business happening
behind the scenes where the compiler decides that certain accesses are
optional.
Here's a little snippet of code which shows the issue, albeit without
nested structures:
#include <local_time.h>
struct sample_s {
T64 a, b;
};
typedef struct sample_s sample;
#define SAMPLES 100
#define nsclock(p) { \
volatile struct timespec T; \
clock_gettime(CLOCK_MONOTONIC, &T); \
p = T.tv_sec * 1000000000 + T.tv_nsec; \
}
void do_timing(sample * p)
{
volatile T64 s, e, v;
// volatile unsigned int i;
nsclock(s);
nsclock(s);
// for(i = 0; i < 1 ; i++);
nsclock(e);
p->a = (e - s);
nsclock(e);
p->b = (e - s);
return;
}
void emit_sample(sample *p)
{
fprintf(stdout, "%-5d %-5d %-5d\n", p->a, p->b, p->b - p->a);
return;
}
int main(char *argv, int argc)
{
sample tr[SAMPLES + 1];
int i;
tr[SAMPLES].a = tr[SAMPLES].b = 0;
for(i = 0; i < SAMPLES; i++)
do_timing(&tr[i]);
fprintf(stdout, "Timing results for %d samples:\n\n", SAMPLES);
fprintf(stdout, "%-5s %-5s %-5s\n", "A", "B", "B - A");
for(i = 0; i < SAMPLES; i++)
emit_sample(&tr[i]);
exit(0);
}
So, without including all the crap in local_time.h, I have to explain
a few definitions.
T64 is an unsigned long long int representing microseconds since
epoch, but I ended up using it as if it represented nanoseconds since
epoch since my i3 dual core laptop has a nanosecond-resolution timer.
The variable declared volatile are required otherwise some variable
accesses are optimized out, giving inaccurate results during the data
acquisition phase. The problems here are magnified when these access
patterns occur with competing threads. IIRC, mutexes are not memory
access barriers and solve different problems.
Regards,
Uncle Steve
--
The moon has never been closer.
|
|
0
|
|
|
|
Reply
|
stevet810 (127)
|
7/6/2012 6:01:01 PM
|
|
On Fri, Jul 06, 2012 at 06:11:28PM -0400, Eric Sosman wrote:
> On 7/6/2012 2:01 PM, Uncle Steve wrote:
> >On Sat, Jul 07, 2012 at 09:20:57AM +1200, Ian Collins wrote:
> >>On 07/ 7/12 05:08 AM, Uncle Steve wrote:
> >>>[...]
> >>>This is a concern in my mind right now as I'm writing some posix
> >>>threads code, and so obviously there are a large number of variables
> >>>which ought to be declared volatile.
> >>
> >>What leads you to that conclusion? That line of though usually implies
> >>you are misunderstanding the relationship between volatile and threads.
> >>
> >>What are you trying to achieve with volatile?
> >
> >Well, I have some data structures that will be accessed from arbitrary
> >threads, so I want to make sure there's no funny business happening
> >behind the scenes where the compiler decides that certain accesses are
> >optional.
>
> Although the latest "C11" Standard adds some multi-threading
> support, I'll ignore that because (1) C11 implementations are not
> exactly thick upon the ground as yet, (2) you mentioned you're
> using Pthreads anyhow, and (3) I haven't studied the C11 threading
> stuff closely enough to be a competent commentator. ;-)
>
> So: As long as you're using an established non-C-specific
> threading framework, I suggest you visit comp.programming.threads,
> a newsgroup devoted to multi-threading issues. There you will learn
> that `volatile' is neither necessary *nor* sufficient in connection
> with variables shared between threads. (You needn't take my word on
> this: Just browse the comp.programming.threads archives and you'll
> see the local experts refuting this misunderstanding every couple
> weeks. It's comp.programming.threads' equivalent of `void main'.)
>
> Or, as my first threading teacher put it, "Don't Do That."
Is there are C standard threading framework?
Threaded code is a de-facto reality in today's multi-core environment.
Save for the embedded sector, most systems will run threads in
parallel, and that is that. C has to recognize this watershed moment
in the refinement of the language.
While the specifics of an implementation within a given operating
system are not C issues, certain common optimizations can be a problem
with certain algorithms, and so there ought to be a C solution to the
problem. What is it about the volatile attribute that makes it
insufficient in your mind to the problem presented by threaded code?
Regards,
Uncle Steve
--
The moon has never been closer.
|
|
0
|
|
|
|
Reply
|
stevet810 (127)
|
7/6/2012 6:33:34 PM
|
|
On Sat, Jul 07, 2012 at 11:29:02AM +1200, Ian Collins wrote:
> On 07/ 7/12 06:33 AM, Uncle Steve wrote:
> >On Fri, Jul 06, 2012 at 06:11:28PM -0400, Eric Sosman wrote:
> >>On 7/6/2012 2:01 PM, Uncle Steve wrote:
> >>>On Sat, Jul 07, 2012 at 09:20:57AM +1200, Ian Collins wrote:
> >>>>On 07/ 7/12 05:08 AM, Uncle Steve wrote:
> >>>>>[...]
> >>>>>This is a concern in my mind right now as I'm writing some posix
> >>>>>threads code, and so obviously there are a large number of variables
> >>>>>which ought to be declared volatile.
> >>>>o
> >>>>What leads you to that conclusion? That line of though usually implies
> >>>>you are misunderstanding the relationship between volatile and threads.
> >>>>
> >>>>What are you trying to achieve with volatile?
> >>>
> >>>Well, I have some data structures that will be accessed from arbitrary
> >>>threads, so I want to make sure there's no funny business happening
> >>>behind the scenes where the compiler decides that certain accesses are
> >>>optional.
> >>
> >> Although the latest "C11" Standard adds some multi-threading
> >>support, I'll ignore that because (1) C11 implementations are not
> >>exactly thick upon the ground as yet, (2) you mentioned you're
> >>using Pthreads anyhow, and (3) I haven't studied the C11 threading
> >>stuff closely enough to be a competent commentator. ;-)
> >>
> >> So: As long as you're using an established non-C-specific
> >>threading framework, I suggest you visit comp.programming.threads,
> >>a newsgroup devoted to multi-threading issues. There you will learn
> >>that `volatile' is neither necessary *nor* sufficient in connection
> >>with variables shared between threads. (You needn't take my word on
> >>this: Just browse the comp.programming.threads archives and you'll
> >>see the local experts refuting this misunderstanding every couple
> >>weeks. It's comp.programming.threads' equivalent of `void main'.)
> >>
> >> Or, as my first threading teacher put it, "Don't Do That."
> >
> >Is there are C standard threading framework?
>
> Yes, read C11. If you are familiar with pthreads, you'll see where most
> of the new primitives originate.
Well, I don't follow gcc development, so I don't know when those
features will be available on my system.
> >Threaded code is a de-facto reality in today's multi-core environment.
> >Save for the embedded sector, most systems will run threads in
> >parallel, and that is that.
>
> Only if you incorporate threads in your code either explicitly or
> implicitly with something like OpenMP.
Well, sure, but I'd like to think that multithreaded applications will
be popular. It helps to improve the interactivity of applications
with a GUI frontend for one thing, never mind what you can do with
message-passing interfaces or asynchronus network I/O threads.
> >C has to recognize this watershed moment
> >in the refinement of the language.
>
> It has.
O.K., so do nested structure elements inherit the parent structure's
volatile attribute?
> >While the specifics of an implementation within a given operating
> >system are not C issues, certain common optimizations can be a problem
> >with certain algorithms, and so there ought to be a C solution to the
> >problem. What is it about the volatile attribute that makes it
> >insufficient in your mind to the problem presented by threaded code?
>
> Given you are the one going against conventional wisdom, what in your
> mind makes volatile a solution to the problem presented by threaded code?
>
> In addition, what is "the problem presented by threaded code"?
Well, code optimization from global CSE might eliminate memory
accesses to variable that could be modified by other threads in ways
that affect the algorithm. Depends of the code.
Regards,
Uncle Steve
--
The moon has never been closer.
|
|
0
|
|
|
|
Reply
|
stevet810 (127)
|
7/6/2012 8:27:29 PM
|
|
On Fri, Jul 06, 2012 at 04:58:09PM -0700, Joshua Maurice wrote:
> On Jul 6, 11:01�am, Uncle Steve <stevet...@gmail.com> wrote:
> > On Sat, Jul 07, 2012 at 09:20:57AM +1200, Ian Collins wrote:
> > > On 07/ 7/12 05:08 AM, Uncle Steve wrote:
> > > >This is a concern in my mind right now as I'm writing some posix
> > > >threads code, and so obviously there are a large number of variables
> > > >which ought to be declared volatile.
> >
> > > What leads you to that conclusion? �That line of though usually implies
> > > you are misunderstanding the relationship between volatile and threads.
> >
> > > What are you trying to achieve with volatile?
> >
> > Well, I have some data structures that will be accessed from arbitrary
> > threads, so I want to make sure there's no funny business happening
> > behind the scenes where the compiler decides that certain accesses are
> > optional.
> >
> > [snip]
> >
> > The variable declared volatile are required otherwise some variable
> > accesses are optimized out, giving inaccurate results during the data
> > acquisition phase. �The problems here are magnified when these access
> > patterns occur with competing threads. �IIRC, mutexes are not memory
> > access barriers and solve different problems.
>
> To be topical, here's the answer for C11: volatile is useless as a
> portable threading construct. Do not use it. It's (maybe) useful in a
> portable way only for 3 obscure situations: MMIO, setjmp and longjmp
> stuff, and signal handling. If you properly use C11 mutexes, there is
> absolutely no need for volatile. To paraphrase you else-thread, there
> will be no "funny business" if you use mutexes correctly. It tells the
> compiler, the hardware, and anything else relevant, to obey the
> semantics of the mutex. volatile is similarly useless when you
> correctly use the new "weak" C11 atomics. volatile adds nothing
> (portably).
>
> Now, to finally answer your question, C11 threading semantics,
> including the lack of useful volatile semantics, is basically copied
> wholesale from pthreads. Don't use volatile as a portable threading
> construct with pthreads either. pthread mutexes do everything you
> need. volatile adds absolutely nothing (portably).
>
> (Yes, you can accomplish useful code with volatile for specific
> platform and specific compilers with specific settings. That's an
> "advanced" topic that I won't cover now.)
>
> Now, what do you mean by "mutexes are not memory access barriers"?
> Mutexes do two things: they provide blocking semantics for mutual
> exclusion, and they guarantee visibility ala the usual "happens-
> before" semantics. Mutexes are implemented with hardware memory
> fences / barriers. This is all true for mutexes in pthreads, win32
> threads, Boost threads, ACE threads, C11 threads, C++11 threads, and
> every case I can recall seeing the word "mutex".
Well, mutexes are barriers for the memory accesses associated with the
mutex itself, but not in general. Reading the above I can tell that
you think I'm using POSIX mutexes, however that is not the case. I am
using a custom locking scheme that allows for concurrent readers, but
is exclusive for writers. As a consequence, the behavior of
variables marked volatile in my code is critical, so I don't want to
mistakenly use it in a way that results in surprises.
Now you can say that what I am doing is non-portable, but you would
be wrong there since my algorithms are in fact portable as long as I
am following the rules of the C standard. Perhaps the proper term is
limited portability as I rely on a tiny amount of platform specific
asm. (One instruction.)
Regards,
Uncle Steve
--
The moon has never been closer.
|
|
0
|
|
|
|
Reply
|
stevet810 (127)
|
7/6/2012 9:20:15 PM
|
|
On 07/ 7/12 05:08 AM, Uncle Steve wrote:
> On Thu, Jul 05, 2012 at 11:22:49PM +0100, Ben Bacarisse wrote:
>>
>> To a very large extent, the C standard washes it hands of volatile
>> objects. Sure, it says a fair bit about what *may* happen but it also
>> says "[w]hat constitutes an access to an object that has volatile-
>> qualified type is implementation-defined" (6.7.3 p7) and "[a]n actual
>> implementation need not evaluate part of an expression if it can deduce
>> that [...] no needed side effects are produced (including any caused by
>> [...] accessing a volatile object)" (5.1.2.3 p4).
>>
>> Together, these give an implementation so much leeway that you often
>> need to refer to the implementation, not the standard. For example, an
>> implementation may well be able to deduce that the assignment is not a
>> "needed side effect", coming as it does at the start of main. Equally,
>> an implementation may treat all accesses to volatile-qualified objects
>> with kid gloves.
>
> This is a concern in my mind right now as I'm writing some posix
> threads code, and so obviously there are a large number of variables
> which ought to be declared volatile.
What leads you to that conclusion? That line of though usually implies
you are misunderstanding the relationship between volatile and threads.
What are you trying to achieve with volatile?
--
Ian Collins
|
|
0
|
|
|
|
Reply
|
ian-news (9875)
|
7/6/2012 9:20:57 PM
|
|
On Sat, Jul 07, 2012 at 12:37:59PM +1200, Ian Collins wrote:
> On 07/ 7/12 08:27 AM, Uncle Steve wrote:
> >On Sat, Jul 07, 2012 at 11:29:02AM +1200, Ian Collins wrote:
> >>On 07/ 7/12 06:33 AM, Uncle Steve wrote:
> >>>
> >>>Is there are C standard threading framework?
> >>
> >>Yes, read C11. If you are familiar with pthreads, you'll see where most
> >>of the new primitives originate.
> >
> >Well, I don't follow gcc development, so I don't know when those
> >features will be available on my system.
>
> Then use your platform's native threading primitives.
Doing so would incur a performance penalty in comparison to my
synchronization primitives in some circumstances.
> >>>Threaded code is a de-facto reality in today's multi-core environment.
> >>>Save for the embedded sector, most systems will run threads in
> >>>parallelo, and that is that.
> >>
> >>Only if you incorporate threads in your code either explicitly or
> >>implicitly with something like OpenMP.
> >
> >Well, sure, but I'd like to think that multithreaded applications will
> >be popular. It helps to improve the interactivity of applications
> >with a GUI frontend for one thing, never mind what you can do with
> >message-passing interfaces or asynchronus network I/O threads.
>
> There already are.
Yes. There are many examples of such code.
> >>>C has to recognize this watershed moment
> >>>in the refinement of the language.
> >>
> >>It has.
> >
> >O.K., so do nested structure elements inherit the parent structure's
> >volatile attribute?
>
> A volatile T is a volatile T no matter what it contains.
Look. In threaded applications, the truth associated with the value
associated with certain variables can be somewhat indeterminate. If
that's true in your code, you might want to be aware of it.
> >>>While the specifics of an implementation within a given operating
> >>>system are not C issues, certain common optimizations can be a problem
> >>>with certain algorithms, and so there ought to be a C solution to the
> >>>problem. What is it about the volatile attribute that makes it
> >>>insufficient in your mind to the problem presented by threaded code?
> >>
> >>Given you are the one going against conventional wisdom, what in your
> >>mind makes volatile a solution to the problem presented by threaded code?
> >>
> >>In addition, what is "the problem presented by threaded code"?
> >
> >Well, code optimization from global CSE might eliminate memory
> >accesses to variable that could be modified by other threads in ways
> >that affect the algorithm. Depends of the code.
>
> If the code depends on volatile, it is broken. Use the atomics or
> synchronisation primitives provided by the language or platform. They
> exist for a reason.
You aren't listening to me.
Regards,
Uncle Steve
--
The moon has never been closer.
|
|
0
|
|
|
|
Reply
|
stevet810 (127)
|
7/6/2012 9:29:43 PM
|
|
On 7/6/2012 2:01 PM, Uncle Steve wrote:
> On Sat, Jul 07, 2012 at 09:20:57AM +1200, Ian Collins wrote:
>> On 07/ 7/12 05:08 AM, Uncle Steve wrote:
>>>[...]
>>> This is a concern in my mind right now as I'm writing some posix
>>> threads code, and so obviously there are a large number of variables
>>> which ought to be declared volatile.
>>
>> What leads you to that conclusion? That line of though usually implies
>> you are misunderstanding the relationship between volatile and threads.
>>
>> What are you trying to achieve with volatile?
>
> Well, I have some data structures that will be accessed from arbitrary
> threads, so I want to make sure there's no funny business happening
> behind the scenes where the compiler decides that certain accesses are
> optional.
Although the latest "C11" Standard adds some multi-threading
support, I'll ignore that because (1) C11 implementations are not
exactly thick upon the ground as yet, (2) you mentioned you're
using Pthreads anyhow, and (3) I haven't studied the C11 threading
stuff closely enough to be a competent commentator. ;-)
So: As long as you're using an established non-C-specific
threading framework, I suggest you visit comp.programming.threads,
a newsgroup devoted to multi-threading issues. There you will learn
that `volatile' is neither necessary *nor* sufficient in connection
with variables shared between threads. (You needn't take my word on
this: Just browse the comp.programming.threads archives and you'll
see the local experts refuting this misunderstanding every couple
weeks. It's comp.programming.threads' equivalent of `void main'.)
Or, as my first threading teacher put it, "Don't Do That."
--
Eric Sosman
esosman@ieee-dot-org.invalid
|
|
0
|
|
|
|
Reply
|
esosman2 (2945)
|
7/6/2012 10:11:28 PM
|
|
On Fri, Jul 06, 2012 at 07:39:56PM -0700, Joshua Maurice wrote:
> On Jul 6, 2:20�pm, Uncle Steve <stevet...@gmail.com> wrote:
> > On Fri, Jul 06, 2012 at 04:58:09PM -0700, Joshua Maurice wrote:
> > > To be topical, here's the answer for C11: volatile is useless as a
> > > portable threading construct. Do not use it. It's (maybe) useful in a
> > > portable way only for 3 obscure situations: MMIO, setjmp and longjmp
> > > stuff, and signal handling. If you properly use C11 mutexes, there is
> > > absolutely no need for volatile. To paraphrase you else-thread, there
> > > will be no "funny business" if you use mutexes correctly. It tells the
> > > compiler, the hardware, and anything else relevant, to obey the
> > > semantics of the mutex. volatile is similarly useless when you
> > > correctly use the new "weak" C11 atomics. volatile adds nothing
> > > (portably).
> >
> > > Now, to finally answer your question, C11 threading semantics,
> > > including the lack of useful volatile semantics, is basically copied
> > > wholesale from pthreads. Don't use volatile as a portable threading
> > > construct with pthreads either. pthread mutexes do everything you
> > > need. volatile adds absolutely nothing (portably).
> >
> > > (Yes, you can accomplish useful code with volatile for specific
> > > platform and specific compilers with specific settings. That's an
> > > "advanced" topic that I won't cover now.)
> >
> > > Now, what do you mean by "mutexes are not memory access barriers"?
> > > Mutexes do two things: they provide blocking semantics for mutual
> > > exclusion, and they guarantee visibility ala the usual "happens-
> > > before" semantics. Mutexes are implemented with hardware memory
> > > fences / barriers. This is all true for mutexes in pthreads, win32
> > > threads, Boost threads, ACE threads, C11 threads, C++11 threads, and
> > > every case I can recall seeing the word "mutex".
> >
> > Well, mutexes are barriers for the memory accesses associated with the
> > mutex itself, but not in general. �Reading the above I can tell that
> > you think I'm using POSIX mutexes, however that is not the case.
>
> I strongly suggest that you don't use the word "mutex" for your code
> because you will confuse most readers. Most readers will associate
> "mutex" with both mutual exclusion and "happens-before" global memory
> visibility. That's how the term has been commonly used (ex: posix
> pthreads), and how it will continue to commonly be used (ex: C11).
>
> >�I am
> > using a custom locking scheme that allows for concurrent readers, but
> > is exclusive for writers. �As a consequence, the behavior of
> > variables marked volatile in my code is critical, so I don't want to
> > mistakenly use it in a way that results in surprises.
> >
> > Now you can say that what I am doing is non-portable, but you would
> > be wrong there since my algorithms are in fact portable as long as I
> > am following the rules of the C standard. �Perhaps the proper term is
> > limited portability as I rely on a tiny amount of platform specific
> > asm. �(One instruction.)
>
> I will say it again: the C89 and C99 standards do not mean what you
> think. What the compiler writers and thread library writers understand
> volatile to mean is not what you understand volatile to mean, and at
> the end of the day what the compiler writers and thread library
> writers think is paramount. You may be able to get this to work for
> specific platforms with specific compilers. I suggest you ask in a
> location specific to such implementations for such platform specific
> hackery. However, do not look at the C standard for guidance on this.
Um, what is it that compiler writers and thread library writers
understand volatile to mean?
> In the C11 parlance, what you're trying to write requires the "weak"
> atomics, such as relaxed, acquire, release, and consume memory
> operations. Attempting to write stuff like this without C11 support
> with the C11 primitives is a wholly platform specific affair, and
> neither C89 nor C90 can help you there. volatile specifically cannot
> help you (barring platform specific documentation to the contrary).
You seem to be making too many assumptions about my code.
> From else-thread:
> On Jul 6, 2:29 pm, Uncle Steve <stevet...@gmail.com> wrote:
> > On Sat, Jul 07, 2012 at 12:37:59PM +1200, Ian Collins wrote:
> > > Then use your platform's native threading primitives.
> >
> > Doing so would incur a performance penalty in comparison to my
> > synchronization primitives in some circumstances.
>
> That's your problem. You have broken code. Your options are:
> 1- broken code (e.g. using volatile and asm memory barriers absent
> platform specific guarantees)
> 2- code that relies on platform specific guarantees (e.g. with using
> volatile and asm memory barriers)
> 3- "slow" code (e.g. with posix mutexes)
> 4- C11 code
How do you know, a priori, that my code is broken? Do I have to
change my passwords again?
Regards,
Uncle Steve
--
The moon has never been closer.
|
|
0
|
|
|
|
Reply
|
stevet810 (127)
|
7/6/2012 10:54:38 PM
|
|
On Fri, Jul 06, 2012 at 08:12:57PM -0700, Joshua Maurice wrote:
> On Jul 6, 3:54�pm, Uncle Steve <stevet...@gmail.com> wrote:
> > On Fri, Jul 06, 2012 at 07:39:56PM -0700, Joshua Maurice wrote:
> > > I will say it again: the C89 and C99 standards do not mean what you
> > > think. What the compiler writers and thread library writers understand
> > > volatile to mean is not what you understand volatile to mean, and at
> > > the end of the day what the compiler writers and thread library
> > > writers think is paramount. You may be able to get this to work for
> > > specific platforms with specific compilers. I suggest you ask in a
> > > location specific to such implementations for such platform specific
> > > hackery. However, do not look at the C standard for guidance on this.
> >
> > Um, what is it that compiler writers and thread library writers
> > understand volatile to mean?
>
> http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2016.html
>
> > > In the C11 parlance, what you're trying to write requires the "weak"
> > > atomics, such as relaxed, acquire, release, and consume memory
> > > operations. Attempting to write stuff like this without C11 support
> > > with the C11 primitives is a wholly platform specific affair, and
> > > neither C89 nor C90 can help you there. volatile specifically cannot
> > > help you (barring platform specific documentation to the contrary).
> >
> > You seem to be making too many assumptions about my code.
>
> I think it's pretty clear from what you've written else-thread.
>
> > > From else-thread:
> > > On Jul 6, 2:29 pm, Uncle Steve <stevet...@gmail.com> wrote:
> > > > On Sat, Jul 07, 2012 at 12:37:59PM +1200, Ian Collins wrote:
> > > > > Then use your platform's native threading primitives.
> >
> > > > Doing so would incur a performance penalty in comparison to my
> > > > synchronization primitives in some circumstances.
> >
> > > That's your problem. You have broken code. Your options are:
> > > 1- broken code (e.g. using volatile and asm memory barriers absent
> > > platform specific guarantees)
> > > 2- code that relies on platform specific guarantees (e.g. with using
> > > volatile and asm memory barriers)
> > > 3- "slow" code (e.g. with posix mutexes)
> > > 4- C11 code
> >
> > How do you know, a priori, that my code is broken? �Do I have to
> > change my passwords again?
>
> How do I know it's broken? By your own admission, you're using
> volatile to try and affect inter-thread visibility (absent constructs
> that actually give inter-thread visibility). By your previous
> statements about using the C standard to derive useful threading
> semantics for volatile, I doubt you've looked at the compiler
> documentation and hardware documentation for your implementation. Thus
> you have broken code. (I don't think you are trying to nit on "it
> could be right by coincidence".)
Well you know what? You're going to have to wait until version 1.0
before you get to decide how wrong it is.
Regards,
Uncle Steve
--
The moon has never been closer.
|
|
0
|
|
|
|
Reply
|
stevet810 (127)
|
7/6/2012 11:25:09 PM
|
|
On 07/ 7/12 06:33 AM, Uncle Steve wrote:
> On Fri, Jul 06, 2012 at 06:11:28PM -0400, Eric Sosman wrote:
>> On 7/6/2012 2:01 PM, Uncle Steve wrote:
>>> On Sat, Jul 07, 2012 at 09:20:57AM +1200, Ian Collins wrote:
>>>> On 07/ 7/12 05:08 AM, Uncle Steve wrote:
>>>>> [...]
>>>>> This is a concern in my mind right now as I'm writing some posix
>>>>> threads code, and so obviously there are a large number of variables
>>>>> which ought to be declared volatile.
>>>>
>>>> What leads you to that conclusion? That line of though usually implies
>>>> you are misunderstanding the relationship between volatile and threads.
>>>>
>>>> What are you trying to achieve with volatile?
>>>
>>> Well, I have some data structures that will be accessed from arbitrary
>>> threads, so I want to make sure there's no funny business happening
>>> behind the scenes where the compiler decides that certain accesses are
>>> optional.
>>
>> Although the latest "C11" Standard adds some multi-threading
>> support, I'll ignore that because (1) C11 implementations are not
>> exactly thick upon the ground as yet, (2) you mentioned you're
>> using Pthreads anyhow, and (3) I haven't studied the C11 threading
>> stuff closely enough to be a competent commentator. ;-)
>>
>> So: As long as you're using an established non-C-specific
>> threading framework, I suggest you visit comp.programming.threads,
>> a newsgroup devoted to multi-threading issues. There you will learn
>> that `volatile' is neither necessary *nor* sufficient in connection
>> with variables shared between threads. (You needn't take my word on
>> this: Just browse the comp.programming.threads archives and you'll
>> see the local experts refuting this misunderstanding every couple
>> weeks. It's comp.programming.threads' equivalent of `void main'.)
>>
>> Or, as my first threading teacher put it, "Don't Do That."
>
> Is there are C standard threading framework?
Yes, read C11. If you are familiar with pthreads, you'll see where most
of the new primitives originate.
> Threaded code is a de-facto reality in today's multi-core environment.
> Save for the embedded sector, most systems will run threads in
> parallel, and that is that.
Only if you incorporate threads in your code either explicitly or
implicitly with something like OpenMP.
> C has to recognize this watershed moment
> in the refinement of the language.
It has.
> While the specifics of an implementation within a given operating
> system are not C issues, certain common optimizations can be a problem
> with certain algorithms, and so there ought to be a C solution to the
> problem. What is it about the volatile attribute that makes it
> insufficient in your mind to the problem presented by threaded code?
Given you are the one going against conventional wisdom, what in your
mind makes volatile a solution to the problem presented by threaded code?
In addition, what is "the problem presented by threaded code"?
--
Ian Collins
|
|
0
|
|
|
|
Reply
|
ian-news (9875)
|
7/6/2012 11:29:02 PM
|
|
On Jul 6, 11:01=A0am, Uncle Steve <stevet...@gmail.com> wrote:
> On Sat, Jul 07, 2012 at 09:20:57AM +1200, Ian Collins wrote:
> > On 07/ 7/12 05:08 AM, Uncle Steve wrote:
> > >This is a concern in my mind right now as I'm writing some posix
> > >threads code, and so obviously there are a large number of variables
> > >which ought to be declared volatile.
>
> > What leads you to that conclusion? =A0That line of though usually impli=
es
> > you are misunderstanding the relationship between volatile and threads.
>
> > What are you trying to achieve with volatile?
>
> Well, I have some data structures that will be accessed from arbitrary
> threads, so I want to make sure there's no funny business happening
> behind the scenes where the compiler decides that certain accesses are
> optional.
>
> [snip]
>
> The variable declared volatile are required otherwise some variable
> accesses are optimized out, giving inaccurate results during the data
> acquisition phase. =A0The problems here are magnified when these access
> patterns occur with competing threads. =A0IIRC, mutexes are not memory
> access barriers and solve different problems.
To be topical, here's the answer for C11: volatile is useless as a
portable threading construct. Do not use it. It's (maybe) useful in a
portable way only for 3 obscure situations: MMIO, setjmp and longjmp
stuff, and signal handling. If you properly use C11 mutexes, there is
absolutely no need for volatile. To paraphrase you else-thread, there
will be no "funny business" if you use mutexes correctly. It tells the
compiler, the hardware, and anything else relevant, to obey the
semantics of the mutex. volatile is similarly useless when you
correctly use the new "weak" C11 atomics. volatile adds nothing
(portably).
Now, to finally answer your question, C11 threading semantics,
including the lack of useful volatile semantics, is basically copied
wholesale from pthreads. Don't use volatile as a portable threading
construct with pthreads either. pthread mutexes do everything you
need. volatile adds absolutely nothing (portably).
(Yes, you can accomplish useful code with volatile for specific
platform and specific compilers with specific settings. That's an
"advanced" topic that I won't cover now.)
Now, what do you mean by "mutexes are not memory access barriers"?
Mutexes do two things: they provide blocking semantics for mutual
exclusion, and they guarantee visibility ala the usual "happens-
before" semantics. Mutexes are implemented with hardware memory
fences / barriers. This is all true for mutexes in pthreads, win32
threads, Boost threads, ACE threads, C11 threads, C++11 threads, and
every case I can recall seeing the word "mutex".
|
|
0
|
|
|
|
Reply
|
joshuamaurice (575)
|
7/6/2012 11:58:09 PM
|
|
Ben Bacarisse <ben.usenet@bsb.me.uk> writes:
> [can optimization sometimes be done even though volatile is used?]
>
> To a very large extent, the C standard washes it hands of volatile
> objects. Sure, it says a fair bit about what *may* happen but it also
> says "[w]hat constitutes an access to an object that has volatile-
> qualified type is implementation-defined" (6.7.3 p7) and "[a]n actual
> implementation need not evaluate part of an expression if it can deduce
> that [...] no needed side effects are produced (including any caused by
> [...] accessing a volatile object)" (5.1.2.3 p4).
>
> Together, these give an implementation so much leeway that you often
> need to refer to the implementation, not the standard. For example, an
> implementation may well be able to deduce that the assignment is not a
> "needed side effect", coming as it does at the start of main. Equally,
> an implementation may treat all accesses to volatile-qualified objects
> with kid gloves.
My conclusion is similar to yours, but we're 180 degrees
apart on the reading of 5.1.2.3 p4. Any access to a
volatile object potentially causes a needed side-effect, and
there is -- by definition -- _no way_ for an implementation
to know whether or which accesses to volatile objects will
cause such a side-effect. However an implementation chooses
to define what actions constitute an access to a volatile
object, these actions _must_ be carried out for each such
access that is evaluated during a program execution, whether
it seems to have any bearing on other "needed" side-effects
or not. All the latitude comes from 6.7.3 p7; none comes
from 5.1.2.3 p4, and in fact just the opposite. Such
actions must in fact be carried out during actual execution,
and must not be "optimized away" no matter whether they seem
to have any bearing on other program behavior or not.
Anyway that's my reading/interpretation; I don't mean to
start any arguments about which reading is right, but I
thought you would want to know that they are different.
|
|
0
|
|
|
|
Reply
|
txr1 (1213)
|
7/7/2012 12:24:50 AM
|
|
On 07/ 7/12 08:27 AM, Uncle Steve wrote:
> On Sat, Jul 07, 2012 at 11:29:02AM +1200, Ian Collins wrote:
>> On 07/ 7/12 06:33 AM, Uncle Steve wrote:
>>>
>>> Is there are C standard threading framework?
>>
>> Yes, read C11. If you are familiar with pthreads, you'll see where most
>> of the new primitives originate.
>
> Well, I don't follow gcc development, so I don't know when those
> features will be available on my system.
Then use your platform's native threading primitives.
>>> Threaded code is a de-facto reality in today's multi-core environment.
>>> Save for the embedded sector, most systems will run threads in
>>> parallel, and that is that.
>>
>> Only if you incorporate threads in your code either explicitly or
>> implicitly with something like OpenMP.
>
> Well, sure, but I'd like to think that multithreaded applications will
> be popular. It helps to improve the interactivity of applications
> with a GUI frontend for one thing, never mind what you can do with
> message-passing interfaces or asynchronus network I/O threads.
There already are.
>>> C has to recognize this watershed moment
>>> in the refinement of the language.
>>
>> It has.
>
> O.K., so do nested structure elements inherit the parent structure's
> volatile attribute?
A volatile T is a volatile T no matter what it contains.
>>> While the specifics of an implementation within a given operating
>>> system are not C issues, certain common optimizations can be a problem
>>> with certain algorithms, and so there ought to be a C solution to the
>>> problem. What is it about the volatile attribute that makes it
>>> insufficient in your mind to the problem presented by threaded code?
>>
>> Given you are the one going against conventional wisdom, what in your
>> mind makes volatile a solution to the problem presented by threaded code?
>>
>> In addition, what is "the problem presented by threaded code"?
>
> Well, code optimization from global CSE might eliminate memory
> accesses to variable that could be modified by other threads in ways
> that affect the algorithm. Depends of the code.
If the code depends on volatile, it is broken. Use the atomics or
synchronisation primitives provided by the language or platform. They
exist for a reason.
--
Ian Collins
|
|
0
|
|
|
|
Reply
|
ian-news (9875)
|
7/7/2012 12:37:59 AM
|
|
Tim Rentsch <txr@alumni.caltech.edu> writes:
> Ben Bacarisse <ben.usenet@bsb.me.uk> writes:
>
>> [can optimization sometimes be done even though volatile is used?]
>>
>> To a very large extent, the C standard washes it hands of volatile
>> objects. Sure, it says a fair bit about what *may* happen but it also
>> says "[w]hat constitutes an access to an object that has volatile-
>> qualified type is implementation-defined" (6.7.3 p7) and "[a]n actual
>> implementation need not evaluate part of an expression if it can deduce
>> that [...] no needed side effects are produced (including any caused by
>> [...] accessing a volatile object)" (5.1.2.3 p4).
>>
>> Together, these give an implementation so much leeway that you often
>> need to refer to the implementation, not the standard. For example, an
>> implementation may well be able to deduce that the assignment is not a
>> "needed side effect", coming as it does at the start of main. Equally,
>> an implementation may treat all accesses to volatile-qualified objects
>> with kid gloves.
>
> My conclusion is similar to yours, but we're 180 degrees
> apart on the reading of 5.1.2.3 p4. Any access to a
> volatile object potentially causes a needed side-effect, and
> there is -- by definition -- _no way_ for an implementation
> to know whether or which accesses to volatile objects will
> cause such a side-effect. However an implementation chooses
> to define what actions constitute an access to a volatile
> object, these actions _must_ be carried out for each such
> access that is evaluated during a program execution, whether
> it seems to have any bearing on other "needed" side-effects
> or not. All the latitude comes from 6.7.3 p7; none comes
> from 5.1.2.3 p4, and in fact just the opposite. Such
> actions must in fact be carried out during actual execution,
> and must not be "optimized away" no matter whether they seem
> to have any bearing on other program behavior or not.
>
> Anyway that's my reading/interpretation; I don't mean to
> start any arguments about which reading is right, but I
> thought you would want to know that they are different.
OK, I can see that. I.e. is there permission to deduce that the side
effect is needed or simply to deduce that it is produced? I will pin my
colours firmly to fence I am sitting on and say hmmm...
--
Ben.
|
|
0
|
|
|
|
Reply
|
ben.usenet (6515)
|
7/7/2012 1:33:42 AM
|
|
On Jul 6, 2:20=A0pm, Uncle Steve <stevet...@gmail.com> wrote:
> On Fri, Jul 06, 2012 at 04:58:09PM -0700, Joshua Maurice wrote:
> > To be topical, here's the answer for C11: volatile is useless as a
> > portable threading construct. Do not use it. It's (maybe) useful in a
> > portable way only for 3 obscure situations: MMIO, setjmp and longjmp
> > stuff, and signal handling. If you properly use C11 mutexes, there is
> > absolutely no need for volatile. To paraphrase you else-thread, there
> > will be no "funny business" if you use mutexes correctly. It tells the
> > compiler, the hardware, and anything else relevant, to obey the
> > semantics of the mutex. volatile is similarly useless when you
> > correctly use the new "weak" C11 atomics. volatile adds nothing
> > (portably).
>
> > Now, to finally answer your question, C11 threading semantics,
> > including the lack of useful volatile semantics, is basically copied
> > wholesale from pthreads. Don't use volatile as a portable threading
> > construct with pthreads either. pthread mutexes do everything you
> > need. volatile adds absolutely nothing (portably).
>
> > (Yes, you can accomplish useful code with volatile for specific
> > platform and specific compilers with specific settings. That's an
> > "advanced" topic that I won't cover now.)
>
> > Now, what do you mean by "mutexes are not memory access barriers"?
> > Mutexes do two things: they provide blocking semantics for mutual
> > exclusion, and they guarantee visibility ala the usual "happens-
> > before" semantics. Mutexes are implemented with hardware memory
> > fences / barriers. This is all true for mutexes in pthreads, win32
> > threads, Boost threads, ACE threads, C11 threads, C++11 threads, and
> > every case I can recall seeing the word "mutex".
>
> Well, mutexes are barriers for the memory accesses associated with the
> mutex itself, but not in general. =A0Reading the above I can tell that
> you think I'm using POSIX mutexes, however that is not the case.
I strongly suggest that you don't use the word "mutex" for your code
because you will confuse most readers. Most readers will associate
"mutex" with both mutual exclusion and "happens-before" global memory
visibility. That's how the term has been commonly used (ex: posix
pthreads), and how it will continue to commonly be used (ex: C11).
>=A0I am
> using a custom locking scheme that allows for concurrent readers, but
> is exclusive for writers. =A0As a consequence, the behavior of
> variables marked volatile in my code is critical, so I don't want to
> mistakenly use it in a way that results in surprises.
>
> Now you can say that what I am doing is non-portable, but you would
> be wrong there since my algorithms are in fact portable as long as I
> am following the rules of the C standard. =A0Perhaps the proper term is
> limited portability as I rely on a tiny amount of platform specific
> asm. =A0(One instruction.)
I will say it again: the C89 and C99 standards do not mean what you
think. What the compiler writers and thread library writers understand
volatile to mean is not what you understand volatile to mean, and at
the end of the day what the compiler writers and thread library
writers think is paramount. You may be able to get this to work for
specific platforms with specific compilers. I suggest you ask in a
location specific to such implementations for such platform specific
hackery. However, do not look at the C standard for guidance on this.
In the C11 parlance, what you're trying to write requires the "weak"
atomics, such as relaxed, acquire, release, and consume memory
operations. Attempting to write stuff like this without C11 support
with the C11 primitives is a wholly platform specific affair, and
neither C89 nor C90 can help you there. volatile specifically cannot
help you (barring platform specific documentation to the contrary).
From else-thread:
On Jul 6, 2:29 pm, Uncle Steve <stevet...@gmail.com> wrote:
> On Sat, Jul 07, 2012 at 12:37:59PM +1200, Ian Collins wrote:
> > Then use your platform's native threading primitives.
>
> Doing so would incur a performance penalty in comparison to my
> synchronization primitives in some circumstances.
That's your problem. You have broken code. Your options are:
1- broken code (e.g. using volatile and asm memory barriers absent
platform specific guarantees)
2- code that relies on platform specific guarantees (e.g. with using
volatile and asm memory barriers)
3- "slow" code (e.g. with posix mutexes)
4- C11 code
|
|
0
|
|
|
|
Reply
|
joshuamaurice (575)
|
7/7/2012 2:39:56 AM
|
|
On Jul 6, 3:54=A0pm, Uncle Steve <stevet...@gmail.com> wrote:
> On Fri, Jul 06, 2012 at 07:39:56PM -0700, Joshua Maurice wrote:
> > I will say it again: the C89 and C99 standards do not mean what you
> > think. What the compiler writers and thread library writers understand
> > volatile to mean is not what you understand volatile to mean, and at
> > the end of the day what the compiler writers and thread library
> > writers think is paramount. You may be able to get this to work for
> > specific platforms with specific compilers. I suggest you ask in a
> > location specific to such implementations for such platform specific
> > hackery. However, do not look at the C standard for guidance on this.
>
> Um, what is it that compiler writers and thread library writers
> understand volatile to mean?
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2016.html
> > In the C11 parlance, what you're trying to write requires the "weak"
> > atomics, such as relaxed, acquire, release, and consume memory
> > operations. Attempting to write stuff like this without C11 support
> > with the C11 primitives is a wholly platform specific affair, and
> > neither C89 nor C90 can help you there. volatile specifically cannot
> > help you (barring platform specific documentation to the contrary).
>
> You seem to be making too many assumptions about my code.
I think it's pretty clear from what you've written else-thread.
> > From else-thread:
> > On Jul 6, 2:29 pm, Uncle Steve <stevet...@gmail.com> wrote:
> > > On Sat, Jul 07, 2012 at 12:37:59PM +1200, Ian Collins wrote:
> > > > Then use your platform's native threading primitives.
>
> > > Doing so would incur a performance penalty in comparison to my
> > > synchronization primitives in some circumstances.
>
> > That's your problem. You have broken code. Your options are:
> > 1- broken code (e.g. using volatile and asm memory barriers absent
> > platform specific guarantees)
> > 2- code that relies on platform specific guarantees (e.g. with using
> > volatile and asm memory barriers)
> > 3- "slow" code (e.g. with posix mutexes)
> > 4- C11 code
>
> How do you know, a priori, that my code is broken? =A0Do I have to
> change my passwords again?
How do I know it's broken? By your own admission, you're using
volatile to try and affect inter-thread visibility (absent constructs
that actually give inter-thread visibility). By your previous
statements about using the C standard to derive useful threading
semantics for volatile, I doubt you've looked at the compiler
documentation and hardware documentation for your implementation. Thus
you have broken code. (I don't think you are trying to nit on "it
could be right by coincidence".)
|
|
0
|
|
|
|
Reply
|
joshuamaurice (575)
|
7/7/2012 3:12:57 AM
|
|
On Jul 6, 4:25=A0pm, Uncle Steve <stevet...@gmail.com> wrote:
> On Fri, Jul 06, 2012 at 08:12:57PM -0700, Joshua Maurice wrote:
> > On Jul 6, 3:54=A0pm, Uncle Steve <stevet...@gmail.com> wrote:
> > > On Fri, Jul 06, 2012 at 07:39:56PM -0700, Joshua Maurice wrote:
> > > > I will say it again: the C89 and C99 standards do not mean what you
> > > > think. What the compiler writers and thread library writers underst=
and
> > > > volatile to mean is not what you understand volatile to mean, and a=
t
> > > > the end of the day what the compiler writers and thread library
> > > > writers think is paramount. You may be able to get this to work for
> > > > specific platforms with specific compilers. I suggest you ask in a
> > > > location specific to such implementations for such platform specifi=
c
> > > > hackery. However, do not look at the C standard for guidance on thi=
s.
>
> > > Um, what is it that compiler writers and thread library writers
> > > understand volatile to mean?
>
> >http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2016.html
>
> > > > In the C11 parlance, what you're trying to write requires the "weak=
"
> > > > atomics, such as relaxed, acquire, release, and consume memory
> > > > operations. Attempting to write stuff like this without C11 support
> > > > with the C11 primitives is a wholly platform specific affair, and
> > > > neither C89 nor C90 can help you there. volatile specifically canno=
t
> > > > help you (barring platform specific documentation to the contrary).
>
> > > You seem to be making too many assumptions about my code.
>
> > I think it's pretty clear from what you've written else-thread.
>
> > > > From else-thread:
> > > > On Jul 6, 2:29 pm, Uncle Steve <stevet...@gmail.com> wrote:
> > > > > On Sat, Jul 07, 2012 at 12:37:59PM +1200, Ian Collins wrote:
> > > > > > Then use your platform's native threading primitives.
>
> > > > > Doing so would incur a performance penalty in comparison to my
> > > > > synchronization primitives in some circumstances.
>
> > > > That's your problem. You have broken code. Your options are:
> > > > 1- broken code (e.g. using volatile and asm memory barriers absent
> > > > platform specific guarantees)
> > > > 2- code that relies on platform specific guarantees (e.g. with usin=
g
> > > > volatile and asm memory barriers)
> > > > 3- "slow" code (e.g. with posix mutexes)
> > > > 4- C11 code
>
> > > How do you know, a priori, that my code is broken? =A0Do I have to
> > > change my passwords again?
>
> > How do I know it's broken? By your own admission, you're using
> > volatile to try and affect inter-thread visibility (absent constructs
> > that actually give inter-thread visibility). By your previous
> > statements about using the C standard to derive useful threading
> > semantics for volatile, I doubt you've looked at the compiler
> > documentation and hardware documentation for your implementation. Thus
> > you have broken code. (I don't think you are trying to nit on "it
> > could be right by coincidence".)
>
> Well you know what? =A0You're going to have to wait until version 1.0
> before you get to decide how wrong it is.
Let me share with you a story. I currently work for a company selling
Enterprise software. The software is heavily intertwined with
databases. We have a license for a large piece of code that does heavy
duty sorting. We have a contract with the guys who wrote this sorting
library for support and maintenance. One of our customers upgraded
some of their hardware, ran some tests - or possibly worse ran actual
usage - and noticed that one row out of a few billion was "missing".
Our engine dropped it, somewhere. It should be in the output, but it
wasn't. Of course, this is very bad for our company. This could cost
millions of dollars in sales. So, we get the guys who wrote the
sorting library, and immediately fly them out to us. We get them to
work on the code. I was only tangentially related to this process. It
took a week or so of work to find and fix the issue. What was the
problem? The guys who wrote the sorting library used "volatile", and
the customer just upgraded to some itanium processor or something. A
quick fix/hack later, and our customer was back in business, our
millions of dollars was secure, as was our company image and
reputation.
Use volatile at your own risk. I don't plan on using your software if
I can help it.
|
|
0
|
|
|
|
Reply
|
joshuamaurice (575)
|
7/7/2012 3:33:45 AM
|
|
On 07/ 7/12 09:29 AM, Uncle Steve wrote:
> On Sat, Jul 07, 2012 at 12:37:59PM +1200, Ian Collins wrote:
>> On 07/ 7/12 08:27 AM, Uncle Steve wrote:
>>> On Sat, Jul 07, 2012 at 11:29:02AM +1200, Ian Collins wrote:
>>>> On 07/ 7/12 06:33 AM, Uncle Steve wrote:
>>>>>
>>>>> Is there are C standard threading framework?
>>>>
>>>> Yes, read C11. If you are familiar with pthreads, you'll see where most
>>>> of the new primitives originate.
>>>
>>> Well, I don't follow gcc development, so I don't know when those
>>> features will be available on my system.
>>
>> Then use your platform's native threading primitives.
>
> Doing so would incur a performance penalty in comparison to my
> synchronization primitives in some circumstances.
In other words, correctness would incur a performance penalty in
comparison to your synchronization primitives in some circumstances.
>>> O.K., so do nested structure elements inherit the parent structure's
>>> volatile attribute?
>>
>> A volatile T is a volatile T no matter what it contains.
>
> Look. In threaded applications, the truth associated with the value
> associated with certain variables can be somewhat indeterminate. If
> that's true in your code, you might want to be aware of it.
If I could parse that, I'd respond to it.
>>>>> While the specifics of an implementation within a given operating
>>>>> system are not C issues, certain common optimizations can be a problem
>>>>> with certain algorithms, and so there ought to be a C solution to the
>>>>> problem. What is it about the volatile attribute that makes it
>>>>> insufficient in your mind to the problem presented by threaded code?
>>>>
>>>> Given you are the one going against conventional wisdom, what in your
>>>> mind makes volatile a solution to the problem presented by threaded code?
>>>>
>>>> In addition, what is "the problem presented by threaded code"?
>>>
>>> Well, code optimization from global CSE might eliminate memory
>>> accesses to variable that could be modified by other threads in ways
>>> that affect the algorithm. Depends of the code.
>>
>> If the code depends on volatile, it is broken. Use the atomics or
>> synchronisation primitives provided by the language or platform. They
>> exist for a reason.
>
> You aren't listening to me.
You aren't making much sense.
An example of you your synchronization primitives would clear things up.
--
Ian Collins
|
|
0
|
|
|
|
Reply
|
ian-news (9875)
|
7/7/2012 3:50:59 AM
|
|
Uncle Steve <stevet810@gmail.com> writes:
> What is it about the volatile attribute that makes it
> insufficient in your mind to the problem presented by
> threaded code?
Let's take a specific example:
volatile int a = 1, b = 2;
... start up threads X and Y ...
... in parallel ... {
... in thread X ... {
a = 3;
b = 4;
}
... in thread Y ... {
printf( "a = %d\n", a );
printf( "b = %d\n", b );
}
}
Question: what values for a and b might thread Y print?
Answer: Besides the obvious possibilities of {1,2}, {3,2},
and {3,4}, the Standard allows a conforming implementation
to define volatile access in such a way that thread Y might
print 1 for a but 4 for b. In other words, using volatile
is not enough to guarantee write ordering. Moreover that
lack of guarantee holds true even if write-barrier opcodes
are used before and after each volatile-qualified access.
So it's kind of unfortunate but true - the meaning of volatile
is inherently non-portable. You may be able to write code
using volatile to do inter-thread communication that works
reliably on a number of implementations, but that depends
on decisions made by those implementations, and not just on
the Standard.
> I don't have a copy of the standard, [...]
Easily solved. For C99:
http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf
For C11:
http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf
|
|
0
|
|
|
|
Reply
|
txr1 (1213)
|
7/7/2012 7:10:02 AM
|
|
On Sat, Jul 07, 2012 at 12:10:02AM -0700, Tim Rentsch wrote:
> Uncle Steve <stevet810@gmail.com> writes:
>
> > What is it about the volatile attribute that makes it
> > insufficient in your mind to the problem presented by
> > threaded code?
>
> Let's take a specific example:
>
> volatile int a = 1, b = 2;
>
> ... start up threads X and Y ...
>
> ... in parallel ... {
>
> ... in thread X ... {
> a = 3;
> b = 4;
> }
>
> ... in thread Y ... {
> printf( "a = %d\n", a );
> printf( "b = %d\n", b );
> }
>
> }
>
> Question: what values for a and b might thread Y print?
>
> Answer: Besides the obvious possibilities of {1,2}, {3,2},
> and {3,4}, the Standard allows a conforming implementation
> to define volatile access in such a way that thread Y might
> print 1 for a but 4 for b. In other words, using volatile
> is not enough to guarantee write ordering. Moreover that
> lack of guarantee holds true even if write-barrier opcodes
> are used before and after each volatile-qualified access.
>
> So it's kind of unfortunate but true - the meaning of volatile
> is inherently non-portable. You may be able to write code
> using volatile to do inter-thread communication that works
> reliably on a number of implementations, but that depends
> on decisions made by those implementations, and not just on
> the Standard.
I never said I was relying on the volatile attribute exclusively to
provide coherence between threads. The bug up my butt concerning the
volatile keyword is more related to presenting consistent results in
d ata structures that might transition several states quickly, and I
don't want the presentation of those states to be optimized out
simply because the compiler decides it can.
I showed the potential for this in the simple timing code I posted
earlier. Note that removing the volatile qualifier from variables
that have them changes the output. So, my interest in the volatile
attribute is not related to thread synchronization so much as it is in
general data coherence in a complex threaded application.
Note that I have not yet found out whether structure attributes
propogate to substructures that are structure elements.
> > I don't have a copy of the standard, [...]
>
> Easily solved. For C99:
>
> http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf
>
> For C11:
>
> http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf
Thanks, I'll have a look.
I've been reviewing the code I posted previously, and I can no longer
make it malfunction the way it was breaking. I forget what I changed,
so I'm going to have to check into it before I can better illustrate
the phenomenon.
Regards,
Uncle Steve
--
The moon has never been closer.
|
|
0
|
|
|
|
Reply
|
stevet810 (127)
|
7/7/2012 8:40:55 AM
|
|
Tim Rentsch <txr@alumni.caltech.edu> writes:
> Question: what values for a and b might thread Y print?
>
> Answer: Besides the obvious possibilities of {1,2}, {3,2},
> and {3,4}, the Standard allows a conforming implementation
> to define volatile access in such a way that thread Y might
> print 1 for a but 4 for b.
Would it be correct to say that if both a & b are volatile, then the
compiler may not reorder them, but the processor may (in the absence of
an explicit memory barrier)?
In which case, volatile may be (non-portably) sufficient for i386
uni-processors, say.
|
|
0
|
|
|
|
Reply
|
gwowen (518)
|
7/7/2012 9:00:51 AM
|
|
On Sat, Jul 07, 2012 at 08:16:53AM -0400, Eric Sosman wrote:
> On 7/6/2012 2:33 PM, Uncle Steve wrote:
> >On Fri, Jul 06, 2012 at 06:11:28PM -0400, Eric Sosman wrote:
> >>[...]
> >> Although the latest "C11" Standard adds some multi-threading
> >>support, [...]
> >>
> >> [...]I suggest you visit comp.programming.threads,
> >>a newsgroup devoted to multi-threading issues. There you will learn
> >>that `volatile' is neither necessary *nor* sufficient in connection
> >>with variables shared between threads. [...]
> >
> >Is there are C standard threading framework?
>
> See above.
>
> >Threaded code is a de-facto reality in today's multi-core environment.
> >Save for the embedded sector, most systems will run threads in
> >parallel, and that is that. C has to recognize this watershed moment
> >in the refinement of the language.
>
> See above.
>
> >What is it about the volatile attribute that makes it
> >insufficient in your mind to the problem presented by threaded code?
>
> "Then Kolokolo Bird said, with a mournful cry, 'Go to the banks of
> the great grey-green, greasy Limpopo River -- er, that is, the G,G-G,G
> comp.programming.threads newsgroup -- all set about with fever-trees,
> and find out.'"
>
> By "go," I mean "go away," as in "Don't continue this thread in
> comp.lang.c unless you specifically wish to discuss the threading
> features of C11." Follow-ups set.
Your message excells as an example of myopic religious orthodoxy. I'm
not actually suggesting that there should be changes to the standard,
so I don't quite understand why you seem so threatened.
Regards,
Uncle Steve
--
The moon has never been closer.
|
|
0
|
|
|
|
Reply
|
stevet810 (127)
|
7/7/2012 9:12:17 AM
|
|
On Sat, Jul 07, 2012 at 09:24:25AM -0400, Eric Sosman wrote:
> On 7/7/2012 5:12 AM, Uncle Steve wrote:
> >On Sat, Jul 07, 2012 at 08:16:53AM -0400, Eric Sosman wrote:
> >>On 7/6/2012 2:33 PM, Uncle Steve wrote:
> >>>On Fri, Jul 06, 2012 at 06:11:28PM -0400, Eric Sosman wrote:
> >>>>[...]
> >>>> Although the latest "C11" Standard adds some multi-threading
> >>>>support, [...]
> >>>>
> >>>> [...]I suggest you visit comp.programming.threads,
> >>>>a newsgroup devoted to multi-threading issues. There you will learn
> >>>>that `volatile' is neither necessary *nor* sufficient in connection
> >>>>with variables shared between threads. [...]
> >>>
> >>>Is there are C standard threading framework?
> >>
> >> See above.
> >>
> >>>Threaded code is a de-facto reality in today's multi-core environment.
> >>>Save for the embedded sector, most systems will run threads in
> >>>parallel, and that is that. C has to recognize this watershed moment
> >>>in the refinement of the language.
> >>
> >> See above.
> >>
> >>>What is it about the volatile attribute that makes it
> >>>insufficient in your mind to the problem presented by threaded code?
> >>
> >> "Then Kolokolo Bird said, with a mournful cry, 'Go to the banks of
> >>the great grey-green, greasy Limpopo River -- er, that is, the G,G-G,G
> >>comp.programming.threads newsgroup -- all set about with fever-trees,
> >>and find out.'"
> >>
> >> By "go," I mean "go away," as in "Don't continue this thread in
> >>comp.lang.c unless you specifically wish to discuss the threading
> >>features of C11." Follow-ups set.
> >
> >Your message excells as an example of myopic religious orthodoxy. I'm
> >not actually suggesting that there should be changes to the standard,
> >so I don't quite understand why you seem so threatened.
>
> When you ask (somewhat incoherently) "Is there are C standard
> threading framework," which part of
>
> THE LATEST "C11" STANDARD ADDS SOME MULTI-THREADING SUPPORT
>
> are you having trouble understanding?
>
> C11's threading support is on-topic here in comp.lang.c -- but
> you've indicated that you're not interested in C11 threads and in fact
> are using Pthreads instead. Pthreads is off-topic here, so please help
> reduce the noise by removing the discussion to comp.programming.threads
> which "excells" [sic] on that topic.
It was previously mentioned that C11 threads were copied from
pthreads, although presumably there would be some difference in
semantics. Please don't cut off my hands if I inadvertently say
something against the standard, eh? I approve of standards, and I'm
not just saying that to get on your good side.
Regards,
Uncle Steve
--
The moon has never been closer.
|
|
0
|
|
|
|
Reply
|
stevet810 (127)
|
7/7/2012 10:07:54 AM
|
|
On Sat, Jul 07, 2012 at 03:47:38PM +0200, Jens Gustedt wrote:
> Am 07.07.2012 01:58, schrieb Joshua Maurice:
>
> > To be topical, here's the answer for C11: volatile is useless as a
> > portable threading construct. Do not use it. It's (maybe) useful in a
> > portable way only for 3 obscure situations: MMIO, setjmp and longjmp
> > stuff, and signal handling.
>
> No, this is misleading. volatile alone is not sufficient to warrant data
> consistency between threads, but is necessary. volatile and _Atomic are
> complementary specifications that *both* are needed if you want to make
> sure that a thread always uses the latest stored value of a variable. If
> you have
>
> static int _Atomic a = ATOMIC_VAR_INIT(23);
> if (tester)
> for (;;) printf("current value is %d\n", a);
>
> The compiler is allowed to change the loop into something equivalent to
>
> {
> int tmp = a; // do an atomic_load operation here
> for (;;) printf("current value is %d\n", tmp);
> }
>
> and thus always print the same value.
>
> static int _Atomic volatile a = ATOMIC_VAR_INIT(23);
>
> would assure that each execution of printf would see an updated value.
>
> > If you properly use C11 mutexes, there is
> > absolutely no need for volatile.
>
> C11 mutexes (as are POSIX mutexes) are much too heavy machinery where a
> simple atomic would suffice. I think that atomics and the precise data
> consistency model are the real novelty in C11 (and C++1). That is to
That was my thinking. The POSIX mutex api is really kind of awkward,
and on Linux the underlying implementation is more complicated than it
has to be. Currently I'm developing on x86_64 and x86, and I chose to
use "lock incl %0" and then inspect the result to detect collisions.
It is obviously very fast, and I can control how long a thread will
spin on the lock before sleeping. I have yet to investigate whether I
need to use a different instruction for the 64-bit arch.
A compiler supported atomic type would allow the removal of the
minimal inline asm that I'm using, but I can keep it to preserve some
backward compatibility with older systems.
> provide a possibility of communicating safely between different threads
> (and with signal handlers) without relying on OS support (or almost).
One potential application for non-mutex locking structures occurs in
some virtualized environments. Under the Xen hypervisor, it is
possible to share virtual memory segments between guest domains. The
facility is called 'grant pages', and looks a little like a SYSV
shared memory segment to the application. With such a memory window,
a ring buffer (or equivalent algorithm) can allow inter-domain
communications without any system calls. The same goes, of course,
for a ring-buffer instantiated in SYSV shared memory, but that is
limited to communications between processes on the same host.
Compared to TCP/IP IPC, this should greatly improve the available IPC
bandwidth among virtualized hosts on a multi-core platform.
I haven't yet got to the point of integrating support for Xen in any
of my code, but it's on my todo list.
Regards,
Uncle Steve
--
The moon has never been closer.
|
|
0
|
|
|
|
Reply
|
stevet810 (127)
|
7/7/2012 10:58:48 AM
|
|
On 7/6/2012 2:33 PM, Uncle Steve wrote:
> On Fri, Jul 06, 2012 at 06:11:28PM -0400, Eric Sosman wrote:
>> [...]
>> Although the latest "C11" Standard adds some multi-threading
>> support, [...]
>>
>> [...]I suggest you visit comp.programming.threads,
>> a newsgroup devoted to multi-threading issues. There you will learn
>> that `volatile' is neither necessary *nor* sufficient in connection
>> with variables shared between threads. [...]
>
> Is there are C standard threading framework?
See above.
> Threaded code is a de-facto reality in today's multi-core environment.
> Save for the embedded sector, most systems will run threads in
> parallel, and that is that. C has to recognize this watershed moment
> in the refinement of the language.
See above.
> What is it about the volatile attribute that makes it
> insufficient in your mind to the problem presented by threaded code?
"Then Kolokolo Bird said, with a mournful cry, 'Go to the banks of
the great grey-green, greasy Limpopo River -- er, that is, the G,G-G,G
comp.programming.threads newsgroup -- all set about with fever-trees,
and find out.'"
By "go," I mean "go away," as in "Don't continue this thread in
comp.lang.c unless you specifically wish to discuss the threading
features of C11." Follow-ups set.
--
Eric Sosman
esosman@ieee-dot-org.invalid
|
|
0
|
|
|
|
Reply
|
esosman2 (2945)
|
7/7/2012 12:16:53 PM
|
|
On 7/7/2012 5:12 AM, Uncle Steve wrote:
> On Sat, Jul 07, 2012 at 08:16:53AM -0400, Eric Sosman wrote:
>> On 7/6/2012 2:33 PM, Uncle Steve wrote:
>>> On Fri, Jul 06, 2012 at 06:11:28PM -0400, Eric Sosman wrote:
>>>> [...]
>>>> Although the latest "C11" Standard adds some multi-threading
>>>> support, [...]
>>>>
>>>> [...]I suggest you visit comp.programming.threads,
>>>> a newsgroup devoted to multi-threading issues. There you will learn
>>>> that `volatile' is neither necessary *nor* sufficient in connection
>>>> with variables shared between threads. [...]
>>>
>>> Is there are C standard threading framework?
>>
>> See above.
>>
>>> Threaded code is a de-facto reality in today's multi-core environment.
>>> Save for the embedded sector, most systems will run threads in
>>> parallel, and that is that. C has to recognize this watershed moment
>>> in the refinement of the language.
>>
>> See above.
>>
>>> What is it about the volatile attribute that makes it
>>> insufficient in your mind to the problem presented by threaded code?
>>
>> "Then Kolokolo Bird said, with a mournful cry, 'Go to the banks of
>> the great grey-green, greasy Limpopo River -- er, that is, the G,G-G,G
>> comp.programming.threads newsgroup -- all set about with fever-trees,
>> and find out.'"
>>
>> By "go," I mean "go away," as in "Don't continue this thread in
>> comp.lang.c unless you specifically wish to discuss the threading
>> features of C11." Follow-ups set.
>
> Your message excells as an example of myopic religious orthodoxy. I'm
> not actually suggesting that there should be changes to the standard,
> so I don't quite understand why you seem so threatened.
When you ask (somewhat incoherently) "Is there are C standard
threading framework," which part of
THE LATEST "C11" STANDARD ADDS SOME MULTI-THREADING SUPPORT
are you having trouble understanding?
C11's threading support is on-topic here in comp.lang.c -- but
you've indicated that you're not interested in C11 threads and in fact
are using Pthreads instead. Pthreads is off-topic here, so please help
reduce the noise by removing the discussion to comp.programming.threads
which "excells" [sic] on that topic.
--
Eric Sosman
esosman@ieee-dot-org.invalid
|
|
0
|
|
|
|
Reply
|
esosman2 (2945)
|
7/7/2012 1:24:25 PM
|
|
Am 07.07.2012 01:58, schrieb Joshua Maurice:
> To be topical, here's the answer for C11: volatile is useless as a
> portable threading construct. Do not use it. It's (maybe) useful in a
> portable way only for 3 obscure situations: MMIO, setjmp and longjmp
> stuff, and signal handling.
No, this is misleading. volatile alone is not sufficient to warrant data
consistency between threads, but is necessary. volatile and _Atomic are
complementary specifications that *both* are needed if you want to make
sure that a thread always uses the latest stored value of a variable. If
you have
static int _Atomic a = ATOMIC_VAR_INIT(23);
if (tester)
for (;;) printf("current value is %d\n", a);
The compiler is allowed to change the loop into something equivalent to
{
int tmp = a; // do an atomic_load operation here
for (;;) printf("current value is %d\n", tmp);
}
and thus always print the same value.
static int _Atomic volatile a = ATOMIC_VAR_INIT(23);
would assure that each execution of printf would see an updated value.
> If you properly use C11 mutexes, there is
> absolutely no need for volatile.
C11 mutexes (as are POSIX mutexes) are much too heavy machinery where a
simple atomic would suffice. I think that atomics and the precise data
consistency model are the real novelty in C11 (and C++1). That is to
provide a possibility of communicating safely between different threads
(and with signal handlers) without relying on OS support (or almost).
Jens
|
|
0
|
|
|
|
Reply
|
Jens.Gustedt1 (231)
|
7/7/2012 1:47:38 PM
|
|
Uncle Steve <stevet810@gmail.com> writes:
> On Sat, Jul 07, 2012 at 12:37:59PM +1200, Ian Collins wrote:
> > If the code depends on volatile, it is broken. Use the atomics or
> > synchronisation primitives provided by the language or platform. They
> > exist for a reason.
>
> You aren't listening to me.
PKB.
I think you'll find that it's not Ian who needs the assistance.
Phil
--
> I'd argue that there is much evidence for the existence of a God.
Pics or it didn't happen.
-- Tom (/. uid 822)
|
|
0
|
|
|
|
Reply
|
thefatphil_demunged (1558)
|
7/7/2012 2:04:14 PM
|
|
Tim Rentsch <txr@alumni.caltech.edu> writes:
> Uncle Steve <stevet810@gmail.com> writes:
>
> > What is it about the volatile attribute that makes it
> > insufficient in your mind to the problem presented by
> > threaded code?
>
> Let's take a specific example:
>
> volatile int a = 1, b = 2;
>
> ... start up threads X and Y ...
>
> ... in parallel ... {
>
> ... in thread X ... {
> a = 3;
> b = 4;
> }
>
> ... in thread Y ... {
> printf( "a = %d\n", a );
> printf( "b = %d\n", b );
> }
>
> }
>
> Question: what values for a and b might thread Y print?
>
> Answer: Besides the obvious possibilities of {1,2}, {3,2},
> and {3,4}, the Standard allows a conforming implementation
> to define volatile access in such a way that thread Y might
> print 1 for a but 4 for b. In other words, using volatile
> is not enough to guarantee write ordering.
The two reads and two writes can be sequenced 6 ways.
ra rb wa wb - {1,2}
ra wa rb wb - {1,2}
ra wa wb rb - {1,4}
wa ra rb wb - {3,2}
wa ra wb rb - {3,4}
wa wb ra rb - {3,4}
{1.4} is not a violation of write ordering - wa is still
before wb.
Changing the order of the two printfs in thread Y would yield
rb ra wa wb - {2,1}
rb wa ra wb - {2,3}
rb wa wb ra - {2,3}
wa rb ra wb - {2,3}
wa rb wb ra - {2,3}
wa wb rb ra - {4,3}
Here the reversed output {4,1} is impossible, as it violates
the write ordering in thread X. (And in fact this read-order
being the reverse of write order is a common technique for
reducing the number of possibilities if you don't wish to
turn both the block read and the block writes into atomic
transactions.)
Phil
--
> I'd argue that there is much evidence for the existence of a God.
Pics or it didn't happen.
-- Tom (/. uid 822)
|
|
0
|
|
|
|
Reply
|
thefatphil_demunged (1558)
|
7/7/2012 2:20:36 PM
|
|
Phil Carmody <thefatphil_demunged@yahoo.co.uk> writes:
> Tim Rentsch <txr@alumni.caltech.edu> writes:
>> Uncle Steve <stevet810@gmail.com> writes:
>>
>> > What is it about the volatile attribute that makes it
>> > insufficient in your mind to the problem presented by
>> > threaded code?
>>
>> Let's take a specific example:
>>
>> volatile int a = 1, b = 2;
>>
>> ... start up threads X and Y ...
>>
>> ... in parallel ... {
>>
>> ... in thread X ... {
>> a = 3;
>> b = 4;
>> }
>>
>> ... in thread Y ... {
>> printf( "a = %d\n", a );
>> printf( "b = %d\n", b );
>> }
>>
>> }
>>
>> Question: what values for a and b might thread Y print?
>>
>> Answer: Besides the obvious possibilities of {1,2}, {3,2},
>> and {3,4}, the Standard allows a conforming implementation
>> to define volatile access in such a way that thread Y might
>> print 1 for a but 4 for b. In other words, using volatile
>> is not enough to guarantee write ordering.
>
> The two reads and two writes can be sequenced 6 ways.
>
> ra rb wa wb - {1,2}
> ra wa rb wb - {1,2}
> ra wa wb rb - {1,4}
> wa ra rb wb - {3,2}
> wa ra wb rb - {3,4}
> wa wb ra rb - {3,4}
>
> {1.4} is not a violation of write ordering - wa is still
> before wb.
>
> Changing the order of the two printfs in thread Y would yield
>
> rb ra wa wb - {2,1}
> rb wa ra wb - {2,3}
> rb wa wb ra - {2,3}
> wa rb ra wb - {2,3}
> wa rb wb ra - {2,3}
> wa wb rb ra - {4,3}
>
> Here the reversed output {4,1} is impossible, as it violates
> the write ordering in thread X. (And in fact this read-order
> being the reverse of write order is a common technique for
> reducing the number of possibilities if you don't wish to
> turn both the block read and the block writes into atomic
> transactions.)
Good analysis. Clearly a weakness in my example;
thank you for pointing this out.
However, even if the printf()s in thread Y are done in
the other order, the reversed output {4,1} may occur in
a conforming implementation. This result holds because
of the great latitude granted by 6.7.3 p7, which allows
implementations to define what it means to access a
volatile object. That freedom means that how the
C program behaves could be different than what would
happen if, for example, an equivalent assembly language
program were run directly on the native hardware.
|
|
0
|
|
|
|
Reply
|
txr1 (1213)
|
7/7/2012 5:33:14 PM
|
|
Gareth Owen <gwowen@gmail.com> writes:
> Tim Rentsch <txr@alumni.caltech.edu> writes:
>
>> Question: what values for a and b might thread Y print?
>>
>> Answer: Besides the obvious possibilities of {1,2}, {3,2},
>> and {3,4}, the Standard allows a conforming implementation
>> to define volatile access in such a way that thread Y might
>> print 1 for a but 4 for b.
>
> Would it be correct to say that if both a & b are volatile, then the
> compiler may not reorder them, but the processor may (in the absence of
> an explicit memory barrier)?
It's more complicated than that. Certainly a write-buffer may
change the order of the writes (as one example), but it's also
true that "memory access" as far as a particular implementation is
concerned might not be the same as native machine instructions.
For a typical implementation, it's probably true that there will
be native machine instructions for every volatile access, and in
the same order as the abstract semantics would imply, but the
Standard allows enough latitude that implementations don't have to
do that (if they don't want to).
> In which case, volatile may be (non-portably) sufficient for i386
> uni-processors, say.
Again, it's likely that this will be true for most such
implementations, but the Standard doesn't force it to be
true if an implementation is determined to behave otherwise
(please excuse the anthropomorphism).
|
|
0
|
|
|
|
Reply
|
txr1 (1213)
|
7/7/2012 5:55:25 PM
|
|
On Thursday, July 5, 2012 11:59:49 AM UTC-7, pete wrote:
> Myth__Buster wrote:
> >
> > #include <stdio.h>
> >
> > struct InterruptData
> > {
> > int numberOfInterrupts;
> > volatile int timer;
> > int numberOfTimesInterruptsMasked;
> > } InterruptMonitor;
> >
> > /*
> > * Interrupt handler code.
> > */
> >
> > int main(void)
> > {
> > InterruptMonitor = InterruptMonitor;
>
> > InterruptMonitor = InterruptMonitor;
> > - Should this be a candidate for compile-time optimization
> > in spite of having a field qualified as volatile?
>
> I don't think so.
>
> Merely accessing a volatile object,
> is considered to be a side effect.
>
>
> --
> pete
Okay, thanks for the clarification.
Cheers,
Raghavan Santhanam
|
|
0
|
|
|
|
Reply
|
raghavanil4m (37)
|
7/7/2012 7:53:23 PM
|
|
On Thursday, July 5, 2012 3:22:49 PM UTC-7, Ben Bacarisse wrote:
> Myth__Buster <raghavanil4m@gmail.com> writes:
>
> > #include <stdio.h>
> >
> > struct InterruptData
> > {
> > int numberOfInterrupts;
> > volatile int timer;
> > int numberOfTimesInterruptsMasked;
> > } InterruptMonitor;
> >
> >
> > /*
> > * Interrupt handler code.
> > */
> >
> > int main(void)
> > {
> > InterruptMonitor = InterruptMonitor;
> <snip>
> > return 0;
> > }
> >
> > -------------
> >
> > InterruptMonitor = InterruptMonitor; - Should this be a candidate for
> > compile-time optimization in spite of having a field qualified as
> > volatile?
>
> I fear that your example may be one "a bit like" the code you really
> care about, but I have to talk about what you post...
>
> To a very large extent, the C standard washes it hands of volatile
> objects. Sure, it says a fair bit about what *may* happen but it also
> says "[w]hat constitutes an access to an object that has volatile-
> qualified type is implementation-defined" (6.7.3 p7) and "[a]n actual
> implementation need not evaluate part of an expression if it can deduce
> that [...] no needed side effects are produced (including any caused by
> [...] accessing a volatile object)" (5.1.2.3 p4).
>
> Together, these give an implementation so much leeway that you often
> need to refer to the implementation, not the standard. For example, an
> implementation may well be able to deduce that the assignment is not a
> "needed side effect", coming as it does at the start of main. Equally,
> an implementation may treat all accesses to volatile-qualified objects
> with kid gloves.
>
> --
> Ben.
Okay, thanks for the clarification.
Cheers,
Raghavan Santhanam
|
|
0
|
|
|
|
Reply
|
raghavanil4m (37)
|
7/7/2012 7:53:55 PM
|
|
Uncle Steve <stevet810@gmail.com> writes:
[...]
> It was previously mentioned that C11 threads were copied from
> pthreads, although presumably there would be some difference in
> semantics. Please don't cut off my hands if I inadvertently say
> something against the standard, eh? I approve of standards, and I'm
> not just saying that to get on your good side.
Nobody is cutting off your hands. It's being suggested that,
unless you specifically want to discuss C11's threading support,
this discussion would be more appropriate in a different newsgroup.
Please don't overreact.
--
Keith Thompson (The_Other_Keith) kst-u@mib.org <http://www.ghoti.net/~kst>
Will write code for food.
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
|
|
0
|
|
|
|
Reply
|
kst-u (21460)
|
7/7/2012 8:26:07 PM
|
|
On Jul 7, 2:00=A0am, Gareth Owen <gwo...@gmail.com> wrote:
> Would it be correct to say that if both a & b are volatile, then the
> compiler may not reorder them, but the processor may (in the absence of
> an explicit memory barrier)?
>
> In which case, volatile may be (non-portably) sufficient for i386
> uni-processors, say.
Perhaps. You'd have to check the hardware docs for the i386 and the
compiler docs to see how it treats volatile. I know some parts of my
company do exactly hacks like this. They wrote some message passing
software which has some of the highest throughput rates of any message
passing software in existence, and they did it by using volatile,
using specific guarantees of the x86, using specific implicit or
explicit guarantees of the compiler, and using some inline assembly. I
actually had the opportunity to hear a technical presentation by them
for my team, and I specifically asked what they do for other
platforms. The answer is that they only support the x86 architecture
(give or take - I'm not the expert), and that's apparently not a
problem as their customers are "fine" with that.
|
|
0
|
|
|
|
Reply
|
joshuamaurice (575)
|
7/7/2012 10:13:24 PM
|
|
On Jul 7, 6:47 am, Jens Gustedt <jens.gust...@loria.fr> wrote:
> Am 07.07.2012 01:58, schrieb Joshua Maurice:
>
> > To be topical, here's the answer for C11: volatile is useless as a
> > portable threading construct. Do not use it. It's (maybe) useful in a
> > portable way only for 3 obscure situations: MMIO, setjmp and longjmp
> > stuff, and signal handling.
>
> No, this is misleading. volatile alone is not sufficient to warrant data
> consistency between threads, but is necessary. volatile and _Atomic are
> complementary specifications that *both* are needed if you want to make
> sure that a thread always uses the latest stored value of a variable. If
> you have
>
> static int _Atomic a = ATOMIC_VAR_INIT(23);
> if (tester)
> for (;;) printf("current value is %d\n", a);
>
> The compiler is allowed to change the loop into something equivalent to
>
> {
> int tmp = a; // do an atomic_load operation here
> for (;;) printf("current value is %d\n", tmp);
> }
>
> and thus always print the same value.
>
> static int _Atomic volatile a = ATOMIC_VAR_INIT(23);
>
> would assure that each execution of printf would see an updated value.
(Let's assume you were talking about C11.) You are simply wrong. That
is not what the standard says. This is not what people on the
standards committee are saying. That is not what compile writers are
implementing. This is not what the experts are saying. volatile is
useless for threading code by the C11 standard. Entirely useless for
inter-thread communication.
However, having said that, your example is an interesting one. The
specific rule which disallows the above compiler transformation is an
obscure one, and not one of my favorites. I've tried to discuss this
very issue on comp.std.c++, with no replies IIRC. C11 threading, being
largely just a copy of C++11 threading, has the rule in question. In
the draft I have lying around of C11, n1570, the rule is:
n1570: 7.17.3 Order and consistency / 16
[quote]
Implementations should make atomic stores visible to atomic loads
within a reasonable amount of time.
[/quote]
That rule disallows the above transformation. The only reasonable
reading of that rule is to prevent compiler transformations like the
one above.
There's also one other new obscure rule, again copied from C++11 to
C11, which has some relevance here.
n1570: 6.8.5 Iteration statements / 6
[quote]
An iteration statement whose controlling expression is not a constant
expression, that performs no input/output operations, does not access
volatile objects, and performs no synchronization or atomic operations
in its body, controlling expression, or (in the case of a for
statement) its expression-3, may be assumed by the implementation to
terminate.
[/quote]
This generated a lot of discussion on the C++ newsgroups. In short,
this says:
> for(;;);
is now undefined behavior. I think I dislike the wording of this one
even more than the earlier rule. However, going with the "spirit" of
this rule, I might argue that above compiler transformation is
disallowed; if you're not allowed to make a non-terminating loop with
a constant controlling expression, then the compiler probably
shouldn't be allowed to make one either. Still, I want to emphasize
that reading is merely IMHO, whereas "7.17.3 Order and consistency /
16" much less ambiguously disallows the above compiler
transformation.
For further reading, see:
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2016.html
http://msdn.microsoft.com/en-us/library/12a04hfd.aspx
http://kernel.org/doc/Documentation/volatile-considered-harmful.txt
http://software.intel.com/en-us/blogs/2007/11/30/volatile-almost-useless-for-multi-threaded-programming/
http://www.aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf
http://kerneltrap.org/Linux/Volatile_Superstition
The C++11 and C11 standards.
|
|
0
|
|
|
|
Reply
|
joshuamaurice (575)
|
7/7/2012 11:19:19 PM
|
|
Am 08.07.2012 01:19, schrieb Joshua Maurice:
> On Jul 7, 6:47 am, Jens Gustedt <jens.gust...@loria.fr> wrote:
>> Am 07.07.2012 01:58, schrieb Joshua Maurice:
>>
>>> To be topical, here's the answer for C11: volatile is useless as a
>>> portable threading construct. Do not use it. It's (maybe) useful in a
>>> portable way only for 3 obscure situations: MMIO, setjmp and longjmp
>>> stuff, and signal handling.
>>
>> No, this is misleading. volatile alone is not sufficient to warrant data
>> consistency between threads, but is necessary. volatile and _Atomic are
>> complementary specifications that *both* are needed if you want to make
>> sure that a thread always uses the latest stored value of a variable. If
>> you have
>>
>> static int _Atomic a = ATOMIC_VAR_INIT(23);
>> if (tester)
>> for (;;) printf("current value is %d\n", a);
>>
>> The compiler is allowed to change the loop into something equivalent to
>>
>> {
>> int tmp = a; // do an atomic_load operation here
>> for (;;) printf("current value is %d\n", tmp);
>> }
>>
>> and thus always print the same value.
>>
>> static int _Atomic volatile a = ATOMIC_VAR_INIT(23);
>>
>> would assure that each execution of printf would see an updated value.
>
> (Let's assume you were talking about C11.)
Sure I was, _Atomic only comes with C11
> You are simply wrong. That
> is not what the standard says. This is not what people on the
> standards committee are saying. That is not what compile writers are
> implementing. This is not what the experts are saying. volatile is
> useless for threading code by the C11 standard. Entirely useless for
> inter-thread communication.
Then read the example I gave again :)
> However, having said that, your example is an interesting one. The
> specific rule which disallows the above compiler transformation is an
> obscure one, and not one of my favorites. I've tried to discuss this
> very issue on comp.std.c++, with no replies IIRC. C11 threading, being
> largely just a copy of C++11 threading, has the rule in question. In
> the draft I have lying around of C11, n1570, the rule is:
>
> n1570: 7.17.3 Order and consistency / 16
> [quote]
> Implementations should make atomic stores visible to atomic loads
> within a reasonable amount of time.
> [/quote]
The "should" makes it clearly a non-normative part of the standard and
leaves it to the appreciation of the compiler implementor what
"reasonable amount of time" is.
If the implementor decides that "reasonable amount of time" is 1
milli-second, the loop could be implemented to only do an update each
milli-second.
Said otherwise, there can be no guarantee that for the non-volatile
case an implementation decides to skip a load operation somewhere that
would bring a fresh value into some other thread.
> That rule disallows the above transformation.
no, I just suggest that a compiler shouldn't do it.
> The only reasonable
> reading of that rule is to prevent compiler transformations like the
> one above.
to discourage, not to prevent
Jens
|
|
0
|
|
|
|
Reply
|
Jens.Gustedt1 (231)
|
7/8/2012 7:40:53 AM
|
|
On 07/08/2012 03:40 AM, Jens Gustedt wrote:
> Am 08.07.2012 01:19, schrieb Joshua Maurice:
....
>> n1570: 7.17.3 Order and consistency / 16
>> [quote]
>> Implementations should make atomic stores visible to atomic loads
>> within a reasonable amount of time.
>> [/quote]
>
> The "should" makes it clearly a non-normative part of the standard and
> leaves it to the appreciation of the compiler implementor what
> "reasonable amount of time" is.
While the "should" is a strong hint, it's the fact that it's in a
"recommended practice" section which makes it perfectly clear that this
is optional.
--
James Kuyper
|
|
0
|
|
|
|
Reply
|
jameskuyper (5140)
|
7/8/2012 2:51:30 PM
|
|
Joshua Maurice wrote:
> There's also one other new obscure rule, again copied from C++11 to
> C11, which has some relevance here.
>
> n1570: 6.8.5 Iteration statements / 6
> [quote]
> An iteration statement whose controlling expression is not a constant
> expression, that performs no input/output operations, does not access
> volatile objects, and performs no synchronization or atomic operations
> in its body, controlling expression, or (in the case of a for
> statement) its expression-3, may be assumed by the implementation to
> terminate.
> [/quote]
>
> This generated a lot of discussion on the C++ newsgroups. In short,
> this says:
> > for(;;);
> is now undefined behavior.
The controlling expression of that loop is a constant expression--
footnote 156 makes this clear--so it doesn't meet the prerequisites for
paragraph 6.8.5p6 to apply.
Furthermore, even if the paragraph did apply, it doesn't make the
behavior undefined, it just allows the compiler to remove the loop if it
doesn't detect any side effects.
|
|
0
|
|
|
|
Reply
|
prl1 (101)
|
7/14/2012 6:25:27 AM
|
|
On Jul 13, 11:25=A0pm, Philip Lantz <p...@canterey.us> wrote:
> Joshua Maurice wrote:
> > There's also one other new obscure rule, again copied from C++11 to
> > C11, which has some relevance here.
>
> > n1570: 6.8.5 Iteration statements / 6
> > [quote]
> > An iteration statement whose controlling expression is not a constant
> > expression, that performs no input/output operations, does not access
> > volatile objects, and performs no synchronization or atomic operations
> > in its body, controlling expression, or (in the case of a for
> > statement) its expression-3, may be assumed by the implementation to
> > terminate.
> > [/quote]
>
> > This generated a lot of discussion on the C++ newsgroups. In short,
> > this says:
> > > for(;;);
> > is now undefined behavior.
>
> The controlling expression of that loop is a constant expression--
> footnote 156 makes this clear--so it doesn't meet the prerequisites for
> paragraph 6.8.5p6 to apply.
Oh damn. My apologies. I stand corrected in the entirety.
> Furthermore, even if the paragraph did apply, it doesn't make the
> behavior undefined, it just allows the compiler to remove the loop if it
> doesn't detect any side effects.
I will have to disagree here. This does not allow the compiler to
remove the loop. It allows the compiler to assume the loop will
terminate. Those are very very different. The way it was described to
me, IIRC, it allows the compiler to change this:
for ( ; /* some non-const expr*/ ; )
{
/* some body that fulfills the requirements */
}
x =3D 1;
into this:
x =3D 1;
for ( ; /* some non-const expr*/ ; )
{
/* some body that fulfills the requirements */
}
The compiler still has to prove that the body does not access x, but
if it can, then it can move that above the loop. I forget what all of
the heated discussion was about, as I cannot see offhand why this is
bad at all. Looking back on this, I don't see why the compiler cannot
already do this absent the rule. The only way another thread could
notice is if it had a formal race condition, and that's undefined
behavior anyway. Maybe I'll go take another look at those discussions
if I can find them, as apparently I am very confused.
So, nevermind. And thanks for the correction!
|
|
0
|
|
|
|
Reply
|
joshuamaurice (575)
|
7/14/2012 9:37:06 PM
|
|
On Jul 14, 2:37=A0pm, Joshua Maurice <joshuamaur...@gmail.com> wrote:
> On Jul 13, 11:25=A0pm, Philip Lantz <p...@canterey.us> wrote:
>
> > Joshua Maurice wrote:
> > > There's also one other new obscure rule, again copied from C++11 to
> > > C11, which has some relevance here.
>
> > > n1570: 6.8.5 Iteration statements / 6
> > > [quote]
> > > An iteration statement whose controlling expression is not a constant
> > > expression, that performs no input/output operations, does not access
> > > volatile objects, and performs no synchronization or atomic operation=
s
> > > in its body, controlling expression, or (in the case of a for
> > > statement) its expression-3, may be assumed by the implementation to
> > > terminate.
> > > [/quote]
>
> > > This generated a lot of discussion on the C++ newsgroups. In short,
> > > this says:
> > > > for(;;);
> > > is now undefined behavior.
>
> > The controlling expression of that loop is a constant expression--
> > footnote 156 makes this clear--so it doesn't meet the prerequisites for
> > paragraph 6.8.5p6 to apply.
>
> Oh damn. My apologies. I stand corrected in the entirety.
>
> > Furthermore, even if the paragraph did apply, it doesn't make the
> > behavior undefined, it just allows the compiler to remove the loop if i=
t
> > doesn't detect any side effects.
>
> I will have to disagree here. This does not allow the compiler to
> remove the loop. It allows the compiler to assume the loop will
> terminate. Those are very very different. The way it was described to
> me, IIRC, it allows the compiler to change this:
> =A0 for ( ; /* some non-const expr*/ ; )
> =A0 {
> =A0 =A0 /* some body that fulfills the requirements */
> =A0 }
> =A0 x =3D 1;
> into this:
> =A0 x =3D 1;
> =A0 for ( ; /* some non-const expr*/ ; )
> =A0 {
> =A0 =A0 /* some body that fulfills the requirements */
> =A0 }
>
> The compiler still has to prove that the body does not access x, but
> if it can, then it can move that above the loop. I forget what all of
> the heated discussion was about, as I cannot see offhand why this is
> bad at all. Looking back on this, I don't see why the compiler cannot
> already do this absent the rule. The only way another thread could
> notice is if it had a formal race condition, and that's undefined
> behavior anyway. Maybe I'll go take another look at those discussions
> if I can find them, as apparently I am very confused.
>
> So, nevermind. And thanks for the correction!
Well, shoot. Just as I hit send, I recall one of the potentially
surprising results.
#include <stdlib.h>
int main(void)
{
unsigned int x =3D 1;
for (; x !=3D 0; x +=3D 2)
;
exit(0);
}
As I understand it, without that rule I would expect this program to
have the "observable behavior" of not-halting and no (other)
observable behavior. With this rule, it's still an allowed
"execution", but another allowed execution is for the program is to
halt and have exit value 0.
So, again you are right that it's entirely unrelated to my earlier
point, but I don't think it's quite right to say "the compiler can
remove the loop" without going into some more of the details. Also,
you are right that it's not "formally undefined behavior", but it's
definitely quite weird to change the obvious semantics of a well
defined program so drastically. Worse, both executions are allowed,
and which you get depends on a coin flip by the compiler /
implementation on each execution. (Of course, QoI would prevent coin
flipping, but nothing else would.)
|
|
0
|
|
|
|
Reply
|
joshuamaurice (575)
|
7/14/2012 9:46:00 PM
|
|
Joshua Maurice wrote:
> So, again you are right that it's entirely unrelated to my earlier
> point, but I don't think it's quite right to say "the compiler can
> remove the loop" without going into some more of the details. Also,
> you are right that it's not "formally undefined behavior", but it's
> definitely quite weird to change the obvious semantics of a well
> defined program so drastically. Worse, both executions are allowed,
> and which you get depends on a coin flip by the compiler /
> implementation on each execution. (Of course, QoI would prevent coin
> flipping, but nothing else would.)
I agree, that was an oversimplification on my part. If I recall
correctly (but I'm not at all sure that I do), when I first encountered
the proposal, I think it didn't include the bit about a non-constant
control. Without that requirement for the rule to apply, I think this
would have been a very bad rule. With that requirement, I think it
simply means that if you want a loop to run forever, you need to make
that clear. I'm not sure that's a bad thing. Like you, I remember some
heated discussions of this, so I've probably forgotten something.
|
|
0
|
|
|
|
Reply
|
prl1 (101)
|
7/15/2012 4:39:48 AM
|
|
Uncle Steve wrote:
> Currently I'm developing on x86_64 and x86, and I chose to
> use "lock incl %0" and then inspect the result to detect collisions.
> It is obviously very fast, and I can control how long a thread will
> spin on the lock before sleeping. I have yet to investigate whether I
> need to use a different instruction for the 64-bit arch.
You may not want to use inc, because then you can't discover the result
without reading it again, so you lose atomicity. I recommend using xadd
to add 1, it atomically returns the previous value in a register.
|
|
0
|
|
|
|
Reply
|
prl1 (101)
|
7/15/2012 4:46:59 AM
|
|
|
46 Replies
37 Views
(page loaded in 0.46 seconds)
|