Thread: Defining "thread-safety" and maybe something that isn't "thread-safety"

  1. #1
    Kung Fu Kitty Angus's Avatar
    Join Date
    Oct 2008
    Location
    Montreal, Canada
    Posts
    115

    Defining "thread-safety" and maybe something that isn't "thread-safety"

    Every time I try to lookup the definition of "thread-safe" I get told that a routine is thread-safe if it can be called by more than 1 thread at the same time. (They also seem to be very Java-oriented, in which this definition might be sufficient) But this seams to mean if that if a function is not thread-safe, then it's ok if that function only gets called by one thread. Or does it?

    Suppose foo() is declared not thread-safe because it calls the POSIX function asctime(), which returns a pointer to static space, rather than its reentrant counterpart, asctime_r(). One might think that it's ok to have multiple threads in one's program, as long as foo() calls are confined to just 1 thread, but suppose another thread calls asctime()? It seems to me that if something is not thread-safe then that kills your freedom to use any other code that isn't thread-safe either.

    I don't know if I'm using "thread-safe" here properly, but I've definitely hit upon a problem of concurrency that deserves to have a name. So what is it?

  2. #2
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,656
    Calling something which is not thread safe is OK, so long as you guarantee that it can only be called by 1 thread at once.

    You can do this in two ways
    - only call it from one thread
    - create an access function which wraps a mutex around the call to the unsafe function.
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  3. #3
    Registered User
    Join Date
    Nov 2004
    Location
    USA
    Posts
    516
    Why would you want to introduce bugs in your code by using something which is not thread-safe in a multithreaded environment?
    Code:
    >+++++++++[<++++++++>-]<.>+++++++[<++++>-]<+.+++++++..+++.[-]>++++++++[<++++>-] <.>+++++++++++[<++++++++>-]<-.--------.+++.------.--------.[-]>++++++++[<++++>- ]<+.[-]++++++++++.

  4. #4
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300
    Quote Originally Posted by Salem View Post
    Calling something which is not thread safe is OK, so long as you guarantee that it can only be called by 1 thread at once.

    You can do this in two ways
    - only call it from one thread
    This would explain why you can do such a thing unscathed when using a threaded API, without programming any threads of your own. Maybe the OP is being paranoid?
    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

  5. #5
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396
    Yes, you can think of "thread safe" as being an awful lot like "const." A const function can only invoke other const functions, and a thread-safe function can only invoke other thread-safe functions.

    A function which is not natively thread-safe can be turned into a thread-safe version by wrapping it in a function which does locking to enforce serial access. But that should be a last resort.

    EDIT: In the instance of asctime(), just wrapping with a lock is not sufficient, because it isn't clear when the pointer to static data becomes stale. You would have to do something else, like wrap with a lock AND make a copy of the result before unlocking the lock, then returning the copy.
    Code:
    //try
    //{
    	if (a) do { f( b); } while(1);
    	else   do { f(!b); } while(1);
    //}

  6. #6
    Kung Fu Kitty Angus's Avatar
    Join Date
    Oct 2008
    Location
    Montreal, Canada
    Posts
    115
    None of these responses directly address the issue.

    Salem just gives the popular definition of "thread-safe" which does not address this issue of concurrency that I'm focusing on.

    PING seems to be saying that using code that isn't thread safe means that threads are out of the question, but doesn't come out and say it.

    MK27: I'm definitely not paranoid, since I've clearly hit on a problem of concurrency.

    I need to know the correct definition of "thread-safe". If that is Salem's definition, then there's this other issue of concurrency that no one seems to address. Go back and read the OP.

  7. #7
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396
    Quote Originally Posted by Angus View Post
    I need to know the correct definition of "thread-safe". If that is Salem's definition, then there's this other issue of concurrency that no one seems to address. Go back and read the OP.
    The definition of thread safe is "safe to call from multiple threads." I'm not sure what else you are looking for. Another term with a similar meaning is "reentrant" which simply means that a region of code can safely be activated multiple times. That's more encompassing than "thread safe" because it also implies that the function is recursion-safe.
    Code:
    //try
    //{
    	if (a) do { f( b); } while(1);
    	else   do { f(!b); } while(1);
    //}

  8. #8
    Kung Fu Kitty Angus's Avatar
    Join Date
    Oct 2008
    Location
    Montreal, Canada
    Posts
    115
    Quote Originally Posted by brewbuck View Post
    The definition of thread safe is "safe to call from multiple threads." I'm not sure what else you are looking for. Another term with a similar meaning is "reentrant" which simply means that a region of code can safely be activated multiple times. That's more encompassing than "thread safe" because it also implies that the function is recursion-safe.
    Ok, so what if we have
    Code:
    char *foo() {
        return asctime();//not a thread-safe function
    }
    char *goo() {
        return ascitime();//still not thread-safe
    }
    
    int main() {
         StartThread(Thread);
         fprintf(file0, "goo(): %s\n", goo());
    ...
    }
    
    void Thread() {
        fprintf(file1, "foo(): %s\n", foo());
    }
    Both foo() and goo() were labeled as not thread-safe, so we were careful not to have one of them called in 2 threads, but both call the same function that isn't thread-safe, so we have a concurrency problem, even though we've respected "thread-safety".

    Another important ramification is if you have 2 3rd party libraries, and both are not thread-safe, you can't trust them in a multi-threaded program, even if you confine all your calls to each library to its own thread.

  9. #9
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,412
    Quote Originally Posted by Angus
    Both foo() and goo() were labeled as not thread-safe, so we were careful not to have one of them called in 2 threads, but both call the same function that isn't thread-safe, so we have a concurrency problem, even though we've respected "thread-safety".
    Mind explaining the concurrency problem? It seems to me there is no such problem since the pointer returned by the first call to asctime() is used and done with before the next call to asctime(), unless I understand the code wrongly.

    EDIT:
    Nevermind, I suspect it is merely due to my lack of familiarity with threads.
    Last edited by laserlight; 05-12-2009 at 01:42 PM.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  10. #10
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396
    Quote Originally Posted by Angus View Post
    Both foo() and goo() were labeled as not thread-safe, so we were careful not to have one of them called in 2 threads, but both call the same function that isn't thread-safe, so we have a concurrency problem, even though we've respected "thread-safety".
    No, it does not respect thread safety. Both foo() and goo() call a non-thread-safe function and are therefore not thread safe by definition. Thread safety does not just include the code which is verbatim in the function in question, but any code called directly or indirectly by that function.

    Another important ramification is if you have 2 3rd party libraries, and both are not thread-safe, you can't trust them in a multi-threaded program, even if you confine all your calls to each library to its own thread.
    Correct. Even a huge lock will not help, because you can't know whether the library is caching some piece of data (for instance the return value of asctime() ) because it assumes it is the only thread of execution. Such libraries are mostly useless in modern programming.

    That said, it doesn't have to be hard to write a thread-safe library although sometimes it can be.
    Code:
    //try
    //{
    	if (a) do { f( b); } while(1);
    	else   do { f(!b); } while(1);
    //}

  11. #11
    Kung Fu Kitty Angus's Avatar
    Join Date
    Oct 2008
    Location
    Montreal, Canada
    Posts
    115
    Quote Originally Posted by brewbuck View Post
    No, it does not respect thread safety. Both foo() and goo() call a non-thread-safe function and are therefore not thread safe by definition. Thread safety does not just include the code which is verbatim in the function in question, but any code called directly or indirectly by that function.
    It does by every definition of thread-safe that's been thrown at me. Foo() cannot be called again until it is finished, and the same criteria applies to goo(). If the definition were extended to imply that nothing else that isn't thread-safe can be trusted to run while foo() and goo() are running, that would be different, but that's not what I've been hearing.


    Quote Originally Posted by brewbuck View Post
    That said, it doesn't have to be hard to write a thread-safe library although sometimes it can be.
    It's not always that simple. This came up because I have these 2 close source libraries that I want to run concurrently. Unfortunately, neither appear to be thread-safe by any definition, so I've had to chuck all my threading code and go for a multi-process solution. It wouldn't make sense to put any reentrant wrapper around the library calls, since that would in effect make execution serial.

  12. #12
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396
    Quote Originally Posted by Angus View Post
    It does by every definition of thread-safe that's been thrown at me. Foo() cannot be called again until it is finished, and the same criteria applies to goo(). If the definition were extended to imply that nothing else that isn't thread-safe can be trusted to run while foo() and goo() are running, that would be different, but that's not what I've been hearing.
    Then you are one of the few people I've run into who has (mis) interpretted the definition in that way. A function is thread safe if it can tolerate multiple, simultaneous invokations. These "foo" and "goo" functions can't.

    It's not always that simple. This came up because I have these 2 close source libraries that I want to run concurrently. Unfortunately, neither appear to be thread-safe by any definition, so I've had to chuck all my threading code and go for a multi-process solution. It wouldn't make sense to put any reentrant wrapper around the library calls, since that would in effect make execution serial.
    Yes, it sucks. Modern general-use libraries should be thread-safe.
    Code:
    //try
    //{
    	if (a) do { f( b); } while(1);
    	else   do { f(!b); } while(1);
    //}

  13. #13
    Kung Fu Kitty Angus's Avatar
    Join Date
    Oct 2008
    Location
    Montreal, Canada
    Posts
    115
    Quote Originally Posted by brewbuck View Post
    Then you are one of the few people I've run into who has (mis) interpretted the definition in that way. A function is thread safe if it can tolerate multiple, simultaneous invokations. These "foo" and "goo" functions can't.
    That's what I've been saying all along, foo() and goo() are not thread-safe. But because neither of them will experience 2 concurrent invocations they are being handled in a thread-safe manner. However, both might be called concurrently, so there's a problem.

  14. #14
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396
    Quote Originally Posted by Angus View Post
    That's what I've been saying all along, foo() and goo() are not thread-safe. But because neither of them will experience 2 concurrent invocations they are being handled in a thread-safe manner. However, both might be called concurrently, so there's a problem.
    Which is why the definition of thread safe is always with respect to a single function. foo and goo are not "mutually unsafe," they are simply unsafe, period.
    Code:
    //try
    //{
    	if (a) do { f( b); } while(1);
    	else   do { f(!b); } while(1);
    //}

  15. #15
    Registered User
    Join Date
    Sep 2007
    Location
    South Africa
    Posts
    20
    From wikipedia:
    "Thread safety is a computer programming concept applicable in the context of multi-threaded programs. A piece of code is thread-safe if it functions correctly during simultaneous execution by multiple threads. In particular, it must satisfy the need for multiple threads to access the same shared data, and the need for a shared piece of data to be accessed by only one thread at any given time."

    The last sentence is key to your question. For a function to be truly thread safe it needs to make sure shared data is accessed by only one thread at a time. Not just function correctly when called simultaneously from different contexts. eg the first condition:

    Code:
    int foo()
    {
    static int count =0;
    count ++;
    
    ...
    //point a
    
    printf("this function has been called %d times\n", count);
    
    ...
    
    //point b
    return count;
    
    }
    foo() is not thread safe. It is called by a low priority thread and gets to point b, then a high priority thread calls it and returns, the result will not be what we expected (not agree with the printout).

    Then the second condition:

    Code:
    int global_int = 0;
    int hoo()
    {
       if (global_int < 5)
       {
          //point a
          if (global_int == 5 )
          {
             printf("shouldnt get here!\n");
          }
          global_int+=5;
       }
    }
    
    goo()
    {
       mutex_lock(global_int_protector);
       global_int++;
       mutex_unlock(global_int_protector);
    }
    woo ()
    {
       mutex_lock(global_int_protector);
       global_int--;
       mutex_unlock(global_int_protector);
    }
    hoo() is not thread safe. Under the right conditions you may see the print out. It doesn't protect the global_int data even from other instances of itself. woo() and goo() are thread safe, however hoo can still cause corruption of global_int and thus cause them to act unexpectedly.

    Have a look at:
    Thread safety - Wikipedia, the free encyclopedia

    this explains it quite well

Popular pages Recent additions subscribe to a feed

Tags for this Thread