Data Sharing & Protection

  • Detect conditions where datasharing requires the use of protection mechanisms
  • Apply protection mechanisms to protect shared data and shared resources.
  • Gain an exposure to deadlocks and methods to avoid them
  • Understand how priority inversion occurs and be exposed to solutions implemented by operating systems.

Data Sharing & Protection

  • Why do we have a problem?
    • Interrupts and Tasks make a system appear to perform multiple tasks simultaneously - i.e. a Multi-tasking Operating System
    • Actual execution occurs in an interleaved fashion
  • If two ‘simultaneous tasks’ share a piece of data then we have opportunities for data corruption

Scenarios

  • ISR signaling an interrupt event.
  • Sharing a resource
  • Coordinating work. Two or more tasks working together to complete distributed work.
  • Lining up work such as in a queue or buffer.
  • Waiting for resource to become available or for work to complete.
  • Passing information
    • ISR to task
    • Task to task

Synchronization and Sharing techniques

  • Interrupt control
    • Enabling / disabling interrupts
    • Powerful method
    • Used during setup
    • Used to protect code sequences that much complete atomically.
  • Tasks
    • A tasks is any function or sequence of functions being executed.
    • Tasks break work into individually manageable chunks.
  • Priorities
    • Assign levels of importance to tasks.
    • Used to decide which tasks should be running
  • Semaphore
    • Is some kind of token used to coordinate who goes and who waits. For example a stoplight is a type of semaphore.
    • Holding and releasing the token
    • Waiting and acquiring the token
    • Acquire, hold, release, blocked, waiting.

Synchronization Issues

Data Corruption

Dead Locks

Race Conditions

Priority Inversion

Description

  • When a lower priority task has possesion of a resource needed by a higher priority task.
  • The high priority task is blocked until the lower priority task releases the resource.
  • The lower priority of the blocking task can mean it can be prempted and thus further killing the blocked high priority task.

Consequences

  • High priority task may miss an important deadline failing its real-time requirement.

How it happens

  • Poor planning in the architecture of the system.
  • Or an unavoidable contension for the same resource.
    • Accessing an atomic operation
    • For example access to the filesystem

Mitigation

  • Make sure the high priority task has few outside dependencies
  • Pre-allocate resources
  • Know which resources other tasks are going to want to access.
    • Is there a way to buffer access?
    • Does the high priority task actually have to wait for particular actions to complete or can it delgate it to buffers and lower priority tasks. That is the high priority task signals other tasks instead of doing the work itself.
  • Minimize the time the lock is held by the lower priority task.
  • Boost the priority of the lower priority task to prevent it from by prempted by other task with high priority then it has.

Data Sharing & Protection

  • Detect conditions where datasharing requires the use of protection mechanisms
  • Apply protection mechanisms to protect shared data and shared resources.
  • Gain an exposure to deadlocks and methods to avoid them
  • Understand how priority inversion occurs and be exposed to solutions implemented by operating systems.
  • Why do we have a problem?
    • Interrupts and Tasks make a system appear to perform multiple tasks simultaneously – i.e. a Multi-tasking Operating System
    • Actual execution occurs in an interleaved fashion
    • If two ‘simultaneous tasks’ share a piece of data then we have opportunities for data corruption

Examples

Single Thread plus Interrupt

Whenever there is a chance that executing code that is using a data item can be interrupted by code that may also use the same data item before the first user has completed its operation on the data item, we have a data-sharing issue. An atomic operation is an operation that is non-interruptible and once started, must complete.

  • Often (typically) an increment operation (index++;) is atomic
  • An operation that requires a load, execute and store is not – read-modify-write
    • (e.g. index = index + 22;).
    • INT32 Counter = 0;
main()
{
    INT32 LastCounter = 0;
    while(not exiting)
    {
        if (Counter != LastCounter)
        {
            LastCounter = Counter;
            TextOut(“Count %d\n”, LastCounter);
        }
    }
}

InterruptA()
{
    Counter++;
}

Multiple Threads (no interrupts)

Struct {
    UINT32 arrayCount;
    UCHAR  data[MAX_ARRAY]
} DATALOG, *PDATALOG;

DATALOG samples;

ThreadA() // creator of data
{
    newSample = ReadNewData()
    if (samples->arrayCount < MAX_ARRAY)
    {
       samples->Data[samples->arrayCount++] = newSample;
    }
    else
    {
        // error case or handle wrap around
    }
}

ThreadB() // consumer of data
{
  UINT32 lastSample = 0;

  if (samples->ArrayCount > lastSample)
  {
      newSample = samples->data[lastSample++];
      ProcessSample(newSample);
      //handle wrap around
  }
}

Data Sharing Protection Solutions

  • Use a design that does not share data. For example by avoiding globals.
  • Disable Task switching
    • Call OS functions that disable the scheduler
    • In uCOS the scheduler can be turn on and off by calling OSSchedLock() and OSSchedUnlock()
  • Disable Interrupts or Interrupt
    • Kind of a big hammer approach
    • Affects the entire systems
    • All interrupts are disabled
  • Protect with a Semaphore
    • A semaphore is a type of OS flag that can be used to synchronize the use of resources.
    • In uCOS there are two semaphore functions OSSemPost()and OSSemPend().
    • More on semaphores later.

Disable Task switching

// creator of data
    ThreadA()
    {
        newSample = ReadNewData();
        if (samples->arrayCount < MAX_ARRAY)
        {
           OsSchedLock();
           samples->data[samples->arrayCount++] = newSample;
           OsSchedUnlock();
        }
        else
        {
            HandleError();
        }
    }

    // consumer of data
    ThreadB()
    {
      UINT32 sastSample = 0;
      OsSchedLock();
      if (samples->arrayCount > lastSample)
      {
          newSample = samples->data[lastSample++];
          OsSchedUnlock();
          ProcessSample(newSample);
      }
      else
      {
          OsSchedUnlock();
      }
    }

Disable Interrupts

INT32 Counter = 0;

main()
{
    INT32 lastCounter = 0;
    while(notExiting)
    {
        OS_ENTER_CRITICAL_SECTION();
        if (counter != lastCounter)
        {
            lastCounter = counter;
            OS_EXIT_CRITICAL_SECTION();
            TextOut(“Count %d\n”, lastCounter);
        }
        else
        {
            OS_EXIT_CRITICAL_SECTION();
        }
    }
}

InterruptA()
{
    Counter++;
}