Protected Access or Serialize

../_images/Protected.png
  • Semaphore protects the call or access to a shared resource.
  • Semaphore is initialized to 1 meaning it is available.
  • Allows one task at a time.
  • If protecting a function call, the function could be one of a group of functions that all interact with the same data.
  • The calls to Pend and Post can be moved into a function to make it easier to use.
  • The pattern can be used to serialize calls to a function, for example printf which would keep the output from being intertwined.

Serialized Print

void serialized_print(char *str)
{
    INT8U err;
    OSSemPend(g_semPrint, 0, &err);
    print(str);
    OSSemPost(g_semPrint);
}

Signal and Wait

../_images/Signal_and_Wait.png
  • Wait for work to complete.
  • Coordinates two tasks.
  • Uses two semaphores both intializes to 0.
  • TaskA starts TaskB with a Post to SemA then waits on a Pend for SemB.
  • TaskB Posts to SemB when its finished.
  • TaskA resumes.

Shared Data

../_images/Shared_Data.png

Using semaphores to share data.

  • Single semaphore intialized to 1.
  • The semaphores coordinates the reads and writes.
  • A task must acquire the semaphore before it can read or write the data.
  • The semaphore ensures only one task at a time is accessing the data.
  • The pattern ensures the data is consistent since the data is protected while makeing udpates to it.
  • Acts like a critical section but without disabling interrupts interrupts.

Multiple Access

../_images/Multiple_Semaphore.png
  • Sometimes a task needs to acquire a lock on more then one resource at the some time.
  • For each resource needed the task must Pend to get it.
  • Once it has all the semaphores then it can use the resources.
  • When its done it Posts to release the semaphores.
  • It is important to pay attention to the order the semaphores are acquired and released.

Note

Having the order of pends and posts different between tasks can lead to deadlocks.

Rule to prevent deadlocks

  • All tasks acquire in the same order.
  • All tasks release in the reverse order of acquiring.

Producer/Consumer

../_images/Producer_Consumer.png

The Producer/Consumer is a common pattern where one function is producing data and another function is consuming the data. Between the producer and consumer there can be a buffer to hold temporarily hold the data until the consumer is ready to accept it.

The producer could be a device driver that is reading keystrokes from a keyboard and assembling them into a message. Each message is a single line of text seperated by a carriage return. Each time it has a full message the producer stores it into the buffer.

The consumer could be a task that is processing the messages from the buffer one by one. It waits for messages to appear in the buffer. When messages appear it removes them one at a time processing the message before getting the next one.

Three things are important in this setup. First knowing or signalling when there is message in the buffer, two the size of the buffer which limits then number of messages that can be stored in it, and third controlling access to the buffer to keep the buffer from being corrupted.

One way of setting this up is to have three semaphores. The first one controls when an item can be added to the buffer, the second controls when an item can be removed from the buffer, and the third protects the buffer by insuring exclusive access.

  • Good pattern for managing buffers.
  • addSem is set to the size of the buffer.
  • addSem counts down as the buffer fills up.
  • takeSem is set to 0.
  • takeSem counts up as the buffer fills.
  • exclSem (exclusive) is set to 1.
  • exclSem used to protect the shared functions.
  • addSem and takeSem work together to guard the number of items in the buffer.
  • Items can only be added while addSem > 0.
  • Items can only be removed while takeSem > 0.