Thread: Is this simple code thread safe?

  1. #1
    Registered User
    Join Date
    Apr 2007
    Posts
    284

    Is this simple code thread safe?

    Here is the code in that 2 threads write to a shared vector (to different items in the vector).
    I know std::vector is not thread safe, but the 2 threads are writing to different items. I am not sure how vector is implemented. Could there be any potential race condition?

    Code:
    void* foo0(void* vec)
    {
      vector<int>* arr = (vector<int>*)vec ;
      int i = (int)(rand()*10);
      arr->at(0) = i;
    }
    
    void* foo1(void* vec)
    {
      vector<int>* arr = (vector<int>*)vec ;
      int i = (int)(rand()*10);
      arr->at(1) = i;
    }
    
    int main()
    {
      vector<int> vec;
      vec.push_back(0);
      vec.push_back(0);
    
      pthread_t t1, t2;
      pthread_create(&t1, NULL, foo1, (void*)&vec);
      pthread_create(&t1, NULL, foo2, (void*)&vec);
    
      pthread_join(t1, NULL);
      pthread_join(t2, NULL);
    
      return 0;
    }

  2. #2
    Hurry Slowly vart's Avatar
    Join Date
    Oct 2006
    Location
    Rishon LeZion, Israel
    Posts
    6,788
    And Why not to pass pointer to int instead?

    (void*)&vec[0] and (void*)&vec[1]

    It should be fine like now as well - but it too complicates the access to the member in the thread function, you may even have same function for both threads...
    All problems in computer science can be solved by another level of indirection,
    except for the problem of too many layers of indirection.
    – David J. Wheeler

  3. #3
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396
    Quote Originally Posted by meili100 View Post
    Here is the code in that 2 threads write to a shared vector (to different items in the vector).
    I know std::vector is not thread safe, but the 2 threads are writing to different items. I am not sure how vector is implemented. Could there be any potential race condition?
    Accesses to any type except char and sig_atomic_t (if present) are not guaranteed to be atomic. That means that if both threads choose to write to the same location in the vector simultaneously, they may corrupt each other's writes.

    On an architecture which supports fundamental multi-byte access to memory (i.e., most architectures) this problem will not happen in practice. But if you were on, for instance, an 8-bit architecture where the compiler implements 16-bit types for convenience, stores to a 16-bit variable might be implemented as multiple memory stores, so in theory, one thread could interrupt the other in mid-store, corrupting the data.

    Incidentally the problem has nothing to do with std::vector<int> but is an underlying issue of the language.

  4. #4
    Registered User
    Join Date
    Apr 2007
    Posts
    284
    Thanks. I agree with you. But it indeed doesn't matter whether it passes an int or a vector. The question is: Is this way thread safe? One of my friends told me this is thread safe, there is no need to use a lock to protect the accessing of the shared vector.

    Quote Originally Posted by vart View Post
    And Why not to pass pointer to int instead?

    (void*)&vec[0] and (void*)&vec[1]

    It should be fine like now as well - but it too complicates the access to the member in the thread function, you may even have same function for both threads...
    Last edited by meili100; 03-11-2008 at 11:47 PM.

  5. #5
    Registered User
    Join Date
    Apr 2007
    Posts
    284
    Thanks, but I don't quite get you. The 2 items in the vector do not share any bytes in memory, regardless of the architecture, don't they?

    Quote Originally Posted by brewbuck View Post
    Accesses to any type except char and sig_atomic_t (if present) are not guaranteed to be atomic. That means that if both threads choose to write to the same location in the vector simultaneously, they may corrupt each other's writes.

    On an architecture which supports fundamental multi-byte access to memory (i.e., most architectures) this problem will not happen in practice. But if you were on, for instance, an 8-bit architecture where the compiler implements 16-bit types for convenience, stores to a 16-bit variable might be implemented as multiple memory stores, so in theory, one thread could interrupt the other in mid-store, corrupting the data.

    Incidentally the problem has nothing to do with std::vector<int> but is an underlying issue of the language.

  6. #6
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    To be safe you should either mutex access to the vector or protect it with a critical section.

  7. #7
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396
    Quote Originally Posted by meili100 View Post
    Thanks, but I don't quite get you. The 2 items in the vector do not share any bytes in memory, regardless of the architecture, don't they?
    Your threads are accessing the vector at random. Why do you think they couldn't randomly access the same item at the same time?

  8. #8
    Registered User
    Join Date
    Sep 2004
    Location
    California
    Posts
    3,268
    Your threads are accessing the vector at random. Why do you think they couldn't randomly access the same item at the same time?
    How could they access the same item? One thread is accessing the first index, while the second thread is accessing the second index.

    Whether or not this is thread safe depends on the implementation of the vector. What you are doing is probably safe, but you would need to see the vector code to know for sure. I checked the implementation on VC8 and you are safe with that.

  9. #9
    Hurry Slowly vart's Avatar
    Join Date
    Oct 2006
    Location
    Rishon LeZion, Israel
    Posts
    6,788
    Code:
    void* foo(void* arg)
    {
       int* i = (int*)(arg);
       *i = rand()*10;
    }
    
    int main()
    {
      vector<int> vec;
      vec.push_back(0);
      vec.push_back(0);
    
      pthread_t t1, t2;
      pthread_create(&t1, NULL, foo, (void*)&vec[0]);
      pthread_create(&t1, NULL, foo, (void*)&vec[1]);
    
      pthread_join(t1, NULL);
      pthread_join(t2, NULL);
    
      return 0;
    }
    All problems in computer science can be solved by another level of indirection,
    except for the problem of too many layers of indirection.
    – David J. Wheeler

  10. #10
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    There's currently absolutely no reliable guarantees with regard to standard library thread safety. In practice, the basic POSIX guarantee holds in all implementations: reading is safe, modifying (in the sense of modifying the actual container, not just its elements) must be synchronized.

    However, because the two ints are adjacent, you actually might get in trouble on platforms that do their memory access in 64-bit units only, like the Alpha.
    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

  11. #11
    Hurry Slowly vart's Avatar
    Join Date
    Oct 2006
    Location
    Rishon LeZion, Israel
    Posts
    6,788
    Quote Originally Posted by CornedBee View Post
    There's currently absolutely no reliable guarantees with regard to standard library thread safety. In practice, the basic POSIX guarantee holds in all implementations: reading is safe, modifying (in the sense of modifying the actual container, not just its elements) must be synchronized.

    However, because the two ints are adjacent, you actually might get in trouble on platforms that do their memory access in 64-bit units only, like the Alpha.
    So you want to tell, that if I pass some pointer to the thread, and it starts to modify data using this pointer I cannot be sure that the data will not be corrupted by other thread that tries to access some other data that is by chance adjusted to the data I modify?

    This is some very new idea for me...
    All problems in computer science can be solved by another level of indirection,
    except for the problem of too many layers of indirection.
    – David J. Wheeler

  12. #12
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    Quote Originally Posted by vart View Post
    So you want to tell, that if I pass some pointer to the thread, and it starts to modify data using this pointer I cannot be sure that the data will not be corrupted by other thread that tries to access some other data that is by chance adjusted to the data I modify?

    This is some very new idea for me...
    Yes, that would be POSSIBLE. However, I do believe that even the early Alpha processors, whilst having a 64-bit BUS, would actually perform longword accesses, which is 32-bit [I don't know this for sure, but that is from what I could find in 2 minutes googling].

    It is CERTAINLY true that there are machines that can't use for example 16 bit numbers without doing something along the lines of:
    Code:
    void writeshort(long *ptr, short x)
    {
       short *p;
       p = ptr & ~3;
       if ((int)ptr & 2)  *p = (*p & 0xFFFF0000) | (value & 0xFFFF);
       else *p = (*p 0xFFFF) | (value << 16);
    }
    Obviously, this would be implemented in machine code by the compiler, not as a user-function in C - I wrote it that way to make it eas{y,ier} to understand.

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  13. #13
    Hurry Slowly vart's Avatar
    Join Date
    Oct 2006
    Location
    Rishon LeZion, Israel
    Posts
    6,788
    Obviously, this would be implemented in machine code by the compiler, not as a user-function in C
    Shouldn't it be implemented as atomic operation? Otherwise a lot of multi-threaded code will be broken
    All problems in computer science can be solved by another level of indirection,
    except for the problem of too many layers of indirection.
    – David J. Wheeler

  14. #14
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    Quote Originally Posted by vart View Post
    Shouldn't it be implemented as atomic operation? Otherwise a lot of multi-threaded code will be broken
    That was the entire point of Cornedbee's post: It is not, by itself, an atomic operation (because it consists of separate read and write operations). It would need to be protected by some sort of lock mechanism to make sure that the set of instructions:
    Code:
    // pseudo risc assembler:
        load  p, temp1
        load  x, temp2
        and   temp1, 0xFFFF0000
        and   temp2, 0xFFFF
        or      temp1, temp2
        store p, temp1
    If the memory location pointed to by p changes during this sequence, then undefined behaviour occurs.

    But machines that have this problem with 32-bit values are VERY rare.

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  15. #15
    Hurry Slowly vart's Avatar
    Join Date
    Oct 2006
    Location
    Rishon LeZion, Israel
    Posts
    6,788
    But machines that have this problem with 32-bit values are VERY rare.
    That implies that ANY 32-bit access should be protected on these machines - because I cannot know what lies around my vars, and who will access that memory... This is really strange idea...
    All problems in computer science can be solved by another level of indirection,
    except for the problem of too many layers of indirection.
    – David J. Wheeler

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Weird error in this simple code! C2248!
    By gross100 in forum C++ Programming
    Replies: 2
    Last Post: 12-10-2005, 01:31 AM
  2. Replies: 14
    Last Post: 11-23-2005, 08:53 AM
  3. Obfuscated Code Contest
    By Stack Overflow in forum Contests Board
    Replies: 51
    Last Post: 01-21-2005, 04:17 PM
  4. Simple thread object model (my first post)
    By Codeplug in forum Windows Programming
    Replies: 4
    Last Post: 12-12-2004, 11:34 PM
  5. Simple Code, looking for input.
    By Alien_Freak in forum C Programming
    Replies: 3
    Last Post: 03-03-2002, 11:34 AM