Thread: Double-Check Locking in C++

  1. #16
    Registered User Codeplug's Avatar
    Join Date
    Mar 2003
    Posts
    4,981
    >> Don't yet Codeplug make you paranoid. There is no big debate about volatile or memory reordering ...
    I agree. That '07 thread didn't have the best context for "more info" on hardware reordering in response to post #9.

    I do have this in my bookmarks though: IA Memory Ordering - YouTube

    gg

  2. #17
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396
    Quote Originally Posted by CornedBee View Post
    A release doesn't do anything without an acquire to synchronize with, and the thing about DCL is that the second thread never acquires the mutex, and thus never synchronizes with the release.
    Doesn't this fix it?

    Code:
    if(global_ptr == 0){
       mutex->Lock();
       if(global_ptr == 0)
       {
          tmp = new Object();
          writebarrier();
          global_ptr = tmp;
       }
       mutex->Unlock();
    }
    In other words, it wasn't the second thread's fault, it was the initializing thread's fault for not ensuring the ordering.
    Code:
    //try
    //{
    	if (a) do { f( b); } while(1);
    	else   do { f(!b); } while(1);
    //}

  3. #18
    Registered User Codeplug's Avatar
    Join Date
    Mar 2003
    Posts
    4,981
    See this section from "Linux Kernel Memory Barriers" - LXR linux/Documentation/memory-barriers.txt

    >> Doesn't this fix it?
    Maybe - for some particular architecture.

    gg

  4. #19
    Registered User
    Join Date
    Sep 2008
    Posts
    200
    Quote Originally Posted by Codeplug View Post
    The compiler has no control on hardware reodering that may occur. More info here: MSDN volatile sample
    I see, so... in a threaded application, all variables accessed by more than one thread must be synchronized with mutexes for reads and writes, even if a load/store of the variable itself would be atomic (i.e. take only one instruction)? Not quite sure where I got the impression that you could "skip" such mutexes, but glad I've found out it's not so...

  5. #20
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300
    Quote Originally Posted by JohnGraham View Post
    I see, so... in a threaded application, all variables accessed by more than one thread must be synchronized with mutexes for reads and writes,
    Yes.

    even if a load/store of the variable itself would be atomic (i.e. take only one instruction)?
    Mutexes/locks are implemented using atomic swap and compare, but there is slightly more to that than simply something that in theory should only take one instruction on a particular architecture, because that theory is not guaranteed by a standard. Ie, assuming it is true would be indulging undefined behavior. Also, there is an obvious problem with the theory on multiple cores: it may really only take one instruction to load an int into one core for checking the value, but while it's there, another core may perform one instruction putting a different value back in memory because of a write. And so on.

    So "Atomic compare and swap" is actually a more specific processor instruction that can be used for synchronization. Read:

    Atomicity is confusin me

    But consider post #2 in light of post #5 , and remember, the "atomic operation" discussed is not simply reading an int in the normal way.
    C programming resources:
    GNU C Function and Macro Index -- glibc reference manual
    The C Book -- nice online learner guide
    Current ISO draft standard
    CCAN -- new CPAN like open source library repository
    3 (different) GNU debugger tutorials: #1 -- #2 -- #3
    cpwiki -- our wiki on sourceforge

  6. #21
    Registered User
    Join Date
    Sep 2008
    Posts
    200
    Quote Originally Posted by MK27 View Post
    Also, there is an obvious problem with the theory on multiple cores: it may really only take one instruction to load an int into one core for checking the value, but while it's there, another core may perform one instruction putting a different value back in memory because of a write. And so on.

    So "Atomic compare and swap" is actually a more specific processor instruction that can be used for synchronization.
    Sorry - I was considering variables that were write-only (or used as such) from one thread and read-only (or used as such) from another (i.e. one-way data transfer). Without the CPU reordering/caching memory writes/reads this seems safe, since writing the data is an atomic operation which does not depend on its value and reading the data is also atomic. Points noted, though...

  7. #22
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396
    "This that and the other thing" may be undefined from the perspective of C and C++ but that doesn't mean there's some kind of uncertainty about what happens. Let's not pretend we're idiots.
    Code:
    //try
    //{
    	if (a) do { f( b); } while(1);
    	else   do { f(!b); } while(1);
    //}

  8. #23
    Registered User
    Join Date
    Sep 2008
    Posts
    200
    Quote Originally Posted by brewbuck View Post
    "This that and the other thing" may be undefined from the perspective of C and C++ but that doesn't mean there's some kind of uncertainty about what happens. Let's not pretend we're idiots.
    What are you trying to say? (Honestly.)

  9. #24
    Registered User Codeplug's Avatar
    Join Date
    Mar 2003
    Posts
    4,981
    >> "This that and the other thing" may be undefined from the perspective of C and C++ but that doesn't mean there's some kind of uncertainty about what happens. Let's not pretend we're idiots.
    I don't understand what you mean, or what you're referring to.

    If you are referring to one-sided synchronization, where only 1 thread takes a lock or some other synchronizing instruction, then you have uncertainty. The only way to be certain about that kind of code is to examine the generated machine code for thread-safety/determinism.

    >> ... all variables ... must be synchronized ..., even if a load/store of the variable itself would be atomic ...
    Yes - from the perspective of C++11 and Posix standards. Here's another thread on load/store atomicity and synchronization: Quick thread safety question - CodeGuru Forums

    gg

  10. #25
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396
    Quote Originally Posted by JohnGraham View Post
    What are you trying to say? (Honestly.)
    It's a pet peeve of mine when people confuse "undefined in the C++ standard" to mean "nobody has a frickin' clue what's going on." I'm not saying that's what's happening in this thread, just don't want to see it turn into that.
    Code:
    //try
    //{
    	if (a) do { f( b); } while(1);
    	else   do { f(!b); } while(1);
    //}

  11. #26
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    Quote Originally Posted by brewbuck View Post
    It's a pet peeve of mine when people confuse "undefined in the C++ standard" to mean "nobody has a frickin' clue what's going on."
    But it's true for many, if not most, kinds of undefined behavior. Optimizers know about undefined behavior too, and may make assumptions based on that. C++ says that the moment something undefined is executed in the abstract machine, the entire program becomes undefined. This means that if you're on a code path leading to undefined behavior, the optimizer can mess with that code path all it wants. In particular, it can cut it out, and use the branch condition that led there to make further assumptions about the values of variables.

    LLVM Project Blog: What Every C Programmer Should Know About Undefined Behavior #1/3

    That's not to say that magic things happen around undefined behavior, but it means that even "completely obvious" things, like crashing on a null pointer access, won't necessarily happen. Take this (obviously wrong and stupid) function for example:
    Code:
    int foo(int i) {
      int* p = 0;
      if (i != 5) *p = i;
      return i;
    }
    Common knowledge says that a null pointer access is a crash, so the behavior of this function is "return 5 if 5 is passed, crash otherwise", right? Well, no. Because the optimizer sees the undefined behavior, it can just decide that i can never be anything but 5 and turn that function into
    Code:
    int foo(int) { return 5; }
    and furthermore, if it inlines the function, further propagate the knowledge that the parameter "must" be 5 to whatever was passed.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Double check
    By calc in forum C Programming
    Replies: 2
    Last Post: 07-11-2009, 03:25 AM
  2. need someone to double check this for me (c)..
    By flamehead144 in forum C Programming
    Replies: 2
    Last Post: 02-16-2009, 12:19 PM
  3. Double-Checked Locking pattern issue
    By George2 in forum C++ Programming
    Replies: 3
    Last Post: 01-02-2008, 04:29 AM
  4. can someone double check and maybe improve my code
    By tommy69 in forum C Programming
    Replies: 23
    Last Post: 04-21-2004, 02:04 PM
  5. Double check on Java code.
    By sean in forum A Brief History of Cprogramming.com
    Replies: 0
    Last Post: 07-05-2002, 09:55 AM

Tags for this Thread