-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathinterrupt.c
195 lines (169 loc) · 3.88 KB
/
interrupt.c
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
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
#include <assert.h>
#include <errno.h>
#include <signal.h>
#include <stdlib.h>
#include <ucontext.h>
#include <unistd.h>
#include <sys/time.h>
#include <stdarg.h>
#include "thread.h"
#include "interrupt.h"
static void interrupt_handler(int sig, siginfo_t * sip, void *contextVP);
static void set_interrupt();
static void set_signal(sigset_t * setp);
static int loud = 0;
/* Should be called when you initialize threads package. Many of the calls won't
* make sense at first -- study the man pages! */
void
register_interrupt_handler(int verbose)
{
struct sigaction action;
int error;
static int init = 0;
assert(!init); /* should only register once */
init = 1;
loud = verbose;
action.sa_handler = NULL;
action.sa_sigaction = interrupt_handler;
/* block SIG_TYPE while interrupt_handler() is running. This will avoid
* recursive interrupts where an interrupt occurs before the previous
* interrupt handler has finished running. */
error = sigemptyset(&action.sa_mask);
assert(!error);
/* use sa_sigaction as handler instead of sa_handler */
action.sa_flags = SA_SIGINFO;
if (sigaction(SIG_TYPE, &action, NULL)) {
perror("Setting up signal handler");
assert(0);
}
set_interrupt();
}
/* enables interrupts. */
int
interrupts_on()
{
return interrupts_set(1);
}
/* disables interrupts */
int
interrupts_off()
{
return interrupts_set(0);
}
/* enables or disables interrupts, and returns whether interrupts were enabled
* or not previously. */
int
interrupts_set(int enabled)
{
int ret;
sigset_t mask, omask;
set_signal(&mask);
if (enabled) {
ret = sigprocmask(SIG_UNBLOCK, &mask, &omask);
} else {
ret = sigprocmask(SIG_BLOCK, &mask, &omask);
}
assert(!ret);
return (sigismember(&omask, SIG_TYPE) ? 0 : 1);
}
int
interrupts_enabled()
{
sigset_t mask;
int ret;
ret = sigprocmask(0, NULL, &mask);
assert(!ret);
return (sigismember(&mask, SIG_TYPE) ? 0 : 1);
}
void
interrupts_quiet()
{
loud = 0;
}
void
spin(int usecs)
{
struct timeval start, end, diff;
int ret;
ret = gettimeofday(&start, NULL);
assert(!ret);
while (1) {
ret = gettimeofday(&end, NULL);
timersub(&end, &start, &diff);
if ((diff.tv_sec * 1000000 + diff.tv_usec) >= usecs) {
break;
}
}
}
/* turn off interrupts while printing */
int
unintr_printf(const char *fmt, ...)
{
int ret, enabled;
va_list args;
enabled = interrupts_off();
va_start(args, fmt);
ret = vprintf(fmt, args);
va_end(args);
interrupts_set(enabled);
return ret;
}
/* static functions */
static void
set_signal(sigset_t * setp)
{
int ret;
ret = sigemptyset(setp);
assert(!ret);
ret = sigaddset(setp, SIG_TYPE);
assert(!ret);
return;
}
static int first = 1;
static struct timeval start, end, diff = { 0, 0 };
/*
* STUB: once register_interrupt_handler() is called, this routine
* gets called each time SIG_TYPE is sent to this process
*/
static void
interrupt_handler(int sig, siginfo_t * sip, void *contextVP)
{
ucontext_t *context = (ucontext_t *) contextVP;
/* check that SIG_TYPE is blocked on entry.
* this signal should be blocked because of the sigemptyset call in
* register_interrupt_handler(). */
assert(!interrupts_enabled());
if (loud) {
int ret;
ret = gettimeofday(&end, NULL);
assert(!ret);
if (first) {
first = 0;
} else {
timersub(&end, &start, &diff);
}
start = end;
printf("%s: context at %10p, time diff = %ld us\n",
__FUNCTION__, context,
diff.tv_sec * 1000000 + diff.tv_usec);
}
set_interrupt();
/* implement preemptive threading by calling thread_yield */
thread_yield(THREAD_ANY);
}
/*
* Use the setitimer() system call to set an alarm in the future. At that time,
* this process will receive a SIGALRM signal.
*/
static void
set_interrupt()
{
int ret;
struct itimerval val;
val.it_interval.tv_sec = 0;
val.it_interval.tv_usec = 0;
val.it_value.tv_sec = 0;
val.it_value.tv_usec = SIG_INTERVAL;
ret = setitimer(ITIMER_REAL, &val, NULL);
assert(!ret);
}