CS 252 Homework 1 Solution

Part 1.

__T_ A process has a stack for every thread it contains.
__T_ Race conditons are more likely to happen in multiprocessor machines.
__F_ In POSIX threads, the thread calling fork will create a child process that is a copy of the parent inclluding all the threads in the process.                   (only in Solaris and by calling thr_create)

Explain what is deadlock and what is starvation and give an example of each.

Deadlock happens when threads are waiting for resources that will not be available to them and will not proceed. The only way to end the deadlock is to kill the process. An example would be the Dining Philosophers Problem where every philosopher waits for a fork and no one is willing to give up the fork he/she is holding.

Starvation is not as serious as deadlock. It is a condition where a thread might take a long time to obtain resources because there are constantly other threads holding the locks. An example would be the read/write locks.

What are the advantages and disadvantages of using threads vs. using processes?

Threads Processes
Advantages Disadvantages Advantages Disadvantages
  • Faster thread creation
  • Faster context switch
  • Faster communication using global variables
  • Synchronization problem
  • Less robust -- 1 thread crashing causes entire program to crash
  • No synchronization problem -- Every process has its own memory address space
  • Slower creation of new processes
  • Slower context switch
  • Slower communication with pipes and files

 

In the following code,  

T1:
a) mutex_lock(&m3)
b) mutex_lock(&m5);
c) mutex_lock(&m1);

T2:
d) mutex_lock(&m2)
e)mutex_lock(&m5);
f)mutex_lock(&m4);

T3:
g)mutex_lock(&m4);
h)mutex_lock(&m3);
i)mutex_lock(&m1);

a) give a sequence of steps using the letters at the left of each statement that will cause the threads to get into a deadlok. (Example: T1a, T2d, T3g ... etc)

 

T1:
a)
Context switch ->
.
.
.
b) Blocked
Context switch->

T2:
.
d)
e)
Context switch ->
.
.
f) Blocked

T3:
.
.
.
g)
h) Blocked
Context switch ->
 

b) Draw the graph representation of the deadlock.

  m3->T1 ->m5->T2->m4->T3
         ^                                          |
         |---------------------------------

 

c) Rewrite the code to prevent the deadlock.

T1:
a) mutex_lock(&m1)
b) mutex_lock(&m3);
c) mutex_lock(&m5);

T2:
d) mutex_lock(&m2)
e)mutex_lock(&m4);
f)mutex_lock(&m5);

T3:
g)mutex_lock(&m1);
h)mutex_lock(&m3);
i)mutex_lock(&m4);

 

 

7.Assume that the following code for adding items into an array. addToArray will return the index in the array where the value was added or -1 if the buffer is full. a) What problems do you see can happen if multiple threads try to run it? Rewrite the code to solve the problem.

a) If multiple threads try to run it, the array data structure can get messed up. For example:
First thread reaches the line “array[count] = value”, then context switch happens. Second thread reaches the same line and overwrites array[count]. The first value added is then lost. Also the return count-1; should be
substituted by return tmp; and have tmp be set to count-1 before the mutex_lock.

 


#define MAXCOUNT;
int count = 0;
int array[MAXCOUNT];

int addToArray(int value)
{
  if (count == MAXCOUNT) {
     return -1;
  }

  array[count]=value;

  count = count + 1;

  return count-1;

}

 

New Code
#include <pthread.h>
#define MAXCOUNT;
int count = 0;
int array[MAXCOUNT];
pthread_mutex_t _mutex;    //initialized by pthread_mutex_init()
int addToArray(int value)
{
  pthread_mutex_lock(&_mutex);
  if (count == MAXCOUNT) {
     pthread_mutex_unlock(&mutex);
      return -1;
  }

  array[count]=value;

  count = count + 1;

  int tmp =  count-1;

  pthread_mutex_unlock(&_mutex);

  return tmp;

}                                     

 

