CS360 Final Exam. Answer to Question 4

December 13, 1997

15 points


Part 1

In my code, I keep three global variables -- a rb-tree of the top 500 scores, the number of high scores in the rb-tree, and a mutex called lock. I have one thread that serves the socket and calls accept_connection(). When it gets a connection, it forks off a thread to service the connection. This is the subroutine service_connection(). In service_connection(), I first read 34 bytes from the connection. This is the user's high score string. I then see if it should go into the tree -- if there are less than 500 scores in the tree, I simply insert it there. If there are exactly 500 scores, then I check to see if the user's score is greater than the smallest score. If so, then I delete the smallest score and insert the user's score. That way the tree holds a maximum of 500 scores. I then traverse the tree and send over each score. I hold the mutex during this entire time so that the tree doesn't get modified while I'm printing it or modifying it.

Note that in the rb-tree, you don't need a struct. You can simply store the string in the val field, and the atoi() value of the string in the key field.

#include < stdio.h >
#include < pthread.h >
#include "rb.h"
#include "socketfun.h"

Rb_node high_scores;
int nhs;
pthread_mutex_t lock;

void *service_connection(void *v)
{
  int *fdp, fd;
  char *s, s2[200];
  int i, j, k;
  Rb_node tmp;

  s = (char *) malloc(34);

  fdp = (int *) v;
  fd = *fdp;

  for (i = 0; i < 34; i++) {
    j = read(fd, s+i, 34-i);
    if (j == 0) { close(fd); return NULL; }
    i += j;
  }

  /* Insert the string into the tree.  If there are now 501 strings, delete
     the one with the lowest score */

  pthread_mutex_lock(&lock);

  rb_inserti(high_scores, atoi(s), s);
  nhs++;
  if (nhs == 501) {
    tmp = rb_first(high_scores);
    free(tmp->v.val);
    rb_delete_node(tmp);
  }

  /* Traverse the tree backwards so that the highest score is first */
  i = 1;
  for (tmp = rb_prev(high_scores); tmp != high_scores; tmp = rb_prev(tmp)) {
    sprintf(s2, "%3d -- %s", i, tmp->v.val);
    j = 0;
    k = strlen(s2);
    while (j < k) { j += write(fd, s2+j, k-j); }
    i++;
  }
  pthread_mutex_unlock(&lock);
  close(fd);
  return NULL;
}

main()
{
  int sock, fd;
  pthread_t *t;
  int *fdp;

  high_scores = make_rb();
  nhs = 0;
  pthread_mutex_init(&lock, NULL);

  sock = serve_socket("games.cs.utk.edu", 8000);

  while(1) {
    fd = accept_connection(sock);
    t = (pthread_t *) malloc(sizeof(pthread_t));
    fdp = (int *) malloc(sizeof(int));
    *fdp = fd;
    pthread_create(t, NULL, service_connection, fdp);
  }
}

Part 2

Locking the mutex while you send the high score tree to the user is a bad idea, because sending all those bytes along the socket takes a lot of time, and shuts out other connections from being serviced. A nice solution to this is to instead create the string that you'll send to the user while you hold the lock, and then release the lock and send the string to the user. That way, you are not holding the lock during the time-consuming part of the operation (sending the string across the socket). Here is some code that does this. Note that we could be more efficient by keeping track of the length of all and not calling strcat.
void *service_connection(void *v)
{
  int *fdp, fd;
  char *s, s2[200];
  int i, j, k;
  Rb_node tmp;
  char all[19000];  /* 38*500 */

  s = (char *) malloc(34);

  fdp = (int *) v;
  fd = *fdp;

  for (i = 0; i < 34; i++) {
    j = read(fd, s+i, 34-i);
    if (j == 0) { close(fd); return NULL; }
    i += j;
  }
  d = make_dl();

  pthread_mutex_lock(&lock);

  rb_inserti(high_scores, atoi(s), s);
  nhs++;
  if (nhs == 501) {
    tmp = rb_first(high_scores);
    free(tmp->v.val);
    rb_delete_node(tmp);
  }

  i = 1;
  all[0] = '\0';
  for (tmp = rb_prev(high_scores); tmp != high_scores; tmp = rb_prev(tmp)) {
    sprintf(s2, "%3d -- %s", i, tmp->v.val);  /* note this is 38 bytes */
    strcat(all, s2);
    i++;
  }
  pthread_mutex_unlock(&lock);
  
  /* Write all */

  j = 0;
  k = strlen(all);
  while (j < k) { j += write(fd, all+j, k-j); }
  }
  close(fd);
  return NULL;
}