Thread synchronization problem
-
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
-
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
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.
-
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.
-
What is a "rare C program?" Is this on Windows?
The difficult we do right away... ...the impossible takes slightly longer.
-
What is a "rare C program?" Is this on Windows?
The difficult we do right away... ...the impossible takes slightly longer.
-
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
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
-
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
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
-
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.
-
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
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)
-
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
In this case you don't need synchronization at all.
-
In this case you don't need synchronization at all.
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
-
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
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
-
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
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); }
-
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
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 linercvd = true;
«_Superman_» _I love work. It gives me something to do between weekends.
_Microsoft MVP (Visual C++) (October 2009 - September 2013)