-
Notifications
You must be signed in to change notification settings - Fork 1
/
monitor.c
332 lines (319 loc) · 10 KB
/
monitor.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
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
#include <stdio.h>
#include <stdlib.h>
#include <hardware.h>
#include <threads.h>
#define SIZE 200
#define ECHOSIZE 200
//struct for storing terminal statistics.
struct box {
int initialized;
int tin;
int tout;
int uin;
int uout;
};
//each input_buffer: only writable by main terminal thread, but can be
//read by multiple user thread.
static char *input_buffer[MAX_NUM_TERMINALS - 1];
//input_buffer size
static int inputbuffer_size[MAX_NUM_TERMINALS - 1];
//input_buffer read index
static int inputbuffer_ridx[MAX_NUM_TERMINALS - 1];
//input_buffer write index
static int inputbuffer_widx[MAX_NUM_TERMINALS - 1];
//each ehco_buffer: user threads could read from this buffer as well as
//main terminal thread.
static char *echo_buffer[MAX_NUM_TERMINALS - 1];
//echo_buffer size
static int echobuffer_size[MAX_NUM_TERMINALS - 1];
//echo_buffer read index
static int echobuffer_ridx[MAX_NUM_TERMINALS - 1];
//echo_buffer write index
static int echobuffer_widx[MAX_NUM_TERMINALS - 1];
//represents if a terminal is busy displaying last character. This data
//is set and read by user threads and main thread.
static int busy[MAX_NUM_TERMINALS - 1];
//Conditional variable coordinating WriteDataRegister to hardware.
static cond_id_t busycond[MAX_NUM_TERMINALS - 1];
//Read and set by user threads. Representing if there exists some write_terminal
//not returning.
static int wt_ocpd[MAX_NUM_TERMINALS - 1];
//Conditional variable making sure only one thread is in write_terminal procedure
//at a given time.
static cond_id_t wtcond[MAX_NUM_TERMINALS - 1];
//Read and set by user threads. Representing if there exists some read_terminal
//not returning.
static int rt_ocpd[MAX_NUM_TERMINALS - 1];
//Conditional variable making sure only one thread is in read_terminal procedure.
static cond_id_t rtcond[MAX_NUM_TERMINALS - 1];
//read and set by multiple user thread. representing if there exists some terminal_
//statistics calls not returning.
static int tds_ocpd;
//conditional variable making sure only one thread is calling terminal statistics method.
static cond_id_t tdscond;
//Conditional variable coordinating read and write to input_buffer.
static cond_id_t inputempty[MAX_NUM_TERMINALS - 1];
//a list of previous copy of terminal statistics.
static struct box *prev;
//a list of current copy of terminal statistics.
static struct box *curr;
//an allocated memory location to store future terminal statistics.
static struct box *next;
/**
* Entry procedure: return all terminal statistics into allocated space pointed by stat.
*/
extern void
tds(struct termstat* stat){
Declare_Monitor_Entry_Procedure();
struct box *old;
int i;
while(tds_ocpd){
CondWait(tdscond);
}
tds_ocpd = 1;
//take a snap image of current terminal statistics.
//after this pointer assignment, all new data will go into "next".
//Then we can slowly copy the snapped statistics into buffer without anyone ever
//changing it.
old = prev;
prev = curr;
curr = next; //after this execution, everything will be written into next
for (i = 0; i < MAX_NUM_TERMINALS; i++){
//prev += old, then free old
if (old[i].initialized){
prev[i].initialized = 1;
prev[i].tin +=old[i].tin;
prev[i].tout +=old[i].tout;
prev[i].uin += old[i].uin;
prev[i].uout += old[i].uout;
}
if (prev[i].initialized){
//copy data into stat
stat[i].tty_in = prev[i].tin;
stat[i].tty_out = prev[i].tout;
stat[i].user_in = prev[i].uin;
stat[i].user_out = prev[i].uout;
} else {
//copy -1 into stat
stat[i].tty_in = -1;
stat[i].tty_out = -1;
stat[i].user_in = -1;
stat[i].user_out = -1;
}
}
free(old); //make sure memory usage is bounded.
next = calloc(MAX_NUM_TERMINALS, sizeof(struct box));
tds_ocpd = 0;
CondSignal(tdscond);
return;
}
/**
* convenience method to write a character to buffer.
* returns number of character written.
*/
static
int write_to_buffer1(char *buffer, char c, int i, int *buffer_size, int* widx, int max) {
if(buffer_size[i] == max) return 0;
buffer[widx[i]] = c;
widx[i] = (widx[i] + 1) % max;
buffer_size[i]++;
return 1;
}
/**
* convenience method to read a character to buffer.
* return the character if the buffer is not empty. return 0 if the buffer is empty.
* This does not block.
*/
static
char read_buffer1(char *buffer, int i, int *buffer_size, int* ridx, int max) {
char c;
if(buffer_size[i] == 0) return 0;
c = buffer[ridx[i]];
ridx[i] = (ridx[i] + 1) % max;
buffer_size[i]--;
return c;
}
/**
* convenience method to put character into input_buffer as well as setting conditional variable
* correctly.
*/
static int
write_char_inputbuffer(int term, char c){
CondSignal(inputempty[term]);
return write_to_buffer1(input_buffer[term], c, term, inputbuffer_size, inputbuffer_widx, SIZE);
}
/**
* Initialize monitor by allocating prev, curr, next for storing terminal statistics.
* At any given time, due to the free() function call in the tds(), the memory usage is thus bounded.
*/
void init_monitor(){
prev = calloc(MAX_NUM_TERMINALS, sizeof(struct box));
curr = calloc(MAX_NUM_TERMINALS, sizeof(struct box));
next = calloc(MAX_NUM_TERMINALS, sizeof(struct box));
//initialize cond var
tds_ocpd = 0;
tdscond = CondCreate();
}
/**
* Initialize a terminal by initializing its input, echo buffers and cond variables.
*/
void
init_moniterminal(int term){
inputbuffer_size[term] = 0;
inputbuffer_ridx[term] = 0;
inputbuffer_widx[term] = 0;
input_buffer[term] = malloc(sizeof(char) * SIZE);
echo_buffer[term] = malloc(sizeof(char) * ECHOSIZE);
echobuffer_size[term] = 0;
echobuffer_ridx[term] = 0;
echobuffer_widx[term] = 0;
busy[term] = 0;
wt_ocpd[term] = 0;
rt_ocpd[term] = 0;
busycond[term] = CondCreate();
wtcond[term] = CondCreate();
rtcond[term] = CondCreate();
inputempty[term] = CondCreate();
curr[term].initialized = 1;
next[term].initialized = 1;
}
/**
* Entrance procedure for transmit interrupt handler.
* If echo is not empty, then continue to write to terminal.
* Output buffer is not taken care here, but in the other procedure for write terminal
* as well as giving echo buffer priority.
*/
extern void
ti_inner(term){
Declare_Monitor_Entry_Procedure();
curr[term].tout++;
busy[term] = 0;
CondSignal(busycond[term]);
char c = read_buffer1(echo_buffer[term], term, echobuffer_size, echobuffer_ridx, ECHOSIZE);
if (c) { //if echo buffer is not empty
WriteDataRegister(term, c);
busy[term] = 1;
}
return;
}
/**
* Entry procedure for receive interrupt.
*/
extern void
ri_inner(term){
Declare_Monitor_Entry_Procedure();
char c = ReadDataRegister(term);
curr[term].tin++; //update statistics
//character processing
if (c == '\r') {
c = '\n';
}
if (c == '\b' || c == '\177') {
if (input_buffer[term][inputbuffer_widx[term] - 1] == '\n'){}
else {
if(inputbuffer_size[term] > 0){
inputbuffer_size[term]--;
inputbuffer_widx[term] = (inputbuffer_widx[term] - 1) % SIZE;
}
if (busy[term]){
write_to_buffer1(echo_buffer[term], '\b', term, echobuffer_size, echobuffer_widx, ECHOSIZE);
write_to_buffer1(echo_buffer[term], ' ', term, echobuffer_size, echobuffer_widx, ECHOSIZE);
write_to_buffer1(echo_buffer[term], '\b', term, echobuffer_size, echobuffer_widx, ECHOSIZE);
} else {
WriteDataRegister(term, '\b');
busy[term] = 1;
write_to_buffer1(echo_buffer[term], ' ', term, echobuffer_size, echobuffer_widx, ECHOSIZE);
write_to_buffer1(echo_buffer[term], '\b', term, echobuffer_size, echobuffer_widx, ECHOSIZE);
}
}
return;
}
int temp = write_char_inputbuffer(term, c);
// successufully wrote into inputbuffer.
if (temp) {
if (busy[term]) {
if(c == '\n'){
write_to_buffer1(echo_buffer[term], '\r', term, echobuffer_size, echobuffer_widx, ECHOSIZE);
write_to_buffer1(echo_buffer[term], '\n', term, echobuffer_size, echobuffer_widx, ECHOSIZE);
} else {
write_to_buffer1(echo_buffer[term], c, term, echobuffer_size, echobuffer_widx, ECHOSIZE);
}
} else {
if(c == '\n'){
WriteDataRegister(term, '\r');
busy[term] = 1;
write_to_buffer1(echo_buffer[term], '\n', term, echobuffer_size, echobuffer_widx, ECHOSIZE);
} else {
WriteDataRegister(term, c);
busy[term] = 1;
}
}
}
}
/**
* Entry procedure for write terminal.
* Use cond var to avoid threads interleaving.
*/
extern int
wt_inner(int term, char *buf, int buflen){
Declare_Monitor_Entry_Procedure();
int result = 0;
char c, cc;
while(wt_ocpd[term] == 1){CondWait(wtcond[term]);}
wt_ocpd[term] = 1;
while (result < buflen){
while(busy[term]){
CondWait(busycond[term]);
}
//give priority to echo buffer.
c = read_buffer1(echo_buffer[term], term, echobuffer_size, echobuffer_ridx, ECHOSIZE);
if (c) {
WriteDataRegister(term, c);
busy[term] = 1;
} else {
cc = buf[result];
//character processing.
if (cc == '\n'){
WriteDataRegister(term, '\r');
busy[term] = 1;
write_to_buffer1(echo_buffer[term], '\n', term, echobuffer_size, echobuffer_widx, ECHOSIZE);
} else {
WriteDataRegister(term, cc);
busy[term] = 1;
}
result++;
}
}
CondWait(busycond[term]); //make sure last character is displayed.
wt_ocpd[term] = 0;
CondSignal(wtcond[term]);
curr[term].uin += result;
return result;
}
/**
* Entry procedure for read terminal.
* Uses cond var to avoid threads interleaving.
*/
extern int
readterminal_inner(int term, char* buf, int buflen){
Declare_Monitor_Entry_Procedure();
int result = 0;
char c;
while(rt_ocpd[term] == 1){CondWait(rtcond[term]);}
rt_ocpd[term] = 1;
while(result < buflen){
while (inputbuffer_size[term] == 0){
CondWait(inputempty[term]);
}
c = read_buffer1(input_buffer[term], term, inputbuffer_size, inputbuffer_ridx, SIZE);
if(c){
buf[result] = c;
result++;
}
if (c == '\n') break; //immediately returns if hitting new line.
}
rt_ocpd[term] = 0;
CondSignal(rtcond[term]);
term[curr].uout += result;
return result;
}