concurrency/synchronization using uml state models and sequence models: a brief review april 21 st,...
Post on 21-Dec-2015
229 views
TRANSCRIPT
Concurrency/synchronization using UML state models and sequence models: A brief review
April 21st, 2008Michigan State University
Simple monitor-process pattern
Idle
[guard] / accept-call ( op(parms) )
/ reply (op, result )
Monitor process
Operation body
Bounded Buffer Example
/ reply ( pull, rv )
BoundedBuffer
Idle
[queue_.size != 0] / accept-call ( pull )
Pulling
do/ rv := queue_.pull
Pushingdo/ queue_.push(x)
/ reply ( push )
[queue_.size != MAX] / accept-call ( push(x) )
Example: Code for Buffer::push
void Buffer::push(int x)
{
lock_.acquire();
while (queue_.size() == MAX) {
full_.wait();
}
queue_.push(x);
empty_.signal();
lock_.release();
}
Idle
[queue_.size() != MAX]/ accept-call (push(x))
Pushingdo/ queue_.push(x)
/ reply (push)
Example: Code for Buffer::push
void Buffer::push(int x)
{
lock_.acquire();
while (queue_.size() == MAX) {
full_.wait();
}
queue_.push(x);
empty_.signal();
lock_.release();
}
Idle
[queue_.size() != MAX]/ accept-call (push(x))
Pushingdo/ queue_.push(x)
/ reply (push)
Example: Code for Buffer::push
void Buffer::push(int x)
{
lock_.acquire();
while (queue_.size() == MAX) {
full_.wait();
}
queue_.push(x);
empty_.signal();
lock_.release();
}
Idle
[queue_.size() != MAX]/ accept-call (push(x))
Pushingdo/ queue_.push(x)
/ reply (push)
Example: Code for Buffer::push
void Buffer::push(int x)
{
lock_.acquire();
while (queue_.size() == MAX) {
full_.wait();
}
queue_.push(x);
empty_.signal();
lock_.release();
}
Idle
[queue_.size() != MAX]/ accept-call (push(x))
Pushingdo/ queue_.push(x)
/ reply (push)
Example: Code for Buffer::pull
int Buffer::pull(){ lock_.acquire();
while (queue_.size() == 0) { empty_.wait(); }
int rv = queue_.pull();
full_.signal(); lock_.release(); return rv;}
Idle
[queue_.size() != 0]/ accept-call (pull)
Pushingdo/ rv :=
queue_.pull()
/ reply (pull, rv)
More complex guard conditions
Consider a banking application that allows multiple clients to deposit and withdraw funds from shared accountsGoals: Protect against data races, so that money is
not accidentally created or destroyed Prevent overdrafts by making withdraw
requests block if account has insufficient funds
Question: How should we model the behavior of an account object?
Monitor-process model of BankAccount
/ reply ( deposit )
BankAccount
Idle
/ accept-call ( deposit(amount) )
Depositingdo/ balance_ += amount;
Withdrawingdo/ balance_ -= amount;
/ reply ( withdraw )
[ amount <= balance_ ] / accept-call ( withdraw(amount) )
Code for BankAccount::withdraw
void
BankAccount::withdraw(int amount)
{
lock_.acquire();
while (amount > balance_) {
okToWithdraw_.wait();
}
balance_ -= amount;
lock_.release();
}
Idle
[amount <= balance_]/ accept-call (withdraw(amount))
Withdrawing
do/ balance_ -= amount;
/ reply (withdraw)
Code for BankAccount::deposit
void
BankAccount::deposit(int amount)
{
lock_.acquire();
balance_ += amount;
okToWithdraw_.broadcast();
lock_.release();
}
Idle/ accept-call (deposit(amount))
Depositing
do/ balance_ += amount;
/ reply (deposit)
Signal vs. BroadcastWhen one thread changes the value of a condition upon which others might be waiting, the modifier is obliged to notify these waiting threadsAlways safest, though perhaps not very efficient, to use broadcast to notify waiting threads after a changeQuestion: When is it safe to use signal?
“Mixed” Monitor objects
Some methods on a monitor class may be “non-monitor methods” Do not acquire / release mutex lock Multiple threads may access
simultaneously Threads may access non-monitor
methods while 1 thread in a monitor method
1 thread may enter monitor method while other threads in non-monitor method