0

I'm attempting to create a simple server using the producer/consumer problem, pthreads, and mutexes/condition variables. My server accepts a port number, number of threads to create, how big the buffer should be, and what type of scheduling it should do.

Although I can pass tests in the case where my buffer size is one (my job queue only holds one task), I fail tests where my buffer size is greater than one. I do not know why.

The code is rather long, so I will post what I feel are the more pertinent snippets, but I will post the code in its entirety if asked. Again, this IS NOT EVERYTHING.

Main (includes producer), takes the connector descriptor from client, and passes it to "requestHandle", which in turn takes information from the connfd and stores that into a data structure. It then places the data structure on a "queue" -- in my case, I choose a binary tree, with different sorting techniques (FIFO = First in, first out, SFNF = Shortest file name first, SFF = smallest file first).

There are three pertinent files:

  • server.c holds references to the data structure's root, mutex, and lock vars.
  • request.c saves pertinent info and enqueues/dequeues in different functions
  • request.h holds structure types

What is failing:

When attempting to run the code with a buffer size>1 (i.e., I can place more than one item on the queue), the server appears to be crashing. Here is an example test being run on the server as follows:

class Locks(ServerTest):
   name = "locks"
   description = "many concurrent requests to test locking"
   threads = 8
   buffers = 16
   schedalg = "FIFO"
   num_clients = 20
   loops = 20
   requests = ["/home.html", "/output.cgi?0.3"]
   def many_reqs(self):
      for i in range(self.loops):
         for request in self.requests:
            conn = httplib.HTTPConnection("localhost", self.port, timeout=8)
            if self.quiet_get(conn, request):
              self.client_run(conn)
   def run(self):
       serverProc = self.run_server(threads=self.threads, buffers=self.buffers, schedalg=self.schedalg)
      clients = [threading.Thread(target=self.many_reqs) for i in range(self.num_clients)]
      for client in clients:
         client.start()
      for client in clients:
         client.join()
      serverProc.kill()
      self.done()

Running the test above results in the following (except, you know, printed out a million times):

Client failed with error: timed out
Client failed with error: [Errno 104] Connection reset by peer
unable to send request to server. it may have crashed.

server.c global vars:

 //root of "queue"
 TREE_NODE *root;
 //lock
 pthread_mutex_t *m;
 //Cond. v for queue is full
 pthread_cond_t *full;
 //cond. v for queue is empty
 pthread_cond_t *empty;
 //total buff size
  int buff_sz;
 //num nodes used (<= buff_sz)
 int num_used;
 //schedule_type: 0 = FIFO, 1 = SFNF, 2 = SFF)
 int sched_num;

main in server.c (also producer thread)

int main(int argc, char *argv[])
{
    int listenfd, connfd, port, num_threads, clientlen;
    char *sched_type = malloc(8096);

    struct sockaddr_in clientaddr;

    getargs(&port, &num_threads, &buff_sz, sched_type, argc, argv);

    if (strcmp(sched_type, "FIFO") == 0) sched_num = 0;
    if (strcmp(sched_type, "SFNF") == 0) sched_num = 1;
    if (strcmp(sched_type, "SFF") == 0) sched_num = 2;

//initial set up
root = NULL;

num_used = 0;

m = malloc(sizeof(pthread_mutex_t));
pthread_mutex_init(m, NULL);
full = malloc(sizeof(pthread_cond_t));
pthread_cond_init(full, NULL);
empty = malloc(sizeof(pthread_cond_t));
pthread_cond_init(empty, NULL);


// 
// Create some threads...
//
int i;
void *argum = malloc(sizeof(void *));
for(i = 0; i < num_threads; i++) {

    pthread_t *pthread = malloc(sizeof(pthread_t));
    int err_check = pthread_create (pthread, NULL, thread_handle, argum);

    assert(err_check == 0);
}


listenfd = Open_listenfd(port);
while (1) {
clientlen = sizeof(clientaddr);
connfd = Accept(listenfd, (SA *)&clientaddr, (socklen_t *) &clientlen);

// 
// In general, don't handle the request in the main thread.
// Save the relevant info in a buffer and have one of the worker threads 
// do the work. However, for SFF, you may have to do a little work
// here (e.g., a stat() on the filename) ...
// 

    //PLACE LOCK
    pthread_mutex_lock(m);

    //check condition if queue full, T = wait on full
    while (buff_sz <= num_used) pthread_cond_wait(full, m);

    pthread_mutex_unlock(m);

    requestHandle(connfd, sched_num);

    pthread_mutex_lock(m);
    num_used++;

    //signal empty to wake sleeping threads
    pthread_cond_signal(empty);

   //unlock
    pthread_mutex_unlock(m);

    }

}

