Slide

advertisement
Nachos Project 3
Lecturer: Hao-Hua Chu
TA: Chun-Po Wang
(Artoo)
Date: 2008/10/25
Project 3
• Practice thread synchronization
– Producer-consumer problem
– Dining philosophers problem
– Implement these problems with
Nachos thread and synchronization
routines
2
Summary
• Motivation & Objective
• Synchronization Problems
• Nachos Synchronization Routines
– Semaphore
– Lock
– Condition
• Requirement
• Submission
3
Motivation & Objective
• In previous project, you learnt to run
multiple threads.
– Now we should practice to make them
work together by implementing some
classic synchronization problems
• Nachos has already implemented
some class and routines to help you
solve these problems
4
Synchronization Problems
• Producer-consumer problem
–
–
–
–
–
A fixed-size buffer
Producer generates data and put it into buffer
Consumer retrieves data from buffer
They work simultaneously
The objective is to make sure that
• producer won’t put data into buffer when it’s full
• consumer won’t remove data from en empty buffer
• the state of buffer is consistent after each action
Producer
Buffer
Consumer
5
Producer-consumer problem
• When it comes to Nachos …
– A buffer may be a global variable, e.g. a
global array
– The consumer and the producer would be
threads
• Where the problem lies …
– Threads may be yielded at any point
– When both threads access the same data,
we must make sure that a thread won’t
access it when another is working on it.
– Or, the data may be corrupted
6
Synchronization Problems
(cont.)
• Dining philosophers problem
– 5 philosophers, with 5
chopsticks
– A philosopher can either think or
eat
– When she/he want to eat,
she/he must take both
chopsticks on her/his left and
right
– If all philosophers hold one
chopstick, we have a deadlock
(philosophers are strange, aren’t
they?)
http://en.wikipedia.org/wiki/Image:Dining_philosophers.png
7
Dining philosophers problem
• Now we deal with 5 threads,
apparently
• Figure out a method to prevent
deadlock
– An easy solution is to make sure
that a philosopher need to pick up
both chopsticks at the same time
8
Nachos
synchronization classes
• code/thread/synch.h and synch.cc
• Semaphore
– A counter for a set of available resources
– Nachos promised that the actions on semaphores
will not be interrupted
• Lock
– A lock can be either BUSY or FREE
– Only the thread that acquired the lock can release
it
– Lock is implemented by semaphores
• Condition
– A condition variable is used on monitors, where
many threads wait on this condition
– We discuss condition variables when introducing9
monitors later
Semaphore class
• int value;
// value >= 0
• P(): waits until value > 0, then decreases it
• V(): increases the value, and wakes up a
thread waiting in P()
• How does OS make these operations
atomic?
– Disable interrupts (this method only works on
uniprocessor machines), or
– Use special instructions (eg. test-and-set)
– Nachos uses the former
10
Lock class
• A Lock is implemented by a Semaphore:
– value 0 means busy, value 1 means free
• Acquire(): wait until the lock is free, then set it
to busy (by calling Semaphore::P())
• Release(): release the lock and wake up a
thread waiting on this lock (by calling
Semaphore::V())
– However, only the thread which acquired the lock
can release it
– This is different to a semaphore
11
An example
• See class SynchConsoleOutput
(code/userprog/synchconsole.cc)
void SynchConsoleOutput::PutChar(char ch)
{
lock->Acquire();
consoleOutput->PutChar(ch);
waitFor->P();
lock->Release();
}
void SynchConsoleOutput::CallBack()
{
waitFor->V();
}
12
Monitor
• “A monitor is an approach to synchronize two
or more computer tasks that use a shared
resource”
• Why use it? Why not just use semaphores?
– Programmers are prone to errors
• A monitor consists of
– Procedures for manipulating shared resources
– A mutual exclusion lock: only one thread can
operate in a monitor at any time
– Conditions (optional): sometimes a thread
operating in a monitor must wait for some condition
to be true before it proceeds
13
Monitor example
• Following pseudo code demonstrate a channel which
can only store one integer value at a time
monitor channel {
int contents
boolean full := false
condition snd
condition rcv
function send(int message) {
while full do wait(rcv)
contents := message
full := true
notify(snd)
}
function receive() {
var int received
while not full do wait(snd)
received := contents
full := false
notify(rcv)
return received
}
}//End of monitor
14
Source: http://en.wikipedia.org/wiki/Monitor_(synchronization)
Condition class
• Nachos DOES NOT provide a Monitor
class, but it does provide a Condition
class, which can be used to build a
“monitor-style” C++ class
– E.g. class SynchList
(code/thread/synchlist.cc), this is a List
which can be accessed by multiple
threads without any synchronization
problems
15
Condition class (cont.)
• Wait(lock): a thread in a monitor waits for this
condition.
– The lock is supplied by the monitor which uses this
condition, because when a thread waits, it should
release the lock for other threads to operate in the
monitor
• Signal(): the condition is met, and a monitor
wakes up a thread waiting on this condition.
• Broadcast(): just like Signal(), but now the
monitor wakes up all threads waiting on the
condition
16
Implementation
• Implements producer-consumer
problem with semaphores and
locks (built by Nachos)
• Implements dining philosopher
problem with a monitor-style class
(built by you)
17
Implementation (cont.)
• Make Nachos run producerconsumer problem with flag -PC,
and run dining philosopher
problem with flag -DP
– Just like -K (ThreadSelfTest) and -S
(previous project) flags
18
Producer-Consumer
problem
• Produce and consume 30 items: 0~29
• The buffer size is 5
• Print the item number you produced or consumed, and
the current total number of items in the shared buffer
// Thread body for producer
void Producer(int arg) {
int i;
for(i=0;i<30;i++){
// Produce item i here (maybe stores it in a global array)
printf("Produced item %d, Total %d item(s)\n“, ...);
}
}
// Thread body for consumer
void Consumer(int arg) {
int i;
for(i=0;i<30;i++){
// Consume item i here (maybe retrieve it from a global array)
printf(“Consumed item %d, Total %d item(s)\n“, ...); 19
}
}
Producer-Consumer
problem (cont.)
• Sample output:
Produced
Produced
Consumed
Consumed
Produced
Produced
Produced
Consumed
Produced
Consumed
Consumed
Produced
Consumed
Produced
Produced
Produced
Consumed
...
item
item
item
item
item
item
item
item
item
item
item
item
item
item
item
item
item
0,
1,
0,
1,
2,
3,
4,
2,
5,
3,
4,
6,
5,
7,
8,
9,
6,
Total
Total
Total
Total
Total
Total
Total
Total
Total
Total
Total
Total
Total
Total
Total
Total
Total
1
2
1
0
1
2
3
2
3
2
1
2
1
2
3
4
3
item(s)
item(s)
item(s)
item(s)
item(s)
item(s)
item(s)
item(s)
item(s)
item(s)
item(s)
item(s)
item(s)
item(s)
item(s)
item(s)
20
item(s)
Dining Philosopher problem
• Following skeleton is an example, you can
design by yourself
• A monitor-style class DiningTable:
• We have 5 philosophers (0~4), which means
5 threads
• Each philosopher starts at thinking, then
eating, then thinking … for 10 times
• Print what a philosopher is doing when
she/he starts to do that
21
Dining Philosopher problem
(cont.)
class DiningTable {
public:
void pickup (int it, int id) {
// Philosopher “id” wants to eat for the “it”-th times
printf(“%d: Philosopher %d is eating\n”, it, id);
}
void putdown (int it, int id) {
// Philosopher “id” goes back to think for the “it”-th times
printf(“%d: Philosopher %d is thinking\n”, it, id);
}
}
DiningTable dining_table;
// Thread body for each philosopher
void philosopher (int id) {
for(int i=0; i<10; i++) {
dining_table.pickup(i, id);
dining_table.putdown(i, id);
}
}
22
Dining Philosopher problem
(cont.)
• Sample output:
0: Philosopher
0: Philosopher
1: Philosopher
0: Philosopher
0: Philosopher
0: Philosopher
1: Philosopher
0: Philosopher
0: Philosopher
0: Philosopher
1: Philosopher
1: Philosopher
0: Philosopher
0: Philosopher
2: Philosopher
1: Philosopher
...
0
0
0
3
3
2
0
2
1
1
1
1
4
4
0
3
is
is
is
is
is
is
is
is
is
is
is
is
is
is
is
is
eating
thinking
eating
eating
thinking
eating
thinking
thinking
eating
thinking
eating
thinking
eating
thinking
eating
eating
23
Please do me a favor
• We all know that in a real system
synchronization problems arise
because threads can be interrupted at
any point
• However, currently we are working in
Nachos kernel, not in user programs,
and we don’t call OneTick() so no
interrupts would occur in our thread
body implementations.
• Things will become too easy…
24
Please do me a favor (cont.)
• So, let’s make it HARD!
• Please put following code into your
thread bodies, and every functions you
built which may be called in thread
bodies.
• This code will call
kernel->currentThread->Yield() with
some probability, effectively interrupts
your code at any point.
25
Please do me a favor (cont.)
#define PY { if(rand()%5==0) { kernel->currentThread->Yield(); } }
...
class DiningTable {
public:
void pickup (int it, int id) {
PY
printf(“%d: Philosopher %d is eating\n”, it, id);
}
void putdown (int it, int id) {
PY
printf(“%d: Philosopher %d is thinking\n”, it, id);
}
}
...
void philosopher (int id) {
PY for(int i=0; i<10; i++) {
PY
dining_table.pickup(i, id);
PY
dining_table.putdown(i, id);
PY }
}
26
Please do me a favor (cont.)
• Please add “{ }” to all if-else and
for,while loops, or this macro would
mess up your program.
• You can change random seed to see
different results by using flag “-rs”
– ./nachos -rs 100 -PC
– Please test with different random seed to
make sure that your implementations are
correct
27
Some notes
• Just like Thread, Semaphore, Lock,
and Condition all have a name
argument in constructor: please DO
NOT provide a local string to them
• You should make sure that main
thread (the only thread which builds
other threads) “waits” until other
threads finish
– How to do? This is also a synchronization
problem (an easy one)
28
Requirement
• Implement 2 synchronization problems
– Make sure that no deadlock would occur
– The output should be reasonable
• E.g. in first problem, following output is wrong
Produced item 0, Total 1 item(s)
Produced item 1, Total 1 item(s) # Total should be 2
Consumed item 2, Total 1 item(s) # Item 2 is not generated yet
• E.g. in second problem, following output is
wrong
0: Philosopher 0 is eating
0: Philosopher 1 is eating # Ph. 1 cannot eat because Ph. 0
is eating and holding the
chopstick Ph. 1 needs
29
Requirement
• Write a 2-page report
– Don’t just paste your code, I’ll read it myself
– Explain why your implementations would
generate correct output, and why there is no
deadlock
• If your project submission can’t compile and
execute on Linux in Workstation Room 217,
we will consider it as fail.
– Please contact me to apply a workstation account if
you need it.
30
Submission
•
Two people in one group (Write down the
name and student ID of all members in the
report)
The file you need to send:
•
1.
2.
•
A report in .pdf or .doc
threads.cc, threads.h, and main.cc
Send your files
–
Tar your files to an archieve
named:other files...
tar zcvf os_hw3_bXXXXXXXX_bOOOOOOOO.tar.gz
report.doc
–
–
os_hw3_bXXXXXXXX_bOOOOOOOO.tar.gz
E-mail to artoo@csie.ntu.edu.tw with following
title:
[os_hw3] bXXXXXXXX_bOOOOOOOO
Please follow the format carefully or our autoreply system will not work
31
Submission (cont.)
• Deadline: 11/10 24:00
– For the delayed submission, deduct
5 points for each day
• DO NOT COPY!!
Protect your code well!!
32
Download