Shared Memory thread synchronization problem

Discussion in 'Mac Programming' started by dude5552, Feb 6, 2013.

  1. macrumors newbie

    I have 2 global variables var1 and var2. I init them to 0 at the beginning. Then i start two threads t0 and t1 and to synchronize their execution on t0 i am incrementing var2 and waiting for var1 to become 10 and on t1 i am incrementing var1 and waiting for var2 to become 10 .. Since both t0 and t1 are incrementing var1 and var2 while waiting for the other to become 10 ...
    What i have noticed is that for t0 var1 is always 0 and for t1 var2 is always 0 so they wait on each other forever...
    Does anyone have an idea why?
    I start both threads t0 and t1 at the same time...t0 is running on core 0 and t1 is runnning on core 1 in a multiprocessor setup..
    I tried to print values var1 and var2 at different intervals .. i always find t0 sees var1 =0 and t1 finds var2=0...

  2. macrumors 603

  3. macrumors G5


    Multithreading is difficult, and so is reading other people's minds. I am quite good at the former, but really bad at the latter, so I have no idea what you are doing wrong. You seem to be bad at multithreading and good at reading minds, or you would have written something that actually could tell us what you are doing and what you are doing wrong.
  4. Moderator


    Staff Member

    How are your threads sharing these variables? From your description is sounds like they aren't. Even if they are how are you guaranteeing atomicity of operations?
  5. macrumors regular

    I think we ought to wait for the OP answering what code he has written.

    Basically, it is about a concept often called thread-safe. It has several part, but the OP got bitten really early on. Good thing, because these errors can be among the really difficult ones to iron out. The system simply behaves in unpredictable ways.

    Assume we have a variable var1 defined as:
    int var1;

    And then add two threads t0 and t1 reading and writing to that variable.

    One of the thread might have this code snippet.

    while ( true)
    if (var1 > 10)
    .... do something
    ... do some other things

    The compiler "knows" that inside the while loop, no-one modifies var1, so it can temporarily hold the value inside a register. There is no guarantee that if another thread modifes var1, that this thread will notice it.

    In c the way to tell the compiler to at least consider this situation is by adding a little something to the definition:

    volatile int var1;

    the little word volatile is an order to the compiler that some "other hardware" might modify the variable and that seemingly meanigless tests should not be optimized out. This is not really supposed to be used for thread synchronization, often it will simply make things work. (That is, until you end up on a compiler / hardware where this is not true). One reason why it might not work is that any operation, say


    is not guaranteed to be atomic. With atomic operations we mean operatins that are done to completion without beeing interrupted in the middle. On many architectures var1++ happens to be atomic for ints, but you actually have to check your compiler and hardware.

    It cold happen that if two threads does var1++ at the same time the value is not incremented twice, but could instead be totally trashed. Most often it will actually work, but once in a time it might only increment once. And in very special circumstances, it might instead end up having some other value.

    In objective-c, there is the volatile keyword, but again it is not thread-safe, in that two threads updating the same variable will not be guaranteed a result.

    The solution instead, is given in the first reply where there are references to the atomic, threadsafe primitives that you should use.

    // Gunnar
  6. macrumors G5


    Not going to happen. Both the waiting and the answering :D

    I wouldn't be surprised if he did this:

    void f (void)
       int x1 = 0, x2 = 0;
       while (x2 != 10) ++x1;
    void g (void)
       int x1 = 0, x2 = 0;
       while (x1 != 10) ++x2;
  7. macrumors newbie

    Before i wrote in x86 64 bit assembly...
    ; assume the memory pointed to by r10 is in allocated space
    ; for t0 thread executed by core 0

    inc DWORD [r10]
    cmp DWORD [r11], 10
    jle T0BARRIER0
    mov r13, [r12]
    cmp DWORD [r10], 10
    jle T0BARRIER0

    ; move further

    ; some other things

    ; For t1 thread executed by core 1

    inc DWORD [r11]
    cmp DWORD [r10], 10
    jle T1BARRIER0
    cmp DWORD [r11], 10
    jle T1BARRIER0

    ; move on after sychronize
    ; what i found is that memory location pointed to by DWORD [r10] is always
    ; 0 for t1 so it gets stuck and DWORD[r11] is 0 for t0
    ; why would sychronization be a problem since register are per core
    ; one core is not using other cores register they are only looking at a memory
    ; location to go > 10 which is incremented by other core
    ; i have made sure that each core hits these loops...
  8. Moderator


    Staff Member

    Why :confused:

    Some points:

    1) Post full code that can actually be compiled and tested
    2) Use the code tags :)
    3) You're "variables" seem to be registers. These are core local...
  9. macrumors 68040

    When ever two threads use the same variable you need to use a locking mechanism or the result is going to be unpredictable, in a case where you use a threading library such as pthreads there are mutex locks for this.
  10. macrumors newbie

    Because this code is sitting in the middle of a whole bunch of code...

    Because of the nature of the code i have to stick to assembly....


    I am using fork to create threads...
    Is there a problem with that ... I though underneath it all it is Unix BSD related code right....


    Besidges I am not sharing variables... r10 and r11 belong to individual CPU registers either core 0 or core 1 respectively... they are only pointing to locations which are shared....
  11. macrumors 68040

    If you use fork() then they are not threads, but processes. Threads share address space, processes don't. In regards to your second paragraph, a shared resource is probably a better term. You need to make sure that each process handles the resource in one atomic operation, hence the lock. Doing this in assembly however, means that you are on your own as far as I'm concerned lol.

    If you are interested, this is how gcc implements a mutex lock:
  12. macrumors 6502

    Fork does not create threads. It creates separate processes which receive a copy of memory from before the fork but is separate after that, so changes made in one process will not be seen in the other.
  13. macrumors newbie

    Right ...
    Now assume that i allocated a big chunk of memory g_mem1
    then i do a fork ()
    now i make register r10 to point to g_mem1 + 32
    r11 g_mem1 + 64

    this for core 0
    then it sits in T0BARRIER0

    similarly i do another fork ()
    now i make register r10 to point to g_mem1 + 32
    r11 g_mem1 + 64

    now goes and sits in T1BARRIER0

    At this point r10 and r11 are being incremented ....
    at some point both should be > 10 right...

    However it doesnt ... It sits in infinite loop


    Here is the fun :)

    I can use mutex lock however since it is a OS facility, an OS code which i dont have much control over, i cannot maintain concurrency.
    I cannot guarantee that after they sychronize they maybe several cpu cycles off... of each other...
    i need to keep both core 0 and core 1 concurrently executing...
  14. macrumors 68040

    How did you allocate g_mem1? Processes do not share address space.
  15. macrumors newbie

    g_mem1=(UINT64)mmap(<SOMETHING HERE>);
  16. macrumors 6502


    What is the <SOMETHING HERE>, because what you pass to mmap() will influence how memory is shared between two processes.
  17. macrumors regular

    Divide the problem.

    First, do the two processes actually share memory?

    Let t0 store a magic number next to its variable. Check in t1 if that magic number is there. If not, the problem clearly is that they are not sharing memory.

    // Gunnar
  18. macrumors newbie

    g_mem1 = (UINT64)mmap((LPVOID)req_addr, g_reqsz, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_ANON|MAP_FIXED|MAP_PRIVATE, 0, 0);
  19. macrumors regular

    What about
    MAP_PRIVATE Modifications are private (copy-on-write)?

    Try MAP_SHARED instead
    // Gunnar
  20. macrumors newbie

    So far it looks like this solved the problem...
    If it does not i will let you know...
    thanks a lot :)
  21. macrumors 68030


    I have to ask...
    What are you doing that needs to be this low level??

    I have a poor imagination when it comes to cases when you are running on a platform that has gcc and the processor speed and optimizations to make C possible, but you prefer to do multi-threading from the ground up in assembly. I believe that C (with optimizations) can, and will, write better assembly then 99% of programmers could ever come up with.
  22. macrumors G5


    I wouldn't say the compiler would produce better assembler code than I could, given an infinite amount of time, but I can write ten times better algorithms in a high level language than I could in assembler. And I can call code written by others who spent ten times more time on it than I could reasonably justify.
  23. macrumors 68030


    One could argue that 'maintainable' is 'better'..

Share This Page