This commit is contained in:
Ingy döt Net 2013-10-27 22:24:23 +00:00
parent 6f050a029e
commit 776bba907c
3887 changed files with 59894 additions and 7280 deletions

View file

@ -1,4 +1,4 @@
The dining philosophers problem illustrates non-composability of low-level synchronization primitives like [[semaphore]]s. It is a modification of a problem posed by Edsger Dijkstra.
The dining philosophers problem illustrates non-composability of low-level synchronization primitives like [[semaphore]]s. It is a modification of a problem posed by [https://en.wikipedia.org/wiki/Edsger_W._Dijkstra Edsger Dijkstra.]
Five philosophers, Aristotle, Kant, Spinoza, Marx, and Russell (the [[task]]s) spend their time thinking and eating spaghetti. They eat at a round table with five individual seats. For eating each philosopher needs two forks (the resources). There are five forks on the table, one left and one right of each seat. When a philosopher cannot grab both forks it sits and waits. Eating takes random time, then the philosopher puts the forks down and leaves the dining room. After spending some random time thinking about the nature of the universe, he again becomes hungry, and the circle repeats itself.

View file

@ -81,9 +81,9 @@ private:
// attempt to grab forks
if( can_grab_fork( fork_left ) ) {
ForkLock::lock_guard lock_left( *fork_left, boost::adopt_lock );
ForkLock lock_left( *fork_left, boost::adopt_lock );
if( can_grab_fork( fork_right ) ) {
ForkLock::lock_guard lock_right( *fork_right, boost::adopt_lock );
ForkLock lock_right( *fork_right, boost::adopt_lock );
// eating
mp_logger->log( boost::str( boost::format( "%1% is eating (%2%)..." ) % m_name % m_meals_left ) );
wait();

View file

@ -31,7 +31,7 @@ void main() {
fork = new Mutex();
defaultPoolThreads = forks.length;
foreach (uint i, philo; taskPool.parallel(philosophers)) {
foreach (i, philo; taskPool.parallel(philosophers)) {
foreach (_; 0 .. 100) {
eat(i, philo, forks);
think(philo);

View file

@ -0,0 +1,65 @@
module Philosophers where
import Control.Monad
import Control.Concurrent
import Control.Concurrent.STM
import System.Random
-- TMVars are transactional references. They can only be used in transactional actions.
-- They are either empty or contain one value. Taking an empty reference fails and
-- putting a value in a full reference fails. A transactional action only succeeds
-- when all the component actions succeed, else it rolls back and retries until it
-- succeeds.
-- The Int is just for display purposes.
type Fork = TMVar Int
newFork :: Int -> IO Fork
newFork i = newTMVarIO i
-- The basic transactional operations on forks
takeFork :: Fork -> STM Int
takeFork fork = takeTMVar fork
releaseFork :: Int -> Fork -> STM ()
releaseFork i fork = putTMVar fork i
type Name = String
runPhilosopher :: Name -> (Fork, Fork) -> IO ()
runPhilosopher name (left, right) = forever $ do
putStrLn (name ++ " is hungry.")
-- Run the transactional action atomically.
-- The type system ensures this is the only way to run transactional actions.
(leftNum, rightNum) <- atomically $ do
leftNum <- takeFork left
rightNum <- takeFork right
return (leftNum, rightNum)
putStrLn (name ++ " got forks " ++ show leftNum ++ " and " ++ show rightNum ++ " and is now eating.")
delay <- randomRIO (1,10)
threadDelay (delay * 1000000) -- 1, 10 seconds. threadDelay uses nanoseconds.
putStrLn (name ++ " is done eating. Going back to thinking.")
atomically $ do
releaseFork leftNum left
releaseFork rightNum right
delay <- randomRIO (1, 10)
threadDelay (delay * 1000000)
philosophers :: [String]
philosophers = ["Aristotle", "Kant", "Spinoza", "Marx", "Russel"]
main = do
forks <- mapM newFork [1..5]
let namedPhilosophers = map runPhilosopher philosophers
forkPairs = zip forks (tail . cycle $ forks)
philosophersWithForks = zipWith ($) namedPhilosophers forkPairs
putStrLn "Running the philosophers. Press enter to quit."
mapM_ forkIO philosophersWithForks
-- All threads exit when the main thread exits.
getLine

View file

@ -0,0 +1,29 @@
global forks, names
procedure main(A)
names := ["Aristotle","Kant","Spinoza","Marks","Russell"]
write("^C to terminate")
nP := *names
forks := [: |mutex([])\nP :]
every p := !nP do thread philosopher(p)
delay(-1)
end
procedure philosopher(n)
f1 := forks[min(n, n%*forks+1)]
f2 := forks[max(n, n%*forks+1)]
repeat {
write(names[n]," thinking")
delay(1000*?5)
write(names[n]," hungry")
repeat {
fork1 := lock(f1)
if fork2 := trylock(f2) then {
write(names[n]," eating")
delay(1000*?5)
break (unlock(fork2), unlock(fork1)) # full
}
unlock(fork1) # Free first fork and go back to waiting
}
}
end

View file

@ -0,0 +1,50 @@
use threads;
use threads::shared;
my @names = qw(Aristotle Kant Spinoza Marx Russell);
my @forks = ('On Table') x @names;
share $forks[$_] for 0 .. $#forks;
sub pick_up_forks {
my $philosopher = shift;
my ($first, $second) = ($philosopher, $philosopher-1);
($first, $second) = ($second, $first) if $philosopher % 2;
for my $fork ( @forks[ $first, $second ] ) {
lock $fork;
cond_wait($fork) while $fork ne 'On Table';
$fork = 'In Hand';
}
}
sub drop_forks {
my $philosopher = shift;
for my $fork ( @forks[$philosopher, $philosopher-1] ) {
lock $fork;
die unless $fork eq 'In Hand';
$fork = 'On Table';
cond_signal($fork);
}
}
sub philosopher {
my $philosopher = shift;
my $name = $names[$philosopher];
for my $meal ( 1..5 ) {
print $name, " is pondering\n";
sleep 1 + rand 8;
print $name, " is hungry\n";
pick_up_forks( $philosopher );
print $name, " is eating\n";
sleep 1 + rand 8;
drop_forks( $philosopher );
}
print $name, " is done\n";
}
my @t = map { threads->new(\&philosopher, $_) } 0 .. $#names;
for my $thread ( @t ) {
$thread->join;
}
print "Done\n";
__END__

View file

@ -0,0 +1,99 @@
extern mod extra;
use std::rt::io::timer;
use extra::comm::DuplexStream;
fn phil(phil: ~str, diner: &DuplexStream<int, int>, firstChopstick: int, secondChopstick: int)
{
let mut sleep_time: u64;
print(fmt!("%s sat down\n", phil));
for _ in range(1,3)
{
print(fmt!("%s is thinking\n", phil));
sleep_time = std::rand::random();
timer::sleep((sleep_time%5)*500);
print(fmt!("%s is hungry\n", phil));
//get left chopstick
diner.send(firstChopstick);
let mut recv: int = diner.recv();
while recv == 0
{
diner.send(firstChopstick);
recv = diner.recv();
}
print(fmt!("%s picked up his left chopstick\n", phil));
//get right chopstick
diner.send(secondChopstick);
recv = diner.recv();
while recv == 0
{
diner.send(secondChopstick);
recv = diner.recv();
}
print(fmt!("%s picked up his right chopstick\n", phil));
//eat
print(fmt!("%s is eating...\n", phil));
sleep_time = std::rand::random();
timer::sleep((sleep_time%3)*500);
print(fmt!("%s is done eating\n", phil));
//set down left chopstick
print(fmt!("%s set down his left chopstick\n", phil));
diner.send(-1*firstChopstick);
//set down right chopstick
print(fmt!("%s set down his right chopstick\n", phil));
diner.send(-1*secondChopstick);
}
diner.send(0);
diner.recv();
print(fmt!("%s has exited\n", phil));
}
fn main()
{
//set the table:
//false means chopstick is on the table
//true means chopstick is taken
let mut chopsticks: ~[bool] = ~[false, false, false, false, false];
//diner_ will try to take resources from the table, host_
//will respond with whether that action was successful.
let (diner1, host1) = DuplexStream();
let (diner2, host2) = DuplexStream();
let (diner3, host3) = DuplexStream();
let (diner4, host4) = DuplexStream();
let (diner5, host5) = DuplexStream();
//Make the first 4 "right-handed" philosophers
do spawn{ phil(~"Hobbes", &diner1, 1, 2); }
do spawn{ phil(~"Locke", &diner2, 2, 3); }
do spawn{ phil(~"Machiavelli", &diner3, 3, 4); }
do spawn{ phil(~"Montesquieu", &diner4, 4, 5); }
//Make the last, "left-handed" philosopher
do spawn{ phil(~"Rousseau", &diner5, 1, 5); }
//keep track of number of people still at the table
let mut remaining = 5;
while remaining > 0
{
matchReq(&mut chopsticks, &host1, &mut remaining);
matchReq(&mut chopsticks, &host2, &mut remaining);
matchReq(&mut chopsticks, &host3, &mut remaining);
matchReq(&mut chopsticks, &host4, &mut remaining);
matchReq(&mut chopsticks, &host5, &mut remaining);
std::task::deschedule();
}
}
fn matchReq(chopsticks: &mut ~[bool], host: &DuplexStream<int, int>, remaining: &mut int) {
if (host.peek())
{
let from = host.try_recv();
match from
{
Some(0) => { *remaining += -1; host.try_send(0); return; },
Some(x) if x > 0 => { if(chopsticks[x-1]) { host.send(0); } else { chopsticks[x-1] = true; host.send(1); } },
Some(x) => { chopsticks[(-x)-1] = false; },
None => { *remaining += -1; host.try_send(0); return; }
}
}
}