/** * An example multithreaded program * * by Ron Mak * Department of Computer Science * San Jose State University * * WARNING: Contains a subtle threading error which can cause a deadlock! * Can you find and fix it? */ #include #include #include #include #include #include #include #include using namespace std; const int ID_BASE = 101; const int CHAIR_COUNT = 3; const int STUDENT_COUNT = 25; const int MAX_MEETING_DURATION = 5; const int OFFICE_HOUR_DURATION = 60; int chairs[CHAIR_COUNT]; // circular buffer of chairs pthread_mutex_t chair_mutex; // protects chairs and wait count pthread_mutex_t print_mutex; // protects printing pthread_mutex_t semaphore_mutex; // used by the semaphore pthread_cond_t student_arrived_semaphore; // professor waits on this semaphore struct itimerval prof_timer; // professor's office hour timer time_t start_time; int in = 0, out = 0; // chair indexes int meeting_id = 0; // id of the student meeting with the professor int arrivals_count = 0; int waitCount = 0; int leaves_count = 0; int meetings_count = 0; int parfore_count = 0; bool first_print = true; bool times_up = false; // true when office hour is over /** * The professor thread. * @param param the thread parameter. */ void *professor(void *param); /** * The professor meets a student * or works on her ParFore language. */ void professor_meets_student(); /** * The student thread. * @param param the thread parameter. */ void *student(void *param); /** * A student arrives. * @param id the ID of the student. */ void student_arrives(int id); /** * Print a line for each event: * elapsed time * ID of student meeting with the professor * IDs of students waiting in the chairs * what event occurred * @param event the event. */ void print(string event); /** * Timer signal handler. */ void timer_handler(int signal); /** * The main. */ int main() { int student_ids[STUDENT_COUNT]; int professor_id = 0; // Initialize the mutexes and the semaphore. pthread_mutex_init(&chair_mutex, NULL); pthread_mutex_init(&print_mutex, NULL); pthread_mutex_init(&semaphore_mutex, NULL); pthread_cond_init(&student_arrived_semaphore, NULL); srand(time(0)); time(&start_time); // Create the professor thread. pthread_t professorThreadId; pthread_attr_t profAttr; pthread_attr_init(&profAttr); pthread_create(&professorThreadId, &profAttr, professor, &professor_id); // Create the student threads. for (int i = 0; i < STUDENT_COUNT; i++) { student_ids[i] = ID_BASE + i; pthread_t studentThreadId; pthread_attr_t studentAttr; pthread_attr_init(&studentAttr); pthread_create(&studentThreadId, &studentAttr, student, &student_ids[i]); } // Set the timer signal handler. signal(SIGALRM, timer_handler); // Wait for the professor to complete the office hour. pthread_join(professorThreadId, NULL); // Remaining waiting students leave. meeting_id = 0; while (waitCount-- > 0) { int student_id = chairs[out]; out = (out+1)%CHAIR_COUNT; leaves_count++; char event[80]; sprintf(event, "Student %d leaves", student_id); print(event); } // Final statistics. printf("\n"); printf("%5d students arrived\n", arrivals_count); printf("%5d students met with Prof. Fore\n", meetings_count); printf("%5d students left without meeting\n", leaves_count); printf("%5d times Prof. Fore worked on ParFore\n", parfore_count); return 0; } void *professor(void *param) { print("Professor opens her door"); // Set the timer for for office hour duration. prof_timer.it_value.tv_sec = OFFICE_HOUR_DURATION; setitimer(ITIMER_REAL, &prof_timer, NULL); // Lock the semaphore mutex. pthread_mutex_lock(&semaphore_mutex); // Meet students until the office hour is over. do { professor_meets_student(); } while (!times_up); print("Professor closes her door"); // Release the semaphore mutex. pthread_mutex_unlock(&semaphore_mutex); return NULL; } void professor_meets_student() { // No student waiting, so work on ParFore language. if (waitCount == 0) { print("Professor works on ParFore"); parfore_count++; // Wait for a student to arrive. pthread_cond_wait(&student_arrived_semaphore, &semaphore_mutex); } if (!times_up) { // Acquire the mutex lock to protect the chairs and the wait count. pthread_mutex_lock(&chair_mutex); // Critical region: Remove a student from a chair. meeting_id = chairs[out]; out = (out+1)%CHAIR_COUNT; waitCount--; char event[80]; sprintf(event, "Professor meets with student %d", meeting_id); print(event); // Release the mutex lock. pthread_mutex_unlock(&chair_mutex); // Meet with the student. sleep(rand()%MAX_MEETING_DURATION + 1); meetings_count++; sprintf(event, "Professor finishes with student %d", meeting_id); meeting_id = 0; print(event); } } void *student(void *param) { int id = *((int *) param); // Students will arrive at random times during the office hour. sleep(rand()%OFFICE_HOUR_DURATION); student_arrives(id); return NULL; } void student_arrives(int id) { char event[80]; arrivals_count++; // Acquire the mutex lock to protect the chairs and the wait count. pthread_mutex_lock(&chair_mutex); if (waitCount < CHAIR_COUNT) { // Seat a student into a chair. chairs[in] = id; in = (in+1)%CHAIR_COUNT; waitCount++; sprintf(event, "Student %d arrives and waits", id); print(event); // Signal the professor that a student has arrived. pthread_cond_signal(&student_arrived_semaphore); // Release the mutex lock. pthread_mutex_unlock(&chair_mutex); } else { // Release the mutex lock. pthread_mutex_unlock(&chair_mutex); leaves_count++; sprintf(event, "Student %d arrives and leaves", id); print(event); } } void print(string event) { time_t now; time(&now); double elapsed = difftime(now, start_time); int min = 0; int sec = (int) elapsed; if (sec >= 60) { min++; sec -= 60; } // Acquire the mutex lock to protect the printing. pthread_mutex_lock(&print_mutex); if (first_print) { printf("TIME | MEETING | WAITING | EVENT\n"); first_print = false; } // Elapsed time. printf("%1d:%02d | ", min, sec); // Who's meeting with the professor. if (meeting_id > 0) printf("%5d |", meeting_id); else printf(" |"); int i = out; int j = waitCount; int k = 0; // Who's waiting in the chairs. while (j-- > 0) { printf("%4d", chairs[i]); i = (i+1)%CHAIR_COUNT; k++; } // What event occurred. while (k++ < CHAIR_COUNT) printf(" "); printf(" | %s\n", event.c_str()); // Release the mutex lock. pthread_mutex_unlock(&print_mutex); } void timer_handler(int signal) { times_up = true; // office hour is over }