1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
/*
* This module adds a timer interrupt to time-slice the threads. The
* details of operation of this file are not important for understanding
* HW7. The end result is to ensure that __sthread_schedule is called
* periodically.
*
* The principle is as follows:
*
* 1. Set up a periodic timer interrupt (the period is specified
* by the QUANTUM_* constants below).
*
* 2. In the signal handler for SIGALRM, set up the return context
* so that the signal handler returns to __sthread_schedule,
* instead than the point where the exception occurred. The
* return address is saved on the stack.
*
*--------------------------------------------------------------------
* Adapted from code for CS24 by Jason Hickey.
* Copyright (C) 2004-2010, Caltech. All rights reserved.
*/
#define __USE_GNU
#define _GNU_SOURCE
#include <ucontext.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <memory.h>
#include <sys/time.h>
#include "timer.h"
#include "glue.h"
/*
* Default timeslice quantum. This is configured for a 0.5ms timeslice so
* that context-switches occur frequently enough to manifest concurrency
* issues in the code. Normally we would want context-switches to be far
* less frequent so that we could minimize the overhead of the switch.
*
* This mechanism relies on the Linux High-Resolution Timer support that
* was integrated into the Linux 2.6.21 kernel, so as long as the OS version
* is at least that far along, and HRT is enabled in the kernel (which it
* usually is) then we should indeed get interrupts at the requested rate.
*/
#define QUANTUM_SEC 0
#define QUANTUM_USEC 500
/*
* Default size of signal stack is set to 64k.
*/
#define SIGNAL_STACKSIZE (1 << 16)
/*
* When the timer fires, set up the process context so that
* the signal handler returns to the __sthread_schedule function.
*/
static void timer_action(int signum, siginfo_t *infop, void *data) {
/*
* Get the scheduler lock.
* If the scheduler is already active, ignore this timer interrupt.
*/
if (__sthread_lock()) {
ucontext_t *contextp = (ucontext_t *) data;
greg_t *regs = contextp->uc_mcontext.gregs;
void **rsp = (void **) regs[REG_RSP];
void *rip = (void *) regs[REG_RIP];
/* Push the current program counter as the new return address */
*--rsp = rip;
/* Set the program counter to the __sthread_schedule function */
rip = (void *) __sthread_switch;
/* Save these two registers back to the context */
regs[REG_RSP] = (greg_t) rsp;
regs[REG_RIP] = (greg_t) rip;
}
}
/*
* Start the timer to generate periodic interrupts.
* Signals are handled on an alternate stack, so that
* we can manipulate the process stack in the signal
* handler.
*/
void start_timer() {
static char sigstack[SIGNAL_STACKSIZE];
struct sigaction action;
struct itimerval itimer;
stack_t stackinfo;
/* Handle signals on an alternate stack */
stackinfo.ss_sp = sigstack;
stackinfo.ss_flags = SS_ONSTACK;
stackinfo.ss_size = sizeof(sigstack);
if (sigaltstack(&stackinfo, (stack_t *) 0) < 0) {
perror("sigaltstack");
exit(1);
}
/* Install the signal handler */
memset(&action, 0, sizeof(action));
action.sa_sigaction = timer_action;
action.sa_flags = SA_SIGINFO | SA_ONSTACK;
if (sigaction(SIGALRM, &action, (struct sigaction *) 0) < 0) {
perror("sigaction");
exit(1);
}
/* Start the periodic timer */
itimer.it_interval.tv_sec = QUANTUM_SEC;
itimer.it_interval.tv_usec = QUANTUM_USEC;
itimer.it_value.tv_sec = QUANTUM_SEC;
itimer.it_value.tv_usec = QUANTUM_USEC;
if (setitimer(ITIMER_REAL, &itimer, (struct itimerval *) 0) < 0) {
perror("setitimer");
exit(1);
}
}