CS360 Final Exam - May 6, 2013 - James S. Plank

Answers and Explanations

Question 1

You need two threads -- one to read from standard input and write to the socket, and one to read from the socket and write to standard output. The simplest thing to do is to write a procedure that reads from one FILE * and writes to another. Then you simply fork a thread with that procedure, having set up the arguments to have the correct FILE *'s. Here's my answer, in q1.c:

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include "sockettome.h"

typedef struct {
  FILE *fin;
  FILE *fout;
} Arg;

void *reader(void *arg)
{
  Arg *a;
  char buf[1000];

  a = (Arg *) arg;
  while (fgets(buf, 1000, a->fin) != NULL) {
    fputs(buf, a->fout);
    fflush(a->fout);
  }
  return NULL;
}
  
main(int argc, char **argv)
{
  /* Not error checking the command line */
  int fd;
  pthread_t tid1, tid2;
  Arg a1, a2;

  fd = request_connection(argv[1], atoi(argv[2]));
  a1.fin = stdin;
  a1.fout = fdopen(fd, "w");
  a2.fin = fdopen(fd, "r");
  a2.fout = stdout;

  pthread_create(&tid1, NULL, reader, (void *) &a1);
  pthread_create(&tid2, NULL, reader, (void *) &a2);
  pthread_exit(NULL);
}

Obviously, there are a few ways to do this -- you could have simply had the main thread call the procedure. If so, you needed to be careful. For example, when standard input is closed, if you were reading it in the main thread, and had the main thread exit, you may actually miss some output from the socket.

Grading:

This problem was worth 20 points, and my grading works as follows. Your solutions tended to fall into one of four categories: Once you started in a category, I adjusted your score up or down for various things, like forgetting fflush(), mis-managing types, serving a socket, or general confusion.


Question 2

A correct answer is in this PDF file. Here are the things that you should have shown me:

Grading:

20 points:


Question 3

Paragraph: The variable b_blocked is a state variable that says whether thread B is blocked. It starts of by saying that thread B isn't blocked. In safe_modifiy_state(), thread A will lock the mutex and then test to see if B is blocked. If not, then it will block it self on condition variable a_cond. When it wakes up, it should test to see if B is blocked, but I think the code will work fine even if not, because B should be blocked when A awakens. It then calls modify_data(). When that's done, it sets the state variable to indicate that B is unblocked, wakes up B and returns.

In BM(), B needs to lock the mutex, set its state to blocked (by setting b_blocked to 1, signal A and block on b_cond. When it wakes up, A is done and b_blocked should be set to 0, so it returns. I actually think it's incorrect to have B set b_blocked to zero -- think about that. I didn't take off for it.

Why do you need b_blocked anyway? It depends on which thread calls its procedure first. For example, suppose you simply have A block without testing b_block. Then you're going to have deadlock if B calls BM() before A calls safe_modify_data().

Code: q3.c

void safe_modify_data(struct Data *D, Info *I)
{
  pthread_mutex_lock(&(I->lock));
  while (!I->b_blocked) pthread_cond_wait(&(I->a_cond), &(I->lock));
  modify_data(D);
  I->b_blocked = 0;
  pthread_cond_signal(&(I->b_cond));
  pthread_mutex_unlock(&(I->lock));
}

void BM(Info *I)
{
  pthread_mutex_lock(&(I->lock));
  I->b_blocked = 1;
  pthread_cond_signal(&(I->a_cond));
  pthread_cond_wait(&(I->b_cond), &(I->lock));
  pthread_mutex_unlock(&(I->lock));
}

Grading:

Again 20 points. The paragraph was 8 points, and you needed to mention the purposes of each of the pieces of data (2 points each). The procedures were 6 points each.


Question 4

This is a nuts-and-bolts fork/dup/exec question. You need to open one pipe, and have that go from kim's standard out to khloe's standard in. However, you don't close it in your parent process. When khloe exits, you then have the pipe also go to kourtney's standard in. You also do this with the socket's output -- you keep it open so that both khloe and kourtney may use it. Here's the code, in q4.c:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "sockettome.h"

main()
{
  int s, fd, dummy;
  int p[2];

  s = serve_socket(9021);
  fd = accept_connection(s);

  close(s);  

  pipe(p);
  
  if (fork() == 0) {
    dup2(fd, 0);
    dup2(p[1], 1);
    close(p[0]);
    close(p[1]);
    close(fd);
    execlp("kim", "kim", NULL);
    exit(1);
  }

  close(p[1]); /* No reason to keep p[1] around any more */
    
  if (fork() == 0) {
    dup2(p[0], 0);
    dup2(fd, 1);
    close(p[0]);
    close(fd);
    execlp("khloe", "khloe", NULL);
    exit(1);
  }
  
  (void) wait(&dummy);  /* Wait for khloe.  If it's Kim, Khloe's dying pretty soon so 
                           don't worry about it.  That wouldn't be true had you not
                           closed p[1]. */
    
  if (fork() == 0) {
    dup2(p[0], 0);
    dup2(fd, 1);
    close(p[0]);
    close(fd);
    execlp("kourtney", "kourtney", NULL);
    exit(1);
  }
  
  close(fd);
  close(p[0]);
  (void) wait(&dummy);
  (void) wait(&dummy);
  exit(0);
}

We can test it -- here are kim, khloe and kourtney:
kim.c

#include <stdio.h>
#include <stdlib.h>

main()
{
  int l;
  char c;

  while (read(0, &c, 1) == 1) {
    write(1, &c, 1);
    if (c == '\n') write(1, "Gucci\n", 6);
  }
}

khloe.c

#include <stdio.h>
#include <stdlib.h>

main()
{
  int l;
  char c;

  l = 0;
  while (read(0, &c, 1) == 1) {
    if (c >= 'a' && c <= 'z') c += ('A' - 'a');
    write(1, &c, 1);
    if (c == '\n') l++;
    if (l == 10) exit(0);
  }
}

kourtney.c

#include <stdio.h>
#include <stdlib.h>

main()
{
  int l;
  char c;

  while (read(0, &c, 1) == 1) {
    if (c >= 'a' && c <= 'z') c += ('A' - 'a');
    write(1, &c, 1);
  }
}

When we run it, we can throw 10 random lines at it. Kim will write "Gucci\n" after every line. khloe will convert lowercase to uppercase, but exits after the first ten lines. kourtney takes over and finishes the job:

UNIX> telnet localhost 9021
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
Julia Doghouse
Elizabeth Tyrannic
Ava Effloresce MD
Hunter Bruise
Matthew Phonic
Jordan
Tyler Ontario
Evan Synonym
Isaac Proponent
Lucas Siesta
JULIA DOGHOUSE
GUCCI
ELIZABETH TYRANNIC
GUCCI
AVA EFFLORESCE MD
GUCCI
HUNTER BRUISE
GUCCI
MATTHEW PHONIC
GUCCI
JORDAN
GUCCI
TYLER ONTARIO
GUCCI
EVAN SYNONYM
GUCCI
ISAAC PROPONENT
GUCCI
LUCAS SIESTA
GUCCI
^]
telnet> quit
Connection closed.
UNIX> 

Grading