A note for C programmers

Discussion of chess software programming and technical issues.

Moderators: hgm, Rebel, chrisw

syzygy
Posts: 5557
Joined: Tue Feb 28, 2012 11:56 pm

Re: A note for C programmers

Post by syzygy »

bob wrote:Sorry, but not allowed. You've been carping about MY using undefined behavior for days now. Isn't this just a tad inconsistent? You use it because it works? You use it in spite of it being called "undefined behavior"?
As I said, I have no illusion that my code will continue to work with future compilers, let alone that it will work on other platforms. Do you not see a difference here?
syzygy
Posts: 5557
Joined: Tue Feb 28, 2012 11:56 pm

Re: A note for C programmers

Post by syzygy »

Henk wrote:I don't know if my operating system Windows 8 uses elements with undefined behavior. I don't feel safe now.
If you have full control over the compiler, it does not matter much whether you follow the standard or not. As long as you know what you're doing.
syzygy
Posts: 5557
Joined: Tue Feb 28, 2012 11:56 pm

Re: A note for C programmers

Post by syzygy »

bob wrote:You need to pull out an intel manual.
Why don't read before you respond:
syzygy wrote:You keep confusing things. What the standard guarantees is one thing. What one would naively expect knowing the architecture of a particular machine is another thing. What a particular compiler actually will do is yet something else.
The C99 standard guarantees nothing about how a 64-bit int is written to memory. On x86-64 it makes sense to use a single store, which will then be atomic provided it does not cross cache lines. But if the only thing you know about a compiler is that it complies with C99, you don't know that it will use single stores and even if it does, you don't know that it will properly align 64-bit ints.
If a compiler does what you suggest, I/O is impossible. How can I write to a buffer, and signal another thread or process to write that out to disk, so that only one process does I/O? Yet the operating system, the libraries, and everyone else depends on that happening. There is a BIG difference between writing to a local variable, and writing to a global (shared) variable. The compiler is NOT free to optimize away stores/writes to global data.
Duh? There is no reason that any particular C library or OS kernel must have been written in C or must have been compiled with the compiler you're using. It may have been written entirely in assembly. Certainly the Linux kernel will NOT properly run when compiled with just any C99 compliant compiler.
syzygy
Posts: 5557
Joined: Tue Feb 28, 2012 11:56 pm

Re: A note for C programmers

Post by syzygy »

bob wrote:Since one of the ongoing quibbles here is about the definition of "undefined behavior", here is the usual definition. One that has been used since the 1950's in fact. "Behavior that can not be predicted." This has ALWAYS been an issue of unexpected results caused, NOT by the compiler intentionally doing something off-the-wall, but by the code the compiler produces, which might not always do the expected thing.
Is it really difficult to understand that the C standard has a very precise definition, which is very different from this one? You can go complain to the drafters of the standard, but it won't change a thing.
syzygy
Posts: 5557
Joined: Tue Feb 28, 2012 11:56 pm

Re: A note for C programmers

Post by syzygy »

rbarreira wrote:
bob wrote:
Rein Halbersma wrote:
bob wrote: Since one of the ongoing quibbles here is about the definition of "undefined behavior", here is the usual definition. One that has been used since the 1950's in fact. "Behavior that can not be predicted." This has ALWAYS been an issue of unexpected results caused, NOT by the compiler intentionally doing something off-the-wall, but by the code the compiler produces, which might not always do the expected thing.
You are the only one quibbling with the Standard's definition of UB. Everyone else is on the same page here. Don't pull in whatever colloquial speech you are using into a precise and exact definition used by every compiler writer.
UB is about the freedom of a compiler to translate your C code to machine instructions.
The standards does not adequately define "undefined behavior" To wit:

3.4.3
1 undefined behavior
behavior, upon use of a nonportable or erroneous program construct or of erroneous data, for which this International Standard imposes no requirements
NOTE Possible undefined behavior ranges from ignoring the situation completely with unpredictable results, to behaving during translation or program execution in a documented manner characteristic of the environment (with or without the issuance of a diagnostic message), to terminating a translation or execution (with the issuance of a diagnostic message).
EXAMPLE An example of undefined behavior is the behavior on integer overflow.

So it can ignore the situation, to behaving in a documented way (both of which make sense) to terminating the compilation with a message. No mention of demons flying out one's nose or anything else.

So how about we get back to planet earth.
On planet earth, you didn't even read properly what you quoted:

"ignoring the situation completely with unpredictable results" OR "behaving during translation or program execution in a documented manner"

So yes, demons flying out of one's nose are a possible case of undefined behavior.
And it's all concisely expressed in imposes no requirements. If demons start flying through your nose, that is compliant with the standard, because you can't violate "no requirements".
Last edited by syzygy on Sat Dec 07, 2013 10:36 pm, edited 1 time in total.
rbarreira
Posts: 900
Joined: Tue Apr 27, 2010 3:48 pm

Re: A note for C programmers

Post by rbarreira »

