Title: Seeking Tutor Problem
Course code: CSE-325
Course title: Operating System
Submission deadline: 09/6/2024
Submission date: 09/6/2024
Submitted to–
Md. Mafiul Hasan Matin
Lecturer
Department of Computer Science and Engineering
East West University
Submitted by–
Mohammad Akibul Hasan Arman
Id – 2020-2-60-079
Mahim Chowdhury Katha
Id - 2020-2-60-181
Shafkat Ahmed Upol
Id – 2022-1-60-216
Sec-05
Session- Spring 2024
Introduction:
In modern educational settings, providing timely assistance to students can be challenging due to
limited resources. The Seeking Tutor Problem models a scenario where students need help from
tutors, but due to limited availability of tutors and seating capacity in the waiting area, students must
manage their time between working independently and waiting for assistance. This problem
demonstrates key concepts in resource management and synchronization, which are crucial in both
academic and real-world environments.
Problem Description:
In a tutoring center, students require assistance from tutors to solve programming or coursework
problems. However, the number of tutors is limited, and there are only a few chairs in the waiting
area for students who need help. This scenario requires a system where students alternately work
independently and seek help from available tutors, following a set of rules:
➢ Students: Multiple students need help but must first work on their own problems. After a
period of programming, they seek assistance from tutors.
➢ Tutors: Tutors are limited and can help only one student at a time. They become available
only after completing a help session.
➢ Chairs: The waiting area has a limited number of chairs. Students wait in these chairs if they
cannot immediately get a tutor’s help. If all chairs are occupied, students must go back to
programming and try again later.
Objectives:
➢ Resource Management: Efficiently manage the limited number of chairs and tutors to minimize wait
times and maximize tutor utilization.
➢ Synchronization: Ensure that students and tutors operate without conflicts, particularly when
accessing shared resources like chairs and the waiting queue.
System Components
1. Students:
o Begin by programming (working on tasks independently).
o Periodically seek help from a tutor after a random duration of programming.
o Check for available chairs in the waiting area. If a chair is available, they sit and wait for a tutor;
otherwise, they resume programming.
o Wait for a tutor to become available if seated in a chair.
2. Tutors:
o Wait for a signal that a student needs help.
o Pick the next student in the queue to provide assistance.
o Help the student for a random duration before becoming available again.
3. Coordinator:
o Manages the interaction between students and tutors.
o Ensures that students are queued correctly when seeking help.
o Alerts tutors when students are waiting.
Code
#include <pthread.h> //POSIX thread (pthread) functions for multithreading
#include <semaphore.h> //Semaphore operations
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h> //Sleep function for timing
#include <time.h> // for random sleep duration
// Define semaphores and mutex
sem_t students_sem; // For signaling between students and tutors
sem_t tutors_sem; // For signaling between coordinator and tutors
pthread_mutex_t chairs_mutex; // Protects access to the count of waiting students
and chairs
pthread_mutex_t queue_mutex; // Protects access to the student queue
int waiting_students = 0; // Keeps track of the number of students waiting for help
int NUM_CHAIRS; // The number of chairs in the waiting area, to be set by
user input
int NUM_ITERATIONS; // The number of iterations each student and tutor will
run, to be set by user input
// Define student structure and queue
typedef struct
{
int id;
} student_t; // A structure representing a student with an ID
student_t *student_queue; // An array that will act as a circular queue for waiting
students
int queue_front = 0;
int queue_rear = 0;
// Indices for the front and rear of the queue
// Function prototypes
void *student_thread(void *arg); // Function executed by student threads
void *tutor_thread(void *arg); // Function executed by tutor threads
void *coordinator_thread(void *arg); // Function executed by the coordinator
thread
void enqueue_student(student_t student); // Adds a student to the queue
student_t dequeue_student(); // Removes a student from the queue
void enqueue_student(student_t student)
{
student_queue[queue_rear] = student; // Adds the student at the
queue_rear position
queue_rear = (queue_rear + 1) % NUM_CHAIRS; // pdates the queue_rear index
(wraps around using modulo)
waiting_students++; // Increments the waiting_students
count
}
student_t dequeue_student()
{
student_t student = student_queue[queue_front]; // Retrieves the student at the
queue_front position
queue_front = (queue_front + 1) % NUM_CHAIRS; // Updates the queue_front index
(wraps around using modulo)
waiting_students--; // Decrements the
waiting_students count
return student;
}
void *student_thread(void *arg)
{
int id = *(int *)arg;
/* Extracts the student's ID from the argument.Simulates programming for a
random duration (1-5 seconds) within each iteration.*/
for (int i = 0; i < NUM_ITERATIONS; i++)
{
printf("Student %d is programming.\n", id);
sleep(rand() % 5 + 1); // Simulate programming
pthread_mutex_lock(&chairs_mutex); // Locks chairs_mutex to check the
availability of chairs.
if (waiting_students < NUM_CHAIRS)
{
printf("Student %d is waiting for a tutor.\n", id); // Prints a message,
enqueues the student, signals tutors via students_sem, and unlocks chairs_mutex.
enqueue_student((student_t){.id = id});
sem_post(&students_sem); // Alert tutors
pthread_mutex_unlock(&chairs_mutex);
sem_wait(&tutors_sem); // Wait for a tutor
printf("Student %d is getting help.\n", id);
sleep(rand() % 5 + 1); // Simulate getting help
}
else
{
pthread_mutex_unlock(&chairs_mutex);
printf("No chairs available, student %d resumes programming.\n", id);
}
}
return NULL;
}
void *tutor_thread(void *arg)
{
int id = *(int *)arg;
while (1)
{
sem_wait(&students_sem); // Wait for a student to arrive
pthread_mutex_lock(&chairs_mutex);
if (waiting_students > 0)
{
student_t student = dequeue_student();
pthread_mutex_unlock(&chairs_mutex);
printf("Tutor %d is helping student %d.\n", id, student.id);
sleep(rand() % 5 + 1); // Simulate tutoring
sem_post(&tutors_sem); // Signal the student
}
else
{
pthread_mutex_unlock(&chairs_mutex);
}
}
return NULL;
}
void *coordinator_thread(void *arg)
{
// Coordinator logic is embedded in the tutor_thread for simplicity
return NULL;
}
int main()
{
srand(time(NULL));
int num_students, num_tutors;
printf("Enter the number of students: ");
scanf("%d", &num_students);
printf("Enter the number of tutors: ");
scanf("%d", &num_tutors);
printf("Enter the number of chairs: ");
scanf("%d", &NUM_CHAIRS);
printf("Enter the number of iterations: ");
scanf("%d", &NUM_ITERATIONS);
pthread_t students[num_students];
pthread_t tutors[num_tutors];
pthread_t coordinator;
int student_ids[num_students];
int tutor_ids[num_tutors];
student_queue = (student_t *)malloc(NUM_CHAIRS * sizeof(student_t)); // Declares
arrays for thread handles and IDs for students and tutors.
sem_init(&students_sem, 0, 0); // Allocates memory for the student queue, based
on the number of chairs.
sem_init(&tutors_sem, 0, 0);
pthread_mutex_init(&chairs_mutex, NULL);
pthread_mutex_init(&queue_mutex, NULL);
// Coordinator thread creation removed since its logic is handled within
tutor_thread
for (int i = 0; i < num_students; i++)
{
student_ids[i] = i + 1;
pthread_create(&students[i], NULL, student_thread, &student_ids[i]);
}
for (int i = 0; i < num_tutors; i++)
{
tutor_ids[i] = i + 1;
pthread_create(&tutors[i], NULL, tutor_thread, &tutor_ids[i]);
}
for (int i = 0; i < num_students; i++)
{
pthread_join(students[i], NULL);
}
// We won't join tutor threads as they run indefinitely. Normally we should
handle cleanup properly.
sem_destroy(&students_sem);
sem_destroy(&tutors_sem);
pthread_mutex_destroy(&chairs_mutex);
pthread_mutex_destroy(&queue_mutex);
free(student_queue);
return 0;
}
Output
Workflow
1. Programming Phase:
• Students start by programming, simulating independent work.
• Each student works for a random amount of time before seeking help.
2. Seeking Help:
• Students attempt to wait for a tutor by checking for available chairs.
• If a chair is available, the student sits and waits. If not, they resume programming and will
check later.
3. Tutoring Phase:
• Tutors monitor for students waiting in chairs.
• Tutors pick students from the waiting queue and provide help.
• After helping a student, tutors check for other waiting students or become available for
new requests.
4. End of Cycle:
• Students resume programming after getting help and repeat the process for a specified
number of iterations.
Example Scenario
For example, consider a tutoring center with 4 students, 2 tutors, and 2 chairs, with each student
going through this cycle once:
• Iteration Begins:
▪ All students begin programming.
▪ After some time, students 1 and 2 finish and seek help. They find chairs and wait.
▪ Students 3 and 4 finish programming later but find no chairs available, so they resume
programming.
• Tutoring:
▪ Tutors 1 and 2 help students 1 and 2 respectively.
▪ After helping, these students return to programming.
• Conclusion:
▪ Students 3 and 4 check again for available chairs. If they find seats, they wait; otherwise,
they miss out on tutoring for this iteration.
Challenges Addressed
o Deadlock Avoidance: Ensures students do not indefinitely wait if chairs are unavailable.
o Fairness: Manages access to tutors so that students are helped in the order they arrive.
o Efficiency: Maximizes tutor utilization by ensuring they always help a waiting student if
available.
Limitations
1. Tutor threads never stop running.
2. Tutors don’t have a schedule and always wait for students.
3. Threads might waste CPU resources if they wait for tasks without a break.
4. The program doesn’t handle errors well.
5. The program may slow down with many students and tutors.
Real-World Applications
This model is applicable to any environment where there are limited resources and a need to manage
access efficiently. Examples include:
• Customer support centers where agents assist clients.
• Healthcare settings with limited doctors and patient queues.
• Technical support desks in corporate environments.
Conclusion:
This code models a classic producer-consumer problem using semaphores and mutexes. Students act
as producers that require a resource tutoring and tutors act as consumers that provide this resource.
The code ensures mutual exclusion and proper signaling between threads to handle the dynamic
interaction of students needing help and tutors providing assistance. By modeling this problem, one
gains insights into solving real-world issues related to resource allocation and process synchronization
in various fields.