Context Switching RTOS

79 views Asked by At

I have two TASK A and B, where Task A is High priorty and B low priorty. condition and mem are 2 global variable (shared) let's assume that Task B is running and doing some memory operation.

***Task B (Low priority) ***             
   if (condition) {
      .....
      for (i = 0; i< 10; i++){
          // example we are at i=4  
          mem[i] = mem[i] +1;
      }
      ..... 
    }

now while Task B is running (for example we are at index i = 4. ) Task A becomes ready, a context switch will occur. memory will be overwritten and the condition of execution for Task B will be set to false.

*** TASK A (High priority)
    ......
    enter_critical();
    condition = false;
    mem[i] = 0;  
    exit_critical();
    ......

now Task A is finshed and yields resources for Task B to run.

Question :

1- Will the memory operation still take place, eventhough the condition is set to false? (will the condition be checked before reentering Task B)

2- If the memory operation still happens, at which index will it restart( index i= 4 or i=5)?

3- what will mem contains after Task B has finished executing. (i excepct 0 0 0 0 1 1 1 1 1 1)

1

There are 1 answers

2
Lundin On

1- Will the memory operation still take place, eventhough the condition is set to false?

Yes. The check against condition in task B has already been carried out long time ago. Furthermore condition in itself is probably not thread safe/atomic.

2- If the memory operation still happens, at which index will it restart( index i= 4 or i=5)?

It will pick up where it left, which could be in the middle of a C language expression evaluation. It doesn't orderly finish a lap in the C language loop before the context switch.

3- what will mem contains after Task B has finished executing.

Some non-deterministic, likely corrupted gibberish with no guarantee of the value at location mem[i]. Whatever i means... because it is not protected either.


You have to understand that context switches are very low level. The interrupted task gets to finish whatever assembler instruction it was previously executing. Then the program counter and task B stack + condition code variables etc are stored, so that task B may resume at the very same assembler instruction it was supposed to execute before the interruption.

This is why you have to use synchronizing means like semaphores or critical sections in both processes. All variables that are shared and modified by at least one process are subject to race condition bugs.

Smart programmers will minimize the amount of code placed inside such synchronizing "locks", since it may stall or disrupt other processes.

For example you could do this:

// task B
static uint8_t buf1[n];
static uint8_t buf2[n];
uint8_t* mem = buf1;

...
/* do the slow processing of data in advance: */
uint8_t* next_buf;
if(mem==buf1) // ensure to work on the inactive buffer
{
  next_buf = buf2;
}
else
{
  next_buf = buf1;
}
prepare(next_buf); // take all the time in the world here

enter_critical();
  /* only do a fast pointer swap here: */
  uint8_t* tmp = mem;
  mem = next_buf;
  next_buf = tmp;
exit_critical();
// now mem is up to date with the changes from prepare()

...

// task A
extern uint8_t* mem;

enter_critical();
  do_stuff_with(mem);
exit_critical();