consumer thread in server.c:

void *thread_handle(void *ptr){
while(1){

    //place lock
    pthread_mutex_lock(m);

    //check condition if empty, T = wait on empty
    while (num_used < 1) pthread_cond_wait(empty, m);

    //dequeue
     runRequest(sched_num);

    num_used--;

    //signal full
    pthread_cond_signal(full);


    //unlock
    pthread_mutex_unlock(m);

}
return NULL;
}//end thread_handle

enqueue function in request.c

 // handle a request
void requestHandle(int fd, int sched_num){ // enqueue and save stuff

 struct req_info *job = malloc(sizeof(struct req_info));

 job->fd = fd;

 struct stat sbuf;
 char buf[MAXLINE], method[MAXLINE], uri[MAXLINE], version[MAXLINE];
 char filename[MAXLINE], cgiargs[MAXLINE];
 rio_t rio;

 Rio_readinitb(&rio, fd);
 Rio_readlineb(&rio, buf, MAXLINE);
 sscanf(buf, "%s %s %s", method, uri, version);

 printf("%s %s %s\n", method, uri, version);
 job->method = strdup(method);
 job->uri = strdup(uri);
 job->version = strdup(version);

  if (strcasecmp(method, "GET")) {
  requestError(fd, method, "501", "Not Implemented", "CS537 Server does not implement this method");
  return;
 }
  requestReadhdrs(&rio);
  job->stat_rio = rio;

  job->is_static = requestParseURI(uri, filename, cgiargs);

  job->filename = strdup(filename);
  job->arguments = strdup(cgiargs);

  if (stat(filename, &sbuf) < 0) {
     requestError(fd, filename, "404", "Not found", "CS537 Server could not find this file");
     return;
  }

  job->sbuf = sbuf;
  job->filename_len = (int)(strlen(filename));

   struct stat *file_stat = malloc(sizeof(struct stat));
   assert((stat(job->filename, file_stat)) == 0);
  //save file size!
   job->size = (int)(file_stat->st_size);

 //create new node
 TREE_NODE *temp = malloc(sizeof(TREE_NODE));
 temp->job_node = job;
 temp->left = NULL;
 temp->right = NULL;

 //now add node!
 int done = 0;
 TREE_NODE *curr = root;
 TREE_NODE *prev = NULL;

  if (root == NULL){
     root = temp;
     done = 1;
  }

  //FIFO: Add to right
  else if (sched_num == 0){

    while (done != 1){

        if (curr->right == NULL){
            curr->right = temp;
            done = 1;
        }
        curr = curr->right;

    }//end while

 }//end FIFO

 //SFNF: Shortest file name first
 else if (sched_num == 1){

    while (done != 1){

        if (curr == NULL){
            curr = temp;
            if ((prev->job_node)->filename_len <= (temp->job_node)->filename_len) prev->right = temp;
            else prev->left = temp;
            done = 1;
        }

        else if ((curr->job_node)->filename_len <= (temp->job_node)->filename_len){
            prev = curr;
            curr = curr->right;
        }

        else {
            prev = curr;
            curr = curr->left;
        }
    }//end while

 }//end SFNF else if


 //SFF: Smallest file first
 else {

     while (done != 1){

        if (curr == NULL){
            curr = temp;
            if ((prev->job_node)->size <= (temp->job_node)->size) prev->right = temp;
            else prev->left = temp;
            done = 1;
        }

        else if ((curr->job_node)->size <= (temp->job_node)->size){
            prev = curr;
            curr = curr->right;
        }

        else {
            prev = curr;
            curr = curr->left;
        }
    }//end while


  }//end else

}

dequeue function in request.c

