You derive the answers from that:
Free list chunks: ----------------- 1. [ 0x10758, 0x10 (16) ] 2. [ 0x10680, 0x38 (56) ] 3. [ 0x10638, 0x38 (56) ] 4. [ 0x106d0, 0x50 (80) ] 5. [ 0x10790, 0x10 (16) ] Allocated chunks: 1. [ 0x10670, 0x10 (16) ] 2. [ 0x106b8, 0x18 (24) ] 3. [ 0x10720, 0x20 (32) ] 4. [ 0x10740, 0x18 (24) ] 5. [ 0x10768, 0x28 (40) ]Grading: Each address is worth 1 point, as is each chunk size.
After 3: PCM *lock; PCT **conds; After 14: p->lock = new_mutex(); p->conds = (PCT **) malloc(sizeof(PCT *)*p->num); for (i = 0; i < p->num; i++) p->conds[i] = new_cond(); |
In pickup(), you need to hold the mutex for the duration of the procedure, and instead of sleeping, you need to block on your condition variable:
After 25: pthread_mutex_lock(p->lock); After 33: pthread_mutex_unlock(p->lock); After 28: pthread_cond_wait(p->conds[i], p->lock); |
Finally, in putdown(), you also need to hold the lock for the duration of the procedure, and you need to signal the two philosophers on either side after you have dropped the chopsticks:
After 38: pthread_mutex_lock(p->lock); After 43: pthread_cond_signal(p->conds[(i+1)%p->num]); pthread_cond_signal(p->conds[(i+p->num-1)%p->num]); pthread_mutex_unlock(p->lock); |
Grading: 20 points. You started with 20 if you had the right structure (one lock, one condition variable per philosopher). You started with 12 if you only had one condition variable. I then took off points for various things.
Most definitely, this was the most time consuming question on the exam. Let's break it into its parts:
#include <stdio.h> #include <stdlib.h> #include <pthread.h> #include <unistd.h> #include <string.h> #include "sockettome.h" /* I've made this #define so that you can test it on smaller intervals. */ #define TIME (3600) |
/* Global variables: The buffer and the mutex. */ char BUFFER[501]; pthread_mutex_t lock; |
/* The sigpipe handler simply re-registers itself as a signal handler, and then ignores SIGPIPE. The standard I/O library will catch closed socket connections. */ void sigpipe_handler(int dummy) { signal(SIGPIPE, sigpipe_handler); return; } |
/* The idea thread opens MBI.txt, and then reads a line from it using fgets every TIME seconds. It protects the fgets call with a mutex so that there are no race conditions. It exits when MBI.txt has nothing else to read. */ void *idea(void *arg) { FILE *f; f = fopen("MBI.txt", "r"); if (f == NULL) { perror("MBI.txt"); exit(1); } while (1) { pthread_mutex_lock(&lock); if (fgets(BUFFER, 500, f) == NULL) { pthread_mutex_unlock(&lock); return NULL; } pthread_mutex_unlock(&lock); sleep(TIME); } } |
/* The connection thread assumes that it is passed a FILE *, which has been created by the main thread using fdopen() on a socket connection. Every TIME seconds it copies BUFFER to a local buffer, and then tries to write it to the socket connection. If either fputs() or fflush() report an error, then the thread exits gracefully. Each thread uses its own buffer so that it is not holding the mutex during the write to the socket. */ void *connection(void *arg) { FILE *f; char buffer[501]; f = (FILE *) arg; while (1) { pthread_mutex_lock(&lock); strcpy(buffer, BUFFER); pthread_mutex_unlock(&lock); if (fputs(buffer, f) == EOF) { fclose(f); return NULL; } if (fflush(f) == EOF) { fclose(f); return NULL; } sleep(TIME); } } |
/* Finally, the main thread sets up the socket and the mutex, and it creates the idea thread. Then it accepts socket connections, creating FILE *'s for each connection using fdopen(), and then creating a thread to handle the connection. It calls pthread_detach() so that when a thread dies, its state is cleaned up. */ int main() { int s, fd; FILE *f; pthread_t tid; signal(SIGPIPE, sigpipe_handler); s = serve_socket(30602); pthread_mutex_init(&lock, NULL); pthread_create(&tid, NULL, idea, NULL); pthread_detach(tid); while (1) { fd = accept_connection(s); f = fdopen(fd, "w"); pthread_create(&tid, NULL, connection, (void *) f); pthread_detach(tid); } } |