Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • World
  • Users
  • Groups
Skins
  • Light
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Default (No Skin)
  • No Skin
Collapse
Code Project
  1. Home
  2. General Programming
  3. C / C++ / MFC
  4. Thread synchronization problem

Thread synchronization problem

Scheduled Pinned Locked Moved C / C++ / MFC
questionhelp
16 Posts 6 Posters 0 Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • S Offline
    S Offline
    samzcs
    wrote on last edited by
    #1

    Hi, I met a thread synchronization scenario in a small code.

    //task A:
    if (rcvd == true)
    {
    read rcv_buffer
    ...
    rcvd = false;
    }

    //task B:
    dataCB(int ch_id, char *buf, int len, char stat)
    {
    rcvd = true;
    receivenotify(chid, rcv_buffer, sizeof(rcv_buffer), dataCB,0);
    }

    here, rcvd = false in task A, can't stop task B produce data. how can I let task A, B synchronized. add if (rcvd == false) in task B before receivenotify()? is that a dead lock ? Thanks

    Richard Andrew x64R L _ 3 Replies Last reply
    0
    • S samzcs

      Hi, I met a thread synchronization scenario in a small code.

      //task A:
      if (rcvd == true)
      {
      read rcv_buffer
      ...
      rcvd = false;
      }

      //task B:
      dataCB(int ch_id, char *buf, int len, char stat)
      {
      rcvd = true;
      receivenotify(chid, rcv_buffer, sizeof(rcv_buffer), dataCB,0);
      }

      here, rcvd = false in task A, can't stop task B produce data. how can I let task A, B synchronized. add if (rcvd == false) in task B before receivenotify()? is that a dead lock ? Thanks

      Richard Andrew x64R Offline
      Richard Andrew x64R Offline
      Richard Andrew x64
      wrote on last edited by
      #2

      That's not how to synchronize threads. You should be using Events. See Using Event Objects | Microsoft Docs[^]

      The difficult we do right away... ...the impossible takes slightly longer.

      S 1 Reply Last reply
      0
      • Richard Andrew x64R Richard Andrew x64

        That's not how to synchronize threads. You should be using Events. See Using Event Objects | Microsoft Docs[^]

        The difficult we do right away... ...the impossible takes slightly longer.

        S Offline
        S Offline
        samzcs
        wrote on last edited by
        #3

        It's a rare C program, no Events :)

        Richard Andrew x64R 1 Reply Last reply
        0
        • S samzcs

          It's a rare C program, no Events :)

          Richard Andrew x64R Offline
          Richard Andrew x64R Offline
          Richard Andrew x64
          wrote on last edited by
          #4

          What is a "rare C program?" Is this on Windows?

          The difficult we do right away... ...the impossible takes slightly longer.

          S 1 Reply Last reply
          0
          • Richard Andrew x64R Richard Andrew x64

            What is a "rare C program?" Is this on Windows?

            The difficult we do right away... ...the impossible takes slightly longer.

            S Offline
            S Offline
            samzcs
            wrote on last edited by
            #5

            No, its on a 32 bit micro controller, no OS.

            C 1 Reply Last reply
            0
            • S samzcs

              No, its on a 32 bit micro controller, no OS.

              C Offline
              C Offline
              CPallini
              wrote on last edited by
              #6

              Then you haven't threads, have you? Are you talking about interrupts?

              S 1 Reply Last reply
              0
              • S samzcs

                Hi, I met a thread synchronization scenario in a small code.

                //task A:
                if (rcvd == true)
                {
                read rcv_buffer
                ...
                rcvd = false;
                }

                //task B:
                dataCB(int ch_id, char *buf, int len, char stat)
                {
                rcvd = true;
                receivenotify(chid, rcv_buffer, sizeof(rcv_buffer), dataCB,0);
                }

                here, rcvd = false in task A, can't stop task B produce data. how can I let task A, B synchronized. add if (rcvd == false) in task B before receivenotify()? is that a dead lock ? Thanks

                L Offline
                L Offline
                leon de boer
                wrote on last edited by
                #7

                You called this thread but you describe these as tasks and I read your other responses ... so I am assuming this is a task switcher. So I am guessing you are on a pre-emtive task switcher that is running multiple tasks. If that is the case usually you synchronize by stopping the task switcher for switching for a short period as you have a critical section. For example of a context switcher running off a timer interrupt, you just disable the interrupts which will stop the context switcher from switching your task out. On all context switchers there is a way to do this because at various points in the kernel they have to enter critical sections. You need to keep your time inside these sections as fast as you possibly can. Now if it is going to take to long you need to use locks. They start simple and grow in complexity as you want different things. Even on a basic task switcher it should have a semaphore because they are so easy to define or you can make one just using a volatile int.

                typepdef volatile int semaphore;

                The volatile is important because you are going to end up looping around reading the same address. You need to make sure the compiler doesn't optimize that to only reading it once and using the old value for each of the check loops. It must read the memory location each and every time. To enter a semaphore locked area generally you will make a call to something called a name like semaphore_inc or semaphore_pend. What that will do is make sure you have single aquire on the semaphore, if you don't it will sleep your task it will basically be doing this

                void semaphore_inc (semaphore* sem)
                {
                disable context switcher;
                while (*sem != 0)
                {
                enable context switcher;
                sleep(1);
                disable context switcher;
                }
                *sem++;
                enable context switcher;
                }

                The disable and enable context switcher will be simple disable and enable interrupt opcodes on a simple timer interrupt system up to a small piece of code on more advanced switchers. What is important is do you get what happens with your task getting sleeped unless it has solo access to the semaphore. The 1 will be the minimum unit of time on the task switcher until your task can be woken up. It's non blocking because the sleep call will activate the next task to run (because you can't and so you give up your timeslice) and it will come back to you sometime later on another context switch. The moment you set the semaphore any other task trying to enter would have the

                S 1 Reply Last reply
                0
                • C CPallini

                  Then you haven't threads, have you? Are you talking about interrupts?

                  S Offline
                  S Offline
                  samzcs
                  wrote on last edited by
                  #8

                  no, no interrupts, it's a simple scheduler using while(1). and one cycle is 10 ms , one cycle is 5 ms. task A in 10 ms cycle, task B in 5 ms cycle.

                  C 1 Reply Last reply
                  0
                  • L leon de boer

                    You called this thread but you describe these as tasks and I read your other responses ... so I am assuming this is a task switcher. So I am guessing you are on a pre-emtive task switcher that is running multiple tasks. If that is the case usually you synchronize by stopping the task switcher for switching for a short period as you have a critical section. For example of a context switcher running off a timer interrupt, you just disable the interrupts which will stop the context switcher from switching your task out. On all context switchers there is a way to do this because at various points in the kernel they have to enter critical sections. You need to keep your time inside these sections as fast as you possibly can. Now if it is going to take to long you need to use locks. They start simple and grow in complexity as you want different things. Even on a basic task switcher it should have a semaphore because they are so easy to define or you can make one just using a volatile int.

                    typepdef volatile int semaphore;

                    The volatile is important because you are going to end up looping around reading the same address. You need to make sure the compiler doesn't optimize that to only reading it once and using the old value for each of the check loops. It must read the memory location each and every time. To enter a semaphore locked area generally you will make a call to something called a name like semaphore_inc or semaphore_pend. What that will do is make sure you have single aquire on the semaphore, if you don't it will sleep your task it will basically be doing this

                    void semaphore_inc (semaphore* sem)
                    {
                    disable context switcher;
                    while (*sem != 0)
                    {
                    enable context switcher;
                    sleep(1);
                    disable context switcher;
                    }
                    *sem++;
                    enable context switcher;
                    }

                    The disable and enable context switcher will be simple disable and enable interrupt opcodes on a simple timer interrupt system up to a small piece of code on more advanced switchers. What is important is do you get what happens with your task getting sleeped unless it has solo access to the semaphore. The 1 will be the minimum unit of time on the task switcher until your task can be woken up. It's non blocking because the sleep call will activate the next task to run (because you can't and so you give up your timeslice) and it will come back to you sometime later on another context switch. The moment you set the semaphore any other task trying to enter would have the

                    S Offline
                    S Offline
                    samzcs
                    wrote on last edited by
                    #9

                    Thank you very much for the answer. But the C program is in an embedded device, it's not a pre-emptive task environment, just a forever loop cycle, in which, some functions be called in 10 ms cycle, some functions be called in 5 ms cycle. each cycle I called it a task. The structure like a simple scheduler using while(1) loop. Task A (or Cycle A) is a 10 ms cycle, and Task B is a 5 ms cycle, just use a counter to implemented the 10 ms , 5 ms cycle, like

                    while(1)
                    {
                    count ++;
                    if (count == 5)
                    {
                    task B be called
                    }
                    if (count == 10)
                    {
                    count = 0;
                    task A be called.
                    }
                    }

                    Thanks

                    L 1 2 Replies Last reply
                    0
                    • S samzcs

                      no, no interrupts, it's a simple scheduler using while(1). and one cycle is 10 ms , one cycle is 5 ms. task A in 10 ms cycle, task B in 5 ms cycle.

                      C Offline
                      C Offline
                      CPallini
                      wrote on last edited by
                      #10

                      But have you threads? Microcontrollers usually have not.

                      1 Reply Last reply
                      0
                      • S samzcs

                        Thank you very much for the answer. But the C program is in an embedded device, it's not a pre-emptive task environment, just a forever loop cycle, in which, some functions be called in 10 ms cycle, some functions be called in 5 ms cycle. each cycle I called it a task. The structure like a simple scheduler using while(1) loop. Task A (or Cycle A) is a 10 ms cycle, and Task B is a 5 ms cycle, just use a counter to implemented the 10 ms , 5 ms cycle, like

                        while(1)
                        {
                        count ++;
                        if (count == 5)
                        {
                        task B be called
                        }
                        if (count == 10)
                        {
                        count = 0;
                        task A be called.
                        }
                        }

                        Thanks

                        L Offline
                        L Offline
                        leon de boer
                        wrote on last edited by
                        #11

                        It's exactly the same you have to be manually yielding or forcibly switching or else you would never return. So the semaphore code is exactly the same except you can ignore all the switcher enable and disable all you do is if the semaphore is locked you immediately switch to the other code (which is your version of sleep) because you can't do anything on that task. So basically if taskA has the semaphore, then whenever you call taskB code and you want to enter the semaphore lock area you just exit and call taskA because taskB can't run until taskA gets it's fat butt out of the lock area. So in your case you would actually get a slight processing boost on taskA because it would get called twice (minus the exit and switch time) for each count loop. The same applies if taskB has the semaphore and taskA went to enter the semaphore lock area then it would simply exit and call taskA. It's pretty straight forward the task that is waiting on the lock is dead in the water and you just need it to give all the processing power to the other task to get it out of the lock area. As your setup is so simple you could also just setup a state machine to mirror a semaphore, you have 5 states 1.) Neither A,B in lock area, 2.) A in lock area B out, 3.) A in B waiting, 4,) B in A out, 5.) B in A waiting The states are changed as A & B enter and leave the lock area ...see functions in code after the modified work loop

                        enum lock_state {ABoutofLockArea = 0, AinLockAreaBnormal, AinLockAreaBwaiting, BinLockAreaAnormal, BinLockAreaAwaiting};

                        enum lock_state state = 0;

                        /* MODIFIED WORK LOOP */

                        while (1){
                        if (count == 5)
                        {
                        if (state == AinLockAreaBwaiting) TaskA is called // no point calling taskB its waiting
                        else task B be called
                        }
                        if (count == 10)
                        {
                        count = 0;
                        if (state == BinLockAreaAwaiting) TaskB is called // no point calling taskA its waiting
                        else task A be called.
                        }
                        }

                        /* HERE ARE YOUR STATE CHANGE FUNCTIONS */

                        void taskA_EnterlockArea(void)
                        {
                        if (state == BinLockAreaAnormal) state = BinLockAreaAwaiting; // B already in there A must wait
                        else state = AinLockAreaBnormal // B is not there so A can enter
                        }

                        void taskA_LeavinglockArea(void)
                        {
                        if (state == AinLockAreaBwaiting) state = BinLockAreaAnormal; // B is waiting it can now enter, A goes back to normal
                        else state = ABoutofLockArea; // Otherwise no one is in the lock area
                        }

                        void taskB_EnterlockArea(void)

                        1 Reply Last reply
                        0
                        • S samzcs

                          Thank you very much for the answer. But the C program is in an embedded device, it's not a pre-emptive task environment, just a forever loop cycle, in which, some functions be called in 10 ms cycle, some functions be called in 5 ms cycle. each cycle I called it a task. The structure like a simple scheduler using while(1) loop. Task A (or Cycle A) is a 10 ms cycle, and Task B is a 5 ms cycle, just use a counter to implemented the 10 ms , 5 ms cycle, like

                          while(1)
                          {
                          count ++;
                          if (count == 5)
                          {
                          task B be called
                          }
                          if (count == 10)
                          {
                          count = 0;
                          task A be called.
                          }
                          }

                          Thanks

                          1 Offline
                          1 Offline
                          11917640 Member
                          wrote on last edited by
                          #12

                          In this case you don't need synchronization at all.

                          S 1 Reply Last reply
                          0
                          • 1 11917640 Member

                            In this case you don't need synchronization at all.

                            S Offline
                            S Offline
                            samzcs
                            wrote on last edited by
                            #13

                            Thanks, First I think I don't need a synchronization. But when I code like:

                            //task A:
                            if (rcvd == true)
                            {
                            read rcv_buffer
                            ...
                            rcvd = false;
                            }

                            //task B:
                            dataCB(int ch_id, char *buf, int len, char stat)
                            {
                            rcvd = true;
                            if((rcvd == false)
                            {
                            receivenotify(chid, rcv_buffer, sizeof(rcv_buffer), dataCB,0);
                            }
                            }

                            The program behavior is not correct

                            L 1 2 Replies Last reply
                            0
                            • S samzcs

                              Thanks, First I think I don't need a synchronization. But when I code like:

                              //task A:
                              if (rcvd == true)
                              {
                              read rcv_buffer
                              ...
                              rcvd = false;
                              }

                              //task B:
                              dataCB(int ch_id, char *buf, int len, char stat)
                              {
                              rcvd = true;
                              if((rcvd == false)
                              {
                              receivenotify(chid, rcv_buffer, sizeof(rcv_buffer), dataCB,0);
                              }
                              }

                              The program behavior is not correct

                              L Offline
                              L Offline
                              leon de boer
                              wrote on last edited by
                              #14

                              It isn't at all surprising it misbehaves you have >>>> SHARED DATA <<<< You guys seem to think having a co-operative task switcher somehow makes you immune to shared data issues ... well your wrong as you found out :-) lets give you the situation .. it may not be your case but follow what happens task b picks up the count of the recieve buffer .. then yields to task a task a changes the count value of the recieve buffer then yields back to task b task b now wrongly writes the old count value back and your program now crashes So the variable "count" is being accessed by both tasks it is shared, it is that simple The same will be true of other variables you are accessing with both taskA and taskB and if you drag all the stuff accessed by both tasks to an area they are called the shared data and they are the problem. Now what creates the problem above is yielding between the two tasks, while one task is using the shared data. Now on the code above it looks like taskB probably never yields while it is inside dataCB. That is because it's a function and I doubt you can organize the yield. However on your taskA code above which looks like it in a flat loop you also can not yield to taskB anywhere in it not even in the "..." section. It must start and finish that entire code without ever yielding to taskB or your code will crash. The hint to why it would crash is the variable rcvd which is probably a bool and it's written to by both tasks a big no no or danger Will Robinson, as per the example I gave at top. If you understoof the issue you can refactor taskA code to make the problem less likely but curing it if you want to yield in "..." needs definitive steps. anyhow I will leave you with it.

                              In vino veritas

                              1 Reply Last reply
                              0
                              • S samzcs

                                Thanks, First I think I don't need a synchronization. But when I code like:

                                //task A:
                                if (rcvd == true)
                                {
                                read rcv_buffer
                                ...
                                rcvd = false;
                                }

                                //task B:
                                dataCB(int ch_id, char *buf, int len, char stat)
                                {
                                rcvd = true;
                                if((rcvd == false)
                                {
                                receivenotify(chid, rcv_buffer, sizeof(rcv_buffer), dataCB,0);
                                }
                                }

                                The program behavior is not correct

                                1 Offline
                                1 Offline
                                11917640 Member
                                wrote on last edited by
                                #15

                                If your system behaves as you describe, this is not synchronization problem. Task A and B are actually not tasks, these are synchronous functions. No parallel execution, every function has exclusive access to all variables (resources). Specifically, in your last code fragment:

                                rcvd = true;
                                if((rcvd == false) 
                                {
                                    // this code is never executed
                                    receivenotify(chid, rcv\_buffer, sizeof(rcv\_buffer), dataCB,0);
                                }
                                
                                1 Reply Last reply
                                0
                                • S samzcs

                                  Hi, I met a thread synchronization scenario in a small code.

                                  //task A:
                                  if (rcvd == true)
                                  {
                                  read rcv_buffer
                                  ...
                                  rcvd = false;
                                  }

                                  //task B:
                                  dataCB(int ch_id, char *buf, int len, char stat)
                                  {
                                  rcvd = true;
                                  receivenotify(chid, rcv_buffer, sizeof(rcv_buffer), dataCB,0);
                                  }

                                  here, rcvd = false in task A, can't stop task B produce data. how can I let task A, B synchronized. add if (rcvd == false) in task B before receivenotify()? is that a dead lock ? Thanks

                                  _ Offline
                                  _ Offline
                                  _Superman_
                                  wrote on last edited by
                                  #16

                                  Since the tasks are not waiting on any object or variable, there will be no dead lock. I guess you do need to add if (rcvd == false) in task B before the line rcvd = true;

                                  «_Superman_»  _I love work. It gives me something to do between weekends.

                                  _Microsoft MVP (Visual C++) (October 2009 - September 2013)

                                  Polymorphism in C

                                  1 Reply Last reply
                                  0
                                  Reply
                                  • Reply as topic
                                  Log in to reply
                                  • Oldest to Newest
                                  • Newest to Oldest
                                  • Most Votes


                                  • Login

                                  • Don't have an account? Register

                                  • Login or register to search.
                                  • First post
                                    Last post
                                  0
                                  • Categories
                                  • Recent
                                  • Tags
                                  • Popular
                                  • World
                                  • Users
                                  • Groups