//FOR THREADS
void runRequest(int sched_num){ // dequeue and run


   struct req_info *job;

   TREE_NODE *prev = NULL;
   TREE_NODE *child = root;
   TREE_NODE *temp = root;
   int done = 0;

  //FIFO: take root
  if (sched_num == 0){
   if(root == NULL){
       printf("SHIT HAS HIT THE FAN\n");
   }
   root = root->right;
   done = 1;
  }

   //SFNF & SFF: take node all the way left
   else {

    while (done != 1){

        if (child->left == NULL){
            if (child->right == NULL){
                temp = child;
                prev->left = NULL;
                done = 1;
            }
            else {
                temp = child;
                prev->left = child->right;
                done = 1;
            }

        }//end if

    prev = child;
    child = child->left;

    }//end SFNF/SFF while
 }//end else

 job = temp->job_node;


 if (job->is_static) {
    if (!(S_ISREG((job->sbuf).st_mode)) || !(S_IRUSR & (job->sbuf).st_mode)) {
       requestError(job->fd, job->filename, "403", "Forbidden", "CS537 Server could not read this file");
      return;
   }
   requestServeStatic(job->fd, job->filename, (job->sbuf).st_size);
 } else {
    if (!(S_ISREG((job->sbuf).st_mode)) || !(S_IXUSR & (job->sbuf).st_mode)) {
      requestError(job->fd, job->filename, "403", "Forbidden", "CS537 Server could not run this CGI program");
      return;
   }

   requestServeDynamic(job->fd, job->filename, job->arguments);

   Close(job->fd);
 }


}

Last, but not least, the header for request.h:

struct req_info {

  char * method;
  char * uri;
  char * version;
  int fd;
  int size;
  char * filename;
  int filename_len;
  char * arguments;
  int is_static;
  rio_t stat_rio;
  struct stat sbuf;

} req_info;


 typedef struct tree_node TREE_NODE;

 struct tree_node {
 struct req_info *job_node;
 TREE_NODE *left, *right;
 };

 void requestHandle(int fd, int shed_num);
 void runRequest(int sched_num);
katiea
  • 223
  • 1
  • 5
  • 19
  • Well, one obvious issue is your sched_type variable in main, it's at least one byte too small. – perh Apr 24 '14 at 08:32
  • Compile the server with symbols (option `-g` for gcc) and run it using a debugger. To debug memory issues run it under Valgrind (http://valgrind.com). – alk Apr 24 '14 at 08:39
  • Sorry - left over from old code. changed to malloc(8096). Still having the same problems though... *EDITED* – katiea Apr 24 '14 at 08:40
  • @alk - did that already. Honestly not seeing anything major... I mean, there's a crap ton of mem leaks, but that's not the issue right now. The amount of memory used in any one of my tests does not in any way come close to exceeding the virtual memory. – katiea Apr 24 '14 at 08:42
  • A detail: `clientlen` should be `socklen_t` not `int`. – alk Apr 24 '14 at 08:46
  • Not only, but also to debug code, error checking of relevant system calls is essential. In this context I'd consider a system call relevant if it returns value(s) used later in the code. – alk Apr 24 '14 at 08:48
  • "*... did that already*" So, in which line does the server crash? – alk Apr 24 '14 at 08:49
  • What about my buffer? I think as of right now, it only needs to accept one item in the buffer before running a thread to do the job. That's simply FIFO. Maybe the reason my SFF and SFNF scheduling is failing is because I'm not actually buffering. There is something called the "select" method in c, but I don't really understand it... Any pointers? – katiea Apr 24 '14 at 15:58
  • @alk: currently, for other queueing methods besides FIFO, it's returning jobs in the wrong order. So say I get jobs 3 1 and 2, and based on size, I should return 1 2 3. Then I'm returning something like 2 3 1... So I was thinking it was my enqueue and dequeue functions, but beside one line of code in those sections, I found nothing amiss... – katiea Apr 24 '14 at 16:00
  • In which source line does the server code crash? – alk Apr 24 '14 at 16:03
  • It is no longer crashing - it is returning in the wrong order, and because this is supposed to be a producer/consumer model where the producer is a buffer, I feel I should be waiting to signal my child threads. Hence, my attempt at using "select" - but I don't know how to implement it. – katiea Apr 24 '14 at 16:09

0 Answers0