Lab 4 - Concurrency Issues

This lab will give you a introduction in the basic concepts of concurrency. Concurrency is a fundamental concept in embedded systems. Many systems include multiple processors or peripherals, with shard common resources. However, when tasks are split up and execute on seperate processors, a whole new class of problems arises.

Part 1 - Mutual Exclusion

One of the first problems that arises is that two or more tasks can access the same resource (e.g. memory location). This can lead to the situation in which there can exist multiple copies of the data with different values. In order to avoid this from happening, we need some sort of device that will limit access to resources to one process at a time. This is the concept behind mutual exclusion primitives such as locks and semaphores. The region of code or memory location that requires mutual exclusion is known as the critical region. If mutual exclusion is not provided or faulty, then the output of your programs would be incorrect.

In this portion of the lab, you will have two tasks which concurrently call the function increment(), which will handle incrementing a shared variable. Each task will call increment 10,000 times, therefore the final value of the shared variable should be 20,000. In order to implement this correctly, only one task should be allowed to update the shared variable at a time.

To provide mutual exclusion to the critical region (the increment function), you will need to use binary semaphores. These were used in the tutorial of lab 1 and can be found in the help files of Tornado. You will need the functions semBCreate, semGive, and semTake. For more information on semaphores, see the help files or look in your text book.

For part 1, you will need to:

  1. Create a new project in Tornado.

  2. Download the source file source1.cpp and insert it into the project.

  3. Make any modifications necessary to the increment function in order to provide mutual exclusion.

  4. Run the program on the target server and make sure the output is 20,000. Note that even if the output is 20,000 the program might still be incorrect.

Part 2 - Producer/Consumer Problem

In this section, you will be implementing the producer/consumer problem. This problem involves a bounded buffer which is modified concurrently by multiple processes. A producer adds items to the buffer and a consumer removes items from it. The buffer is bounded, therefore a producer can only add items when the buffer isn't full and a consumer can only remove items when the buffer isn't empty. In this lab, there will be exactly 1 producer and 1 consumer.

For part 2, you will need to:

  1. Download the source, source2.cpp
  2. Modify the produce task to add items to the buffer. The producer needs to pick a random value, insert the value into the buffer. The taskDelay function that is provided causes your added code to occur at random times, thus creating a more realistic simulation.
  3. Modify consumer task to remove items from the buffer. The provided code will also cause this to occur at random times.
  4. Add printf statements to the producer and consumer function that state what values were produced or consumed.
  5. Use counting semaphores to implement concurrency control and mutual exclusion (don't allow producer to insert into full buffer or consumer to remove from empty buffer). You will need the functions semCCreate, semGive, and semTake. See help->manual index for information on how to use these functions. For more information on semaphores and the producer/consumer problem, see your text book.
  6. Use a binary semaphore to provide mutual exclusion for the printf statements.

Part 3 - Lab Report

In your lab report be sure to discuss the following:

  1. A common mistake in providing mutual exclusion is to use a flag to signify whether or not there is a process executing in the critical region. Whenever a process wants to enter the critical region, it checks to see if the flag is set. If it is, it will simply wait until the flag is not set (i.e. while(flag);). When a process enters the critical region, the flag is set. When a process leaves the critical region, the flag is reset. Explain why this does not gaurantee mutual exclusion. Also, discuss any other problems with this solution that you can think of.
  2. Explain how your solution to the producer/consumer problem guarantees there won't ever be any conflicts between the producer and the consumer. Also, discuss the effect on performance and functionality if busy waiting (i.e. while(flag)) was used instead of semaphores.


CS122B, Winter 2002