Write a program "grepsort arg1 arg2 arg3" that implements the command "grep arg1 | sort  < arg2 >> arg3". The program should not return until the command finishes. "arg1", "arg2", and "arg3"  are passed as arguments to the program. Example of the usage is "grepsort hello infile outfile". This command will print the entries in file infile that contain the string hello and will append the output sorted to file outfile. Do error checking. Notice that the output is appended to arg3.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

const char *usage = "Usage:\tgrepsort arg1 arg2 arg3\n\tgrep arg1 | sort  < arg2 >> arg3\n";
int main(int argc, char **argv)
{
            if (argc != 4)
            {
                        fprintf(stderr, "%s\n", usage);
                        exit(1);
            }
           
            int defaultin = dup(STDIN_FILENO);
            int defaultout = dup(STDOUT_FILENO);
            int pipefd[2];
           
            if (pipe(pipefd) == -1)
            {
                        perror("pipe");
                        exit(1);
            }
           
            int infd = open(argv[2], O_RDONLY);
            if (infd < 0)
            {
                        perror("open in");
                        exit(1);
            }
           
            dup2(infd, STDIN_FILENO);
            close(infd);
           
            dup2(pipefd[1], STDOUT_FILENO);
            close(pipefd[1]);
           
            int ret = fork();
            //child
            if (ret == 0)
            {
                        close(pipefd[0]);
           
                        char *args[3];
                        args[0] = "grep";
                        args[1] = argv[1];
                        args[2] = NULL;
                       
                        execvp(args[0], args);
                        perror("execvp");
                        _exit(1);
            }
            else if (ret < 0)
            {
                        perror("fork");
                        exit(1);
            }
           
            int outfd = open(argv[3], O_WRONLY | O_APPEND | O_CREAT, 0666);
            if (outfd < 0)
            {
                        perror("open out");
                        exit(1);
            }
           
            dup2(outfd, STDOUT_FILENO);
            close(outfd);
           
            dup2(pipefd[0], STDIN_FILENO);
            close(pipefd[0]);
           
            ret = fork();
            if (ret == 0)
            {
                        close(pipefd[1]);
                       
                        char *args[2];
                        args[0] = "sort";
                        args[1] = NULL;
                       
                        execvp(args[0], args);
                        perror("execvp");
                        _exit(1);
            }
            else if (ret < 0)
            {
                        perror("fork");
                        exit(1);
            }
           
            dup2(defaultin, STDIN_FILENO);
            dup2(defaultout, STDOUT_FILENO);
            close(defaultin);
            close(defaultout);
           
            waitpid(ret, 0, 0);
           
            return 0;
}

 

Using C++ and Semaphores write a class SynchronizedStackSemaphores of int values where pop() will block  if the stack is empty and push will block if the stack is full. Write the member variables that you think are necessary. Implement the stack with an array of int's and allocate it dynamically in the constructor. Hint: Use the "Bounded Buffer Problem" with semaphores as an example in your implementation.

#include <synch.h>
#include <pthread.h>

class SynchronizedStackSemaphores {
    // Add your member variables here
    int top;
    int * stack;
    sema_t emptySem, fullSem;
    pthread_mutex_t lock;
    public:
        SynchronizedStackSemaphores(int maxStackSize);
        void push(int val);
        int pop();
};

SynchronizedStackSemaphores::SynchronizedStackSemaphores(int maxStackSize) {
        top = 0;
        stack = new int[maxStackSize];
        sema_init(&emptySem, 0, USYNC_THREAD, NULL);
        sema_init(&fullSem, maxStackSize, USYNC_THREAD, NULL);
        pthread_mutex_init(&lock, NULL);
}

void SynchronizedStackSemaphores::push(int val) {
        sema_wait(&fullSem);
        pthread_mutex_lock(&lock);
        stack[top] = val;
        top++;
        pthread_mutex_unlock(&lock);
        sema_post(&emptySem);
}

int SynchronizedStackSemaphores::pop(){
        sema_wait(&emptySem);
        pthread_mutex_lock(&lock);
        int retval = stack[top-1];
        top--;
        pthread_mutex_unlock(&lock);
        sema_post(&fullSem);
        return retval;
}