-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsynch.cc
268 lines (234 loc) · 8.32 KB
/
synch.cc
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
// synch.cc
// Routines for synchronizing threads. Three kinds of
// synchronization routines are defined here: semaphores, locks
// and condition variables (the implementation of the last two
// are left to the reader).
//
// Any implementation of a synchronization routine needs some
// primitive atomic operation. We assume Nachos is running on
// a uniprocessor, and thus atomicity can be provided by
// turning off interrupts. While interrupts are disabled, no
// context switch can occur, and thus the current thread is guaranteed
// to hold the CPU throughout, until interrupts are reenabled.
//
// Because some of these routines might be called with interrupts
// already disabled (Semaphore::V for one), instead of turning
// on interrupts at the end of the atomic operation, we always simply
// re-set the interrupt state back to its original value (whether
// that be disabled or enabled).
//
// Copyright (c) 1992-1993 The Regents of the University of California.
// All rights reserved. See copyright.h for copyright notice and limitation
// of liability and disclaimer of warranty provisions.
#include <iostream>
#include "copyright.h"
#include "synch.h"
#include "system.h"
using namespace std;
//----------------------------------------------------------------------
// Semaphore::Semaphore
// Initialize a semaphore, so that it can be used for synchronization.
//
// "debugName" is an arbitrary name, useful for debugging.
// "initialValue" is the initial value of the semaphore.
//----------------------------------------------------------------------
Semaphore::Semaphore(char* debugName, int initialValue)
{
name = debugName;
value = initialValue;
queue = new List;
}
//----------------------------------------------------------------------
// Semaphore::Semaphore
// De-allocate semaphore, when no longer needed. Assume no one
// is still waiting on the semaphore!
//----------------------------------------------------------------------
Semaphore::~Semaphore()
{
delete queue;
}
//----------------------------------------------------------------------
// Semaphore::P
// Wait until semaphore value > 0, then decrement. Checking the
// value and decrementing must be done atomically, so we
// need to disable interrupts before checking the value.
//
// Note that Thread::Sleep assumes that interrupts are disabled
// when it is called.
//----------------------------------------------------------------------
void
Semaphore::P()
{
IntStatus oldLevel = interrupt->SetLevel(IntOff); // disable interrupts
while (value == 0) { // semaphore not available
queue->Append((void *)currentThread); // so go to sleep
currentThread->Sleep();
}
value--; // semaphore available,
// consume its value
(void) interrupt->SetLevel(oldLevel); // re-enable interrupts
}
//----------------------------------------------------------------------
// Semaphore::V
// Increment semaphore value, waking up a waiter if necessary.
// As with P(), this operation must be atomic, so we need to disable
// interrupts. Scheduler::ReadyToRun() assumes that threads
// are disabled when it is called.
//----------------------------------------------------------------------
void
Semaphore::V()
{
Thread *thread;
IntStatus oldLevel = interrupt->SetLevel(IntOff);
thread = (Thread *)queue->Remove();
if (thread != NULL) // make thread ready, consuming the V immediately
scheduler->ReadyToRun(thread);
value++;
(void) interrupt->SetLevel(oldLevel);
}
// Dummy functions -- so we can compile our later assignments
// Note -- without a correct implementation of Condition::Wait(),
// the test case in the network assignment won't work!
Lock::Lock(char* debugName)
{
name = debugName;
lockStatus = FREE;
lockOwner = NULL;
waitQueue = new List;
}
Lock::~Lock()
{
delete waitQueue;
}
void Lock::Acquire()
{
IntStatus oldLevel = interrupt->SetLevel(IntOff); //disable interrupts
cout << "Current thread name: " << currentThread->getName() << " > is trying to acquire lock: " << this->getName() << endl;
if(currentThread == lockOwner) //current thread is lock owner
{
(void) interrupt->SetLevel(oldLevel); //restore interrupts
return;
}
if(lockStatus == FREE) //lock is available
{
//I can have the lock
lockStatus = BUSY; //make state BUSY
lockOwner = currentThread; //make myself the owner
}
else //lock is busy
{
waitQueue->Append(currentThread); //Put current thread on the lock’s waitQueue
currentThread->Sleep();
}
(void) interrupt->SetLevel(oldLevel); //restore interrupts
}
void Lock::Release()
{
IntStatus oldLevel = interrupt->SetLevel(IntOff); //disable interrupts
if(currentThread != lockOwner) //current thread is not lock owner
{
printf("Lock::Release::(currentThread != lockOwner)::Current thread is not lock owner\n");
(void) interrupt->SetLevel(oldLevel); //restore interrupts
return;
}
if(!waitQueue->IsEmpty()) //lock waitQueue is not empty
{
Thread* thread = (Thread*)waitQueue->Remove(); //remove 1 waiting thread
lockOwner = thread; //make them lock owner
scheduler->ReadyToRun(thread); //puts a thread at the back of the
//readyQueue in the ready state
}
else
{
lockStatus = FREE; //make lock available
lockOwner = NULL; //unset ownership
}
(void) interrupt->SetLevel(oldLevel); //restore interrupts
}
void Lock::Print()
{
printf("Lock waitQueue contents:\n");
waitQueue->Mapcar((VoidFunctionPtr) ThreadPrint);
}
Condition::Condition(char* debugName)
{
name = debugName;
waitingLock = NULL;
waitQueue = new List;
}
Condition::~Condition()
{
delete waitQueue;
}
void Condition::Wait(Lock* conditionLock)
{
//ASSERT(FALSE);
IntStatus oldLevel = interrupt->SetLevel(IntOff); //disable interrupts
if(conditionLock == NULL)
{
printf("Condition::Wait::(conditionLock == NULL)::There is no lock to be acquired\n");
(void) interrupt->SetLevel(oldLevel); //restore interrupts
return;
}
if(waitingLock == NULL)
{
//no one waiting
waitingLock = conditionLock;
}
if(waitingLock != conditionLock)
{
printf("Condition::Wait::(waitingLock != conditionLock)::waitingLock and the lock passed in don't match\n");
(void) interrupt->SetLevel(oldLevel); //restore interrupts
return;
}
//OK to wait
waitQueue->Append(currentThread);//Hung: add myself to Condition Variable waitQueue
conditionLock->Release();
currentThread->Sleep(); //currentThread is put on the waitQueue
conditionLock->Acquire();
(void) interrupt->SetLevel(oldLevel); //restore interrupts
}
void Condition::Signal(Lock* conditionLock)
{
IntStatus oldLevel = interrupt->SetLevel(IntOff); //disable interrupts
if(waitQueue->IsEmpty()) //no thread waiting
{
(void) interrupt->SetLevel(oldLevel); //restore interrupts
return;
}
if(waitingLock != conditionLock)
{
printf("Condition::Signal::(waitingLock != lock)::Incorrect lock passed in\n");
(void) interrupt->SetLevel(oldLevel); //restore interrupts
return;
}
//Wake up one waiting thread
Thread* thread = (Thread*)waitQueue->Remove();//Remove one thread from waitQueue
scheduler->ReadyToRun(thread); //Put on readyQueue
if(waitQueue->IsEmpty()) //waitQueue is empty
{
waitingLock = NULL;
}
(void) interrupt->SetLevel(oldLevel); //restore interrupts
}
void Condition::Broadcast(Lock* conditionLock)
{
IntStatus oldLevel = interrupt->SetLevel(IntOff); //disable interrupts
if(conditionLock == NULL)
{
printf("Condition::Broadcast::(conditionLock == NULL)::No lock passed in. Reenable interrupts and return.\n");
(void) interrupt->SetLevel(oldLevel); //restore interrupts
return;
}
if(conditionLock != waitingLock)
{
printf("Condition::Broadcast::(conditionLock != waitingLock)::Waiting lock is not the same as the condition lock.\n");
(void) interrupt->SetLevel(oldLevel); //restore interrupts
return;
}
(void) interrupt->SetLevel(oldLevel); //restore interrupts
while(!waitQueue->IsEmpty()) //waitQueue is not empty
{
Signal(conditionLock);
}
}