CS372H Spring 2011 Homework 4

Problem 1

Consider a uniprocessor kernel that user programs can trap into using system calls. The kernel receives and handles interrupt requests from I/O devices. Would there be any need for critical sections within that kernel?

Problem 2

Show that the following algorithm is a correct solution to the critical section problem for two processes (satisfies the 3 conditions discussed in class). The solution for process Pi (i = 0 or 1) with Pj(j = 1 or 0) is shown below:
flag[2];
int turn = 0;

flag[i] = 1;
while (flag[j])
{
    if (turn == j)
    {
        flag[i] = 0;
        while(turn == j)
            ;
        flag[i] = 1;
    }
}

/* enter C.S. */
/* exit C.S. */
turn = j;
flag[i] = 0;

This algorithm is called Dekker's solution.

Problem 3

Kim Hacker is a hardware designer who came up with a great idea for a hardware instruction that Kim claims can help in solving the critical section problem. It is called atomic counters. An atomic counter is a variable in memory that can be sampled and incremented in one atomic operation. Also, it can be reset to 0. That is, the two operations allowed on the shared variable aare:

        j = a++;         // execute in one atomic operation
        a = 0;

Either come up with a solution to the C.S. problem using this facility or show that it cannot help.

Problem 4

A common technique to achieve mutual exclusion in a uniprocessor kernel is to disable interrupts before getting inside a critical
section and enable them after exiting a critical section. Explain why this technique works.

Problem 5

Following is an implementation of a stack data structure for a multithreaded application. Identify the bugs, if any, and correct them.

#include "Exception.h"
#include "Semaphore.h"
#include <iostream.h>

const MaxStackSize = 100;

class Stack            // throws an exception object when popping an empty stack, and when pushing into a full stack
{
private:
    int s[MaxStackSize];
    int stackp;                        // stack pointer
    Exception * e;                  // For error handling
    Semaphore * sem;           // For mutual exclusion
public:
    Stack();
    ~Stack()        {};
    int Pop(void);
    void Push(int item);
};

Stack::Stack()
{
    stackp = MaxStackSize;
    e = new Exception();
    sem = new Semaphore(1);
}

int Stack::Pop(void)
{
    P(sem)
    if(stackp == MaxStackSize)
        {
            e->SetErrorMsg("Popping empty stack");
            e->SetErrorLocation("Stack::Pop()");
            throw(e);
        }
    V(sem);
    return s[stackp++];
}

void Stack::Push(int item)
{
    P(sem)
    if(stackp == 0)
        {
            e->SetErrorMsg("Pushing to a full stack");
            e->SetErrorLocation("Stack::Push()");
            throw(e);
        }
    s[--stackp] = item;
    V(sem);
}

Problem 6

Consider a system with three smoker processes and one agent process. Each smoker continuously rolls a cigarette and then smokes it. But to roll and smoke a cigarette, the smoker needs three ingredients: tobacco, paper, and matches. One of the smoker processes has paper, another has tobacco, and the third has matches. The agent has an infinite supply of all three materials.

The agent places two of the ingredients on the table. The smoker who has the remaining ingredient then makes and smokes a cigarette, signaling the agent on completion. The agent then puts out another two of the three ingredients, and the cycle repeats.

Assume the agent calls the procedure

void chooseIngredients(int *paper, int *tobacco, int *match);
to randomly select 2 of the 3 ingredients. The routine randomly sets 2 of the values to "1" and one of the values to "0". You don't have to write this routine.

Write a program to synchronize the agent and smokers.

  1. What synchronization and state variables will you use in this problem? (For each variable, indicate the variable's type, the variable's name, its initial value (if any), and a short comment describing the variable's purpose.

    Variable Name        Variable Type        Initial Value        Description

  2. Write the routines Agent() and matchSmoker() (the routine for the smoker that has lots of matches.) You don't have to write the routines paperSmoker() or tobaccoSmoker(), but your solution should be general enough so that those routines would be simple variations of matchSmoker().

Problem 7

Write a solution to the dining philosophers using locks and condition variables. You may wish to use the sthread wrapper in the lab T source. Your solution must prevent philosopher starvation. Follow the coding standards discussed in class. On the exam, you will be required to follow these coding standards in all questions of this type.

Problem 8

Implement the function
void parallelDo(int n, void *(function(void *)), void *arg[])
parallelDo spawns off N worker threads. function is a pointer to a function, and arg[] is an array of arguments to that function (e.g., worker thread i is to execute (*function)(arg[i])). parallelDo returns once all worker threads have finished running the function.

Assume you are given mutex locks and condition variables with standard public interfaces.

Assume that you are given functions creating and destroying threads that are similar to those discussed in class:

void sthread_create(sthread_t *thrd,

void *(start_routine(void*)),
void *arg0ToStartRoutine,
void *arg1ToStartRoutine,
...); // Pass as many args as you like
void sthread_exit(void);

Implement this parallelDo function. Follow the coding standards specified in the handout. Important: follow an object-oriented style of programming - encapsulate all shared states in an object that manages access to it.