CS 420 - Assignment 6 (Option 1)

Due: Tuesday, December 11th by 11:59 PM

Timer-based preemption

Your task is to add timer-based preemption to your thread library.  Using a periodic timer signal (which will invoke a signal handler), you can preempt threads that have used an entire time slice without yielding.  This prevents any thread from monopolizing the CPU.

Getting started

Use your implementation of Assignment 4 or Assignment 5 as a starting point.

Copy the following file into the testprog directory:

abdemo.c

In this version of abdemo, the child threads do NOT yield.  Without timer-based preemption, thread A will execute completely and exit, then thread B will execute completely and exit.  Once you have timer-based preemption working, you should see that the execution of the two threads is interleaved, even though neither one yields.

Setting up the timer

The timer interrupt you can use is the SIGVTALRM signal.  Here is how to set up the timer:

Include timer header file (at top of ythread.c):

#include <sys/time.h>

Define a signal handler function:

static void handle_timer_interrupt(int sig_num)
{
/* TODO: add your code here */
}

In ythread_init(), install the timer signal handler, and initialize the timer:

{
struct itimerval itv;
struct sigaction sa;

/* install a handler for SIGVTALRM */
sa.sa_handler = &handle_timer_interrupt;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_NODEFER;
if (sigaction(SIGVTALRM, &sa, NULL) < 0) {
fprintf(stderr, "sigaction failed: %s\n", strerror(errno));
exit(1);
}

/* configure timer */
itv.it_interval.tv_sec = QUANTUM_MS / 1000;
itv.it_interval.tv_usec = (QUANTUM_MS % 1000) * 1000;
itv.it_value.tv_sec = QUANTUM_MS / 1000;
itv.it_value.tv_usec = (QUANTUM_MS % 1000) * 1000;
if (setitimer(ITIMER_VIRTUAL, &itv, NULL) < 0) {
fprintf(stderr, "setitimer failed: %s\n", strerror(errno));
exit(1);
}
}

You will need to define the QUANTUM_MS constant as the number of milliseconds between timer interrupts.  I suggest 200, so there are 5 timer interrupts per second.

Handling timer interrupts

When a timer interrupt arrives, check to see if there are any ready threads.  If so, switch to the next ready thread.

Protecting the scheduling functions

Timer interrupts can occur at any time.  You need to be careful that the timer interrupt handler does not try to force a thread switch when your thread functions are

In other words, the timer interrupt should only preempt the current thread if the current thread is not in the middle of executing one of the ythread functions.  So, the code inside each ythread function is a critical section that needs to be protected against execution of the timer interrupt handler.

A simple way of guaranteeing that the timer interrupt does not interrupt a critical section is to temporarily disable the timer interrupt while a critical section is being performed.

Here are two functions which disable and re-enable the timer interrupt, respectively:

static void disable_timer(void)
{
sigset_t mask;
sigemptyset(&mask);
sigaddset(&mask, SIGVTALRM);

if (sigprocmask(SIG_BLOCK, &mask, NULL) < 0) {
fprintf(stderr, "sigprocmask failed: %s\n", strerror(errno));
exit(1);
}
}

static void enable_timer(void)
{
sigset_t mask;
sigemptyset(&mask);
sigaddset(&mask, SIGVTALRM);

if (sigprocmask(SIG_UNBLOCK, &mask, NULL) < 0) {
fprintf(stderr, "sigprocmask failed: %s\n", strerror(errno));
exit(1);
}
}

You can add calls to your code to ensure that a timer interrupt does not interrupt a critical section.

It the timer is ready to go off while the thread library is in a critical section, the timer interrupt will be deferred until the thread library re-enables the timer interrupt.

Here are the invariants you should maintain:

If a thread is executing one of the ythread functions, the timer interrupt should be disabled.

If a thread is executing its start function (or a non-ythread function called from the start function), the timer interrupt should be enabled.

Submitting

Add a comment to the top of ythread.c indicating who the members of your group are.

Run make clean, create a zip file of the entire project, and upload the zip file to Marmoset as Project 6:

https://camel.ycp.edu:8443/