Thread: Test C code: function call order

Hybrid View

Previous Post Previous Post   Next Post Next Post
  1. #1
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    5,108
    However, if g, for example, modifies an atomic variable, then that's not allowed, because the access to the atomic guarantees that other threads see the write to result_of_f as happening before a read on the same atomic, and it guarantees that other thread see the write of result_of_h as happening after that read, (and therefore they cannot modify result_of_h after the read without a race condition -- the atomic write in g() can funtion like an aquire of the shared resource result_of_h).
    An atomic write to an unrelated variable (let's call it `gatom') in `g' does not in any way guarantee that the write to `result_of_f' happens before a read from `result_of_f'.

    An atomic write to `gatom' does not in any way guarantee that a write to `result_of_f' happens before a read from `gatom'.

    An atomic read from `gatom' does not guarantee that a read from `result_of_h' happens after the read from `gatom'.

    An atomic read from `result_of_f' does not guarantee that a read from `result_of_h' happens after the read from `result_of_f'.

    An atomic variable only guarantees that reads and writes are completed in isolation. They do not impose any mutual or identity ordering. If one thread (`i') increments a variable (`v1') while another thread (`d') decrements a variable (`v2') the sole use of atomic reads and writes to the variables (`v1' and `v2') is insufficient to enforce a `v1++'->`v2--' pattern; it is entirely possible to get `v1++'->`v1++'->`v2--' as an ordering. This lack of any mutual or identity ordering holds for single threaded applications as well as applications with multiple threads.

    So, we wind up back at the compiler and sequence points. If the compiler doesn't recognize a sequence point, and so reorders, the use of atomic doesn't buy you correct ordering. Any given compiler may recognize any specific atomic operation as a sequence point, but that is not something enforced by the standards. (Well, I do not know C11 at all so it may be something in that standard.) If a compiler does recognize atomic reads and writes and necessary sequence points the compiler doesn't reorder so you get the canonical ordering.

    so you are saying if 'g()' modifies an atomic variable, then the compiler cannot reorder the sequence of calls?
    It has nothing to do with atomic variables. This is about sequence points. (C11 may specifically have mentioned atomic variables as sequence points.) According to the standard a compiler can not reorder any sequence point that matches certain criteria. This criteria includes calling standard "IO" functions (like `printf'), any operation with a `volatile' variable, mutating (writing) any object, and something to do with global variables that I can't really recall because almost every compiler ignores that bit.

    Of course, there is what the standard says and then there is what compilers do.

    how does the compiler know what f(),g() and h() are doing if they are in a separate compilation unit?
    Many compiler, by default, assume that functions beyond inspection always constitute necessary sequence points. This isn't mandated by the standard, but it is a practical approach.

    Some compiler, at higher optimization levels, can break code for any number of reasons; reordering necessary sequence points is one possible break.

    Soma

  2. #2
    Registered User
    Join Date
    Apr 2006
    Posts
    2,149
    Quote Originally Posted by phantomotap View Post
    An atomic write to an unrelated variable (let's call it `gatom') in `g' does not in any way guarantee that the write to `result_of_f' happens before a read from `result_of_f'.

    An atomic write to `gatom' does not in any way guarantee that a write to `result_of_f' happens before a read from `gatom'.

    An atomic read from `gatom' does not guarantee that a read from `result_of_h' happens after the read from `gatom'.

    An atomic read from `result_of_f' does not guarantee that a read from `result_of_h' happens after the read from `result_of_f'.

    An atomic variable only guarantees that reads and writes are completed in isolation. They do not impose any mutual or identity ordering. If one thread (`i') increments a variable (`v1') while another thread (`d') decrements a variable (`v2') the sole use of atomic reads and writes to the variables (`v1' and `v2') is insufficient to enforce a `v1++'->`v2--' pattern; it is entirely possible to get `v1++'->`v1++'->`v2--' as an ordering. This lack of any mutual or identity ordering holds for single threaded applications as well as applications with multiple threads.
    Atomic do impose an odering.

    Quote Originally Posted by C committee draft from April 12, 2012
    Certain library calls synchronize with other library calls performed by another thread. In
    particular, an atomic operation A that performs a release operation on an object M
    synchronizes with an atomic operation B that performs an acquire operation on M and
    reads a value written by any side effect in the release sequence headed by A.

    An evaluation A inter-thread happens before an evaluation B if A synchronizes with B, A
    is dependency-ordered before B, or, for some evaluation X:
    — A synchronizes with X and X is sequenced before B,
    — A is sequenced before X and X inter-thread happens before B, or
    — A inter-thread happens before X and X inter-thread happens before B.
    f() is sequenced before g(). If g() preforms an release on an atomic 'gatom', then it synchronizes with a future thread that does an aquire on 'gatom'. Then in that thread, anything sequenced after that aquire can assume that result_of_f has been properly set. It cannot read from result_of_f prior to the aquire on 'gatom', because that would be a data race, and therefore undefined.

    Simmilarly in the other direction. The aquire of 'gatom' implies that everything sequenced before the previous release of gatom in another thread. That includes any writes to result_of_h. As a result, those writes to result_of_h does not constitute a data race with that prior write of result_of_h.

    This two way release and acquire also ensures a thread that acquires 'gatom' after this thread knows that it cannot write to result_of_h, because it is not inter thread happends before the write in this thread to result_of_h, and no other inter thread sequencing exists.


    So, we wind up back at the compiler and sequence points. If the compiler doesn't recognize a sequence point, and so reorders, the use of atomic doesn't buy you correct ordering. Any given compiler may recognize any specific atomic operation as a sequence point, but that is not something enforced by the standards. (Well, I do not know C11 at all so it may be something in that standard.) If a compiler does recognize atomic reads and writes and necessary sequence points the compiler doesn't reorder so you get the canonical ordering.
    It's not a matter of recognising sequence points or not. C simply does not define the order in which operations are done, except where there is a dependency or IO.
    It is too clear and so it is hard to see.
    A dunce once searched for fire with a lighted lantern.
    Had he known what fire was,
    He could have cooked his rice much sooner.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Call managed code (c#) from unmanaged code (VC++ 6.0)
    By playxn in forum C++ Programming
    Replies: 3
    Last Post: 10-14-2008, 12:11 PM
  2. Replies: 14
    Last Post: 04-01-2008, 02:23 AM
  3. How do you order your game code?
    By Queatrix in forum A Brief History of Cprogramming.com
    Replies: 15
    Last Post: 02-05-2006, 06:26 PM
  4. using a driver function to test my code??
    By tommy69 in forum C Programming
    Replies: 24
    Last Post: 03-20-2004, 07:12 PM
  5. order of multiple test expression?
    By wolf in forum C Programming
    Replies: 5
    Last Post: 02-05-2002, 04:33 PM

Tags for this Thread