syzygy wrote:
rbarreira wrote:
bob wrote:
Rein Halbersma wrote:
bob wrote: Since one of the ongoing quibbles here is about the definition of "undefined behavior", here is the usual definition. One that has been used since the 1950's in fact. "Behavior that can not be predicted." This has ALWAYS been an issue of unexpected results caused, NOT by the compiler intentionally doing something off-the-wall, but by the code the compiler produces, which might not always do the expected thing.
You are the only one quibbling with the Standard's definition of UB. Everyone else is on the same page here. Don't pull in whatever colloquial speech you are using into a precise and exact definition used by every compiler writer.
UB is about the freedom of a compiler to translate your C code to machine instructions.
The standards does not adequately define "undefined behavior" To wit:

3.4.3
1 undefined behavior
behavior, upon use of a nonportable or erroneous program construct or of erroneous data, for which this International Standard imposes no requirements
NOTE Possible undefined behavior ranges from ignoring the situation completely with unpredictable results, to behaving during translation or program execution in a documented manner characteristic of the environment (with or without the issuance of a diagnostic message), to terminating a translation or execution (with the issuance of a diagnostic message).
EXAMPLE An example of undefined behavior is the behavior on integer overflow.

So it can ignore the situation, to behaving in a documented way (both of which make sense) to terminating the compilation with a message. No mention of demons flying out one's nose or anything else.

So how about we get back to planet earth.
On planet earth, you didn't even read properly what you quoted:

"ignoring the situation completely with unpredictable results" OR "behaving during translation or program execution in a documented manner"

So yes, demons flying out of one's nose are a possible case of undefined behavior.
And it's all concisely expressed in imposes no requirements. If demons start flying through your nose, that is compliant with the stanard, because you can't violate "no requirements".
Simple logic is apparently lost on Robert. Either that or he's trolling us.
syzygy
Posts: 5557
Joined: Tue Feb 28, 2012 11:56 pm

Re: A note for C programmers

Post by syzygy »

wgarvin wrote:I have an alternative explanation for the last few days of this thread that makes it suddenly make sense to me!

Bob doesn't really believe the things he's saying about undefined behavior, but he has figured out that we get indignant and he's having too much fun trolling us to stop! :lol:
Maybe something triggered undefined behavior in Bob?
Sven
Posts: 4052
Joined: Thu May 15, 2008 9:57 pm
Location: Berlin, Germany
Full name: Sven Schüle

Re: A note for C programmers

Post by Sven »

syzygy wrote:
wgarvin wrote:I have an alternative explanation for the last few days of this thread that makes it suddenly make sense to me!

Bob doesn't really believe the things he's saying about undefined behavior, but he has figured out that we get indignant and he's having too much fun trolling us to stop! :lol:
Maybe something triggered undefined behavior in Bob?
You did a strcpy(s, s+40) where s points to knowledge from 1973.
syzygy
Posts: 5557
Joined: Tue Feb 28, 2012 11:56 pm

Re: A note for C programmers

Post by syzygy »

How to miscompile programs with "benign" data races
The example in 2.1 reads as if it was taken from my code...

Code: Select all

  if (!init_flag) {
    lock();
    if (!init_flag) {
      my_data = ...;
      init_flag = true;
    }
    unlock();
  }
  tmp = my_data;
What I wrote:

Code: Select all

  if (!ptr->ready) {
    LOCK(TB_mutex);
    if (!ptr->ready) {
      char str[16];
      prt_str(pos, str, ptr->key != key);
      if (!init_table_wdl(ptr, str)) {
        ptr2[i].key = 0ULL;
        *success = 0;
        UNLOCK(TB_mutex);
        return 0;
      }
      ptr->ready = 1;
    }
    UNLOCK(TB_mutex);
  }
The problem is that the compiler might reorder statements and execute "init_flag = true" / "ptr->ready = 1" before initialisation has finished. For this to happen in my code, init_table_wdl() would have to be inlined, but nothing is stopping the compiler from doing just that. The if() statement complicates things only a little bit.

What I need to do is insert a memory barrier just before "ptr->ready = 1":

Code: Select all

__asm__ __volatile__ ("" ::: "memory");
This ensures that "ptr->ready = 1" is executed after the initialisation has finished.

Alternatively, I could always take the TB_mutex lock, but that could kill performance. Introducing a "ptr->lock" would be only slightly better.

So I'm really not writing standard C here. I am using gcc features to write my own synchronisation primitives.

(Note that even if the compiler does not reorder things, the processor might. This cannot happen on x86 though, as x86 guarantees that memory writes are observed by other processors / cores in the order they were made.)
Rein Halbersma
Posts: 741
Joined: Tue May 22, 2007 11:13 am

Re: A note for C programmers

Post by Rein Halbersma »

https://www.usenix.org/legacy/event/hot ... /Boehm.pdf

Great paper!
Although data races essentially have no defined semantics
in C or C++, they are perfectly meaningful in
x86 assembly code [17]. An assembly program with a
data race is not necessarily an error. In fact synchronization
primitives are commonly implemented with assembly
code that has data races.
However, we argue here that such a distinction is not
useful if the data race existed in a C or C++ which
was then compiled, and the code was intended to be
portable.3 Not only is the original source code technically
incorrect in such cases, future recompilation of the
code by reasonable compilers may realistically introduce
bugs.