Resource Management

This chapter will discuss services provided by µC/OS-III to manage shared resources. A shared resource is typically a variable (static or global), a data structure, table (in RAM), or registers in an I/O device.

When protecting a shared resource it is preferred to use mutual exclusion semaphores, as will be described in this chapter. Other methods are also presented.

Tasks can easily share data when all tasks exist in a single address space and can reference global variables, pointers, buffers, linked lists, ring buffers, etc. Although sharing data simplifies the exchange of information between tasks, it is important to ensure that each task has exclusive access to the data to avoid contention and data corruption.

For example, when implementing a module that performs a simple time-of-day algorithm in software, the module obviously keeps track of hours, minutes and seconds. The TimeOfDay() task may appear as shown in the listing below.

Imagine if this task was preempted by another task, after setting the Minutes to 0, because an interrupt occurred and that the other task was more important than the TimeOfDay() task. Now imagine what will happen if this higher priority task wants to know the current time from the time-of-day module. Since the Hours were not incremented prior to the interrupt, the higher-priority task will read the time incorrectly and, in this case, it will be incorrect by a whole hour.

The code that updates variables for the TimeOfDay() task must treat all of the variables indivisibly (or atomically) whenever there is possible preemption. Time-of-day variables are considered shared resources and any code that accesses those variables must have exclusive access through what is called a critical section. µC/OS-III provides services to protect shared resources and enables the easy creation of critical sections.


Faulty Time-Of-Day clock task
          CPU_INT08U  Hours;
          CPU_INT08U  Minutes;
          CPU_INT08U  Seconds;
           
           
          void  TimeOfDay (void *p_arg)
          {
              OS_ERR  err;
           
           
              (void)&p_arg;
              while (DEF_ON) {
                  OSTimeDlyHMSM(0, 
                                0, 
                                1, 
                                0, 
                                OS_OPT_TIME_HMSM_STRICT, 
                                &err);
                  /* Examine "err" to make sure the call was successful */
                  Seconds++;
                  if (Seconds > 59) {
                      Seconds = 0;
                      Minutes++;
                      if (Minutes > 59) {
                          Minutes = 0;
                          Hours++;
                          if (Hours > 23) {
                              Hours = 0;
                          }
                      }
                  }
              }
          }


The most common methods of obtaining exclusive access to shared resources and to create critical sections are:

  • disabling interrupts
  • disabling the scheduler
  • using semaphores
  • using mutual exclusion semaphores (a.k.a. a mutex)

The mutual exclusion mechanism used depends on how fast the code will access a shared resource, as shown in the table below.

Resource Sharing
Resource Sharing MethodWhen should you use?
Disable/Enable InterruptsWhen access to shared resource is very quick (reading from or writing to few variables) and access is faster than µC/OS-III’s interrupt disable time.

It is highly recommended to not use this method as it impacts interrupt latency.
Locking/Unlocking the SchedulerWhen access time to the shared resource is longer than µC/OS-III’s interrupt disable time, but shorter than µC/OS-III’s scheduler lock time.

Locking the scheduler has the same effect as making the task that locks the scheduler the highest-priority task.

It is recommended not to use this method since it defeats the purpose of using µC/OS-III. However, it is a better method than disabling interrupts, as it does not impact interrupt latency.
SemaphoresWhen all tasks that need to access a shared resource do not have deadlines. This is because semaphores may cause unbounded priority inversions (described later). However, semaphore services are slightly faster (in execution time) than mutual-exclusion semaphores.
Mutual Exclusion SemaphoresThis is the preferred method for accessing shared resources, especially if the tasks that need to access a shared resource have deadlines.

µC/OS-III’s mutual exclusion semaphores have a built-in priority inheritance mechanism, which avoids unbounded priority inversions.

However, mutual exclusion semaphore services are slightly slower (in execution time) than semaphores since the priority of the owner may need to be changed, which requires CPU processing