Lecture 21

wjddlstjd396·2022년 3월 14일
0

CS343-Parallel Programming

목록 보기
18/24

External scheduling is not as powerful as internal scheduling.

• You cannot access information within the routine
• if you need to wait after cooperation, you need condition lock.

Ex) the dating service problem

1. after some questions, you get a compatibility code - ccode

senario:
1. a girl comes in with ccode
2. if there is no boy with same ccode, she waits in the bench
3. else, she leaves a phone number on the board and wake a boy, and sleeps on a chair
4. the boy wakes up with her phone number.
5. the boy leaves his phone number on another board.
6. the boy wakes the girl and leaves
7. the girl wakes up with his phone number and leaves.

_Monitor DatingService {
enum { CCodes = 20 }; // compatibility codes
uCondition girls[CCodes], boys[CCodes], exchange;
int girlPhoneNo, boyPhoneNo;
public:
int girl( int phoneNo, int ccode ) {
if ( boys[ccode].empty() ) { // no compatible boy ?
girls[ccode].wait(); // wait for boy
girlPhoneNo = phoneNo; // make phone number available
exchange.signal(); // wake boy from chair
} else {
girlPhoneNo = phoneNo; // make phone number available

// boys[ccode].signalBlock() can replace lower 2 lines
boys[ccode].signal(); // wake boy

// you can use girls[ccode] as a chair instead of
// 'exchange' since its empty banch.
exchange.wait(); // sit in chair
}
return boyPhoneNo;
}
int boy( int phoneNo, int ccode ) {
// same as above, with boy/girl interchanged
}
};

Solution 3 of previous reader/writers problem.
No barging. solves all 5 rules.
BUT with temperal barging.

_Monitor ReadersWriter {
int rcnt = 0, wcnt = 0;
// the moment we seperate readers, writers, it has temperal barging.
public:
if ( wcnt != 0 | | ! writers.empty() ) readers.wait();
rcnt += 1;
}
rcnt -= 1;
if ( rcnt == 0 ) writers.signal();
}
void startWrite() {
if ( wcnt !=0 | | rcnt != 0 ) writers.wait();
wcnt = 1;
}
void endWrite() {
wcnt = 0;
else writers.signal();
}
};

But this only allows one reader at a time.

Ex) conversion to 1 step protocal + allowing multiple reads at a time

_Monitor ReadersWriter {
if ( ! writers.empty() ) readers.wait();
rcnt += 1;
}
_Mutex void endRead() { . . . }
public:
_Nomutex void read(. . .) { // no const or mutable
}
void write(. . .) { // acquire mutual exclusion
if ( rcnt != 0 ) writers.wait(); // release/reacquire
// write, mutual exclusion
else writers.signal();
}
};

back to 2 step protocol + solving rule 6 - fixing temperal barging

_Monitor ReadersWriter {
int rcnt = 0, wcnt = 0;
uCondition RWers;
enum RW { READER, WRITER };
public:
if ( wcnt !=0 | | ! RWers.empty() ) RWers.wait( READER );
rcnt += 1;
if ( ! RWers.empty() && RWers.front() == READER ) RWers.signal();
}
rcnt -= 1;
if ( rcnt == 0 ) RWers.signal();
}
void startWrite() {
if ( wcnt != 0 | | rcnt != 0 ) RWers.wait( WRITER );
wcnt = 1;
}
void endWrite() {
wcnt = 0;
RWers.signal();
}
};

passing parameter to uCondition::wait() attaches the information on the shadow queue of the internal condition lock.

you can ask only one question: uCondition::front() - what is the tag on the first item of the shadow queue.

readers and writers problem solution 8 with external scheduling

Monitor ReadersWriter {
int rcnt = 0, wcnt = 0;
public:
rcnt -= 1;
}
void endWrite() {
wcnt = 0;
}
if ( wcnt > 0 ) _Accept( endWrite );
rcnt += 1;
}
void startWrite() {
if ( wcnt > 0 ) _Accept( endWrite );
else while ( rcnt > 0 ) _Accept( endRead );
wcnt = 1;
}
};

This does not cause startWrite() to starve by startRead().
That is because when _Accept(endRead) is called, it does not just leave but it activly hands endRead() a botton; so startWrite will call as much endRead as rcnt and while doing so, no startRead can barge in.

8.6 Exception

call to _Accept(...) may not return with proper results.
it may be the case where the method in _Accept throws an exception.

in such case, _Accept throws an exception called uMutexFailure::RendezvousFailure

_Monitor M {
public:
void mem1() {
. . . if ( . . . ) _Throw E(); . . . // E goes to caller
} // uRendezvousFailure goes to “this”
void mem2() {
try {
. . . if ( . . . ) _Accept( mem1 ); . . .
} catch( uMutexFailure::RendezvousFailure & ) { // implicitly enabled
// deal with rendezvous failure
} // try
}
};

There are 3 or 4 exception in uC++ that is SO important that it is always turned on (regardless of _Enable), and uMutexFailure::RendezvousFailure is one of them.

_Accept call with multiple member routines - _Accept(mem1, mem2, ...) - may require flag variables to make distinguish between which member that has failed.

8.7 Nested Monitor Calls

If only way to get to M2 is through M1, this may result in a deadlock.

Same problems can happen with any kinds of lock and it is called lock composition problem.

3B CS