Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is wrong with the following solution to the "Dining Philosophers"?

In order to get familiar with STM in Haskell, I wrote the following solution to the Dining Philosophers problem:

import Control.Concurrent
import Control.Concurrent.STM
import Control.Monad
import System.Random

type Fork = TVar Bool
type StringBuffer = TChan String

philosopherNames :: [String]
philosopherNames = map show ([1..] :: [Int])

logThinking :: String -> StringBuffer -> STM ()
logThinking name buffer = writeTChan buffer $ name ++ " is thinking..."

logEating :: String -> StringBuffer -> STM ()
logEating name buffer = writeTChan buffer $ name ++ " is eating..."

firstLogEntry :: StringBuffer -> STM String
firstLogEntry buffer = do empty <- isEmptyTChan buffer
                          if empty then retry
                                   else readTChan buffer

takeForks :: Fork -> Fork -> STM ()
takeForks left right = do leftUsed <- readTVar left
                          rightUsed <- readTVar right
                          if leftUsed || rightUsed
                             then retry
                             else do writeTVar left True
                                     writeTVar right True

putForks :: Fork -> Fork -> STM ()
putForks left right = do writeTVar left False
                         writeTVar right False

philosopher :: String -> StringBuffer -> Fork -> Fork -> IO ()
philosopher name out left right = do atomically $ logThinking name out
                                     randomDelay
                                     atomically $ takeForks left right
                                     atomically $ logEating name out
                                     randomDelay
                                     atomically $ putForks left right

randomDelay :: IO ()
randomDelay = do delay <- getStdRandom(randomR (1,3))
                 threadDelay (delay * 1000000)

main :: IO ()
main = do let n = 8
          forks <- replicateM n $ newTVarIO False
          buffer <- newTChanIO
          forM_ [0 .. n - 1] $ \i ->
              do let left = forks !! i
                     right = forks !! ((i + 1) `mod` n)
                     name = philosopherNames !! i
                 forkIO $ forever $ philosopher name buffer left right

          forever $ do str <- atomically $ firstLogEntry buffer
                       putStrLn str

When I compile and run my solution, it seems that no obvious concurrency issues exist: Each philosopher will eventually eat and no philosopher seems to be favoured. However, if I remove the randomDelay statements from philosopher, compile and run, the output of my program looks like the following:

1 is thinking...
1 is eating...
1 is thinking...
1 is eating...
2 is thinking...
2 is eating...
2 is thinking...
2 is eating...
2 is thinking...
2 is eating...
2 is thinking...

About 2500 lines later...

2 is thinking...
2 is eating...
2 is thinking...
3 is thinking...
3 is eating...
3 is thinking...
3 is eating...

And so on...

What is happening in this case?

like image 880
Alexandros Avatar asked Aug 30 '12 18:08

Alexandros


1 Answers

You need to compile it with the threaded runtime and enabled rtsopts, and run it with +RTS -N (or +RTS -Nk where k is the number of threads. With that, I get output like

8 is eating...
6 is eating...
4 is thinking...
6 is thinking...
4 is eating...
7 is eating...
8 is thinking...
4 is thinking...
7 is thinking...
8 is eating...
4 is eating...
4 is thinking...
4 is eating...
6 is eating...
4 is thinking...

The point is that for another philosopher to think/eat, a context switch must happen if you don't have several hardware threads at your disposition. Such a context switch doesn't happen very often here, where not much allocation is done, so each philosopher has a lot of time to think and eat a lot before the next one's turn comes up.

With enough threads at your disposition, all philosophers can concurrently try to reach for the forks.

like image 99
Daniel Fischer Avatar answered Sep 26 '22 16:09

Daniel Fischer