/** * An example multithreaded program that simulates * a professor meeting with students during her office hour. * * In this simulation, 1 realtime second simulates 1 minute. * * by Ron Mak * Department of Computer Engineering * 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 #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 = 20; deque chairs; vector student_threads; mutex chair_mutex; mutex print_mutex; condition_variable condition_student_arrived; struct itimerval office_hour_timer; time_t start_time; int meeting_id = 0; // id of the student meeting with the professor int arrivals_count = 0; int wait_count = 0; int leaves_count = 0; int meetings_count = 0; int parfore_count = 0; bool first_print = true; // becomes false after printing the first event bool times_up = false; // becomes true when the office hour is over /** * The professor thread. */ void professor(); /** * The professor meets a student * or works on her new book. */ void professor_meets_student(); /** * The student thread. * @param id the student's id. */ void student(int id); /** * A student arrives. * @param id the student's id. */ 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() { srand(time(0)); time(&start_time); // Start the professor thread. thread professor_thread(professor); // Start the student threads. student_threads.reserve(STUDENT_COUNT); for (int i = 0; i < STUDENT_COUNT; i++) { student_threads.push_back(thread(student, ID_BASE + i)); } // Set the timer signal handler. signal(SIGALRM, timer_handler); // Set the timer for the office hour duration. office_hour_timer.it_value.tv_sec = OFFICE_HOUR_DURATION; setitimer(ITIMER_REAL, &office_hour_timer, NULL); // Wait for the professor to complete the office hour. professor_thread.join(); // Remaining waiting students leave. meeting_id = 0; while (wait_count-- > 0) { int student_id = chairs.front(); chairs.pop_front(); leaves_count++; char event[80]; sprintf(event, "Student %d leaves", student_id); print(event); } // Detach from the student threads. for (auto& st : student_threads) st.join(); // 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 her book\n", parfore_count); return 0; } void professor() { print("Professor opens her door"); // Meet students until the office hour is over. do { professor_meets_student(); } while (!times_up); print("Professor closes her door"); } void professor_meets_student() { // No student waiting, so work on ParFore language. if (wait_count == 0) { print("Professor works on her new book"); parfore_count++; // Lock the chair mutex to protect the chairs and the wait count. unique_lock lock(chair_mutex); // Wait for a student to arrive. condition_student_arrived.wait(lock, [] { return chairs.size() > 0; }); } if (!times_up) { // Critical region: Remove a student from a chair. meeting_id = chairs.front(); chairs.pop_front(); wait_count--; char event[80]; sprintf(event, "Professor meets with student %d", meeting_id); print(event); // Release the mutex lock. chair_mutex.unlock(); // Meet with the student. this_thread::sleep_for(std::chrono::seconds( rand()%MAX_MEETING_DURATION + 1)); meetings_count++; sprintf(event, "Professor finishes with student %d", meeting_id); print(event); } } void student(int id) { // Students will arrive at random times during the office hour. this_thread::sleep_for(std::chrono::seconds(rand()%OFFICE_HOUR_DURATION)); student_arrives(id); } void student_arrives(int id) { char event[80]; arrivals_count++; // Lock the chair mutex to protect the chairs and the wait count. unique_lock lock(chair_mutex); if (wait_count < CHAIR_COUNT) { // Seat a student into a chair. chairs.push_back(id); wait_count++; sprintf(event, "Student %d arrives and waits", id); print(event); // Notify the professor and release the mutex lock. condition_student_arrived.notify_one(); lock.unlock(); } else { // Release the mutex lock. lock.unlock(); 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. unique_lock 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 k = 0; // Who's waiting in the chairs. for (auto it = chairs.begin(); it != chairs.end(); it++) { printf("%4d", *it); k++; } // What event occurred. while (k++ < CHAIR_COUNT) printf(" "); printf(" | %s\n", event.c_str()); // Release the print mutex lock. lock.unlock(); } void timer_handler(int signal) { times_up = true; // office hour is over }