# From: "Dat Nguyen"
# Subject: DiningAndDrinkingPhilosophers
# Date: Thu, 06 May 2004 10:43:20 -0400
#
# Besides the binary semaphore which flipflops to allow access to a resource 
# one at a time, regular semaphore mediates multiple requests to a resource. 
# This element can be realized in C-Kermit easily. I offer the Philosophers 
# some bottles wine which are managed by a winepool object. The philosopher 
# object sends the request to the winepool which assigns an availble bottle to 
# the philosopher or puts him on a queue for the bottle with the least numbers 
# of waiting philosophers for it. To create a fierce competition for the wine, 
# only a few bottles are to serve all philosophers. The more philosophers 
# there are, some more bottles are served.
# 
# Since the original concept is solid, it can accomdate this extension 
# smoothly.
# 
# Please enjoy the modern version of the philosopher society: The 
# DiningAndDrinkingPhilosophers attached here.
# 
take class

define randnum {
# \%1 random seed
    while true {
        if {\fsexpr(> (let rd \frandom(\%1)) 0)} return \m(rd)
    }
}

class Semaphore

define Semaphore::new: {
    (setq \%1.Semaphore.stat -1)
    _asg \%1.Semaphore.Queue |
    (\%1)
}

define Semaphore>>acquire {
    (++ \%1.Semaphore.stat)
    if > \%1.Semaphore.stat 0 {
        _asg \%1.Semaphore.Queue \m(\%1.Semaphore.Queue)\%2|
        echo \%2 waits for \m(\%2.\%1.ChopStick)
    } else {
        echo \%2 grabs \m(\%2.\%1.ChopStick)
    }
    (! \%1.Semaphore.stat)
}

define Semaphore>>release {
    (-- \%1.Semaphore.stat)
    local \&w[] 
    if > \fsplit(\m(\%1.Semaphore.Queue),&w,|) 0 {
        _asg \%1.Semaphore.Queue \freplace(\m(\%1.Semaphore.Queue),|\&w[1]|,|)
        DiningPhilosophers add: \&w[1]
        echo \&w[1] grabs \m(\&w[1].\%1.ChopStick)
    }
    (\%1)
}

define Semaphore>>inspect {
    echo \%1 \m(\%1.Semaphore.stat)
    echo \%1 \m(\%1.Semaphore.Queue)
}

class Bottle
define Bottle::new: {
    (setq \%1.Bottle.stat -1)
    _asg \%1.Bottle.Queue |
    (\%1)
}
define Bottle>>val {
    (\%1.Bottle.stat)
}
define Bottle>>acquire {
    (++ \%1.Bottle.stat)
    if > \%1.Bottle.stat 0 {
        _asg \%1.Bottle.Queue \m(\%1.Bottle.Queue)\%2|
        echo \%2 waits for bottle \%1
    } else {
        echo \%2 grabs bottle \%1
    }
    (! \%1.Bottle.stat)
}
define Bottle>>release {
    (-- \%1.Bottle.stat)
    echo \%2 returns \%1
    local \&w[] 
    if > \fsplit(\m(\%1.Bottle.Queue),&w,|) 0 {
        _asg \%1.Bottle.Queue \freplace(\m(\%1.Bottle.Queue),|\&w[1]|,|)
        DiningPhilosophers add: \&w[1]
        echo \&w[1] grabs Bottle \%1
    }
    (\%1)
}

class Philosopher

define Philosopher::new:leftChopStick:rightChopStick:place: {
# \%2 leftChopStick
# \%3 rightChopStick
# \%4 no. seat
    if {\fsexpr(mod \%4 2)} {       # odd
        _asg \%1.firstChopStick \%2
        _asg \%1.secondChopStick \%3
    } else {            # even
        _asg \%1.firstChopStick \%3
        _asg \%1.secondChopStick \%2
    }
    _asg \%1.\%2.ChopStick left ChopStick
    _asg \%1.\%3.ChopStick right ChopStick
    _asg \%1.Philosopher.act thinking
    (setq \%1.Philosopher.timeRepeat (randnum 15))
    (\%1)
}

define Philosopher>>act {
    if {\fsexpr(> (-- \%1.Philosopher.timeRepeat) 0)} return 1
    (setq \%1.Philosopher.timeRepeat (randnum 50))
    (\%1 '\m(\%1.Philosopher.act))
}

define Philosopher>>thinking {
    echo \%1 is thinking
    _asg \%1.Philosopher.act getChopStick
    (1)     # line up in the process queue
}

define Philosopher>>getChopStick {
    (AND (\%1 'getFirstChopStick) (\%1 'getSecondChopStick))
}

define Philosopher>>getFirstChopStick {
    _asg \%1.Philosopher.act getSecondChopStick
    (\m(\%1.firstChopStick) 'acquire \%1)
}

define Philosopher>>getSecondChopStick {
    _asg \%1.Philosopher.act eat
    (\m(\%1.secondChopStick) 'acquire \%1)
}

define Philosopher>>eat {
    echo \%1 is eating
    _asg \%1.Philosopher.act grabBottle
    (1)
}

define Philosopher>>grabBottle {
    _asg \%1.Philosopher.act drink
    (WinePool 'grabBottle: '\%1)
}

define Philosopher>>drink {
    echo \%1 is drinking
    _asg \%1.Philosopher.act dropBottle
    (1)
}

define Philosopher>>dropBottle {
    _asg \%1.Philosopher.act release_ChopSticks
    WinePool dropBottle: \%1
    (1)
}

define Philosopher>>release_ChopSticks {
    echo \%1 releases both Chopsticks
    _asg \%1.Philosopher.act sleep
    (\m(\%1.firstChopStick) 'release)
    (\m(\%1.secondChopStick) 'release)
    (1)
}

define Philosopher>>sleep {
    echo \%1 is sleeping
    _asg \%1.Philosopher.act wakeup
    (1)
}

define Philosopher>>wakeup {
    echo \%1 wakes up
    _asg \%1.Philosopher.act thinking
    (1)
}

define Philosopher>>inspect {
    echo \fsexp(\m(\%1.leftChopStick) 'inspect) and \fsexp(\m(\%1.rightChopStick) 'inspect)
    (\%1)
}

class winePoolMgr
define winePoolMgr::new:numberOfBottles: {
    local \%i
    _asg \%1.winePool.numberOfBottles \%2
    for \%i 1 \%2 1 {
        Bottle new: \%1.Bottle.\%i
        _asg \%1.Bottle.\%i.has |
    }
}

define winePoolMgr>>grabBottle: {
    local \%i \%m \%k
    (setq \\\\%m 9999999)
    for \%i 1 \%1.winePool.numberOfBottles 1 {
        (if (< (\%1.Bottle.\%i 'val) \%m)
            (. (setq \\\\%k \%i) (setq \\\\%m (\%1.Bottle.\%i 'val))
            )
        )
    }
    _asg \%1.\%2.hasBottle \%k      # Philosopher has this bottle
    (\%1.Bottle.\%k 'acquire \%2)
}

define winePoolMgr>>dropBottle: {
    (let \\\\%k \%1.\%2.hasBottle)
    \%1.Bottle.\%k release \%2
}

class Process

define Process>>run {
    while true {
        local \&w[]
        (if (\fsplit(\m(\%1.Process.Queue),&w,|)) (\%1 'remove: '\&w[1]))
        (if (\&w[1] 'act) (\%1 'add: \&w[1]))
    }
}

define Process>>add: {
    _asg \%1.Process.Queue \m(\%1.Process.Queue)\%2|
    (\%1)
}

define Process>>remove: {
    _asg \%1.Process.Queue \freplace(\m(\%1.Process.Queue),|\%2|,|)
    (\%1)
}

define Process>>inspect {
    echo \m(\%1.Process.Queue)
    (\%1)
}

define Process::new:number: {
# \%2   Number of Philosophers and ChopSticks
    _asg \%1.Process.Queue |
    local \%i \%n
    for \%i 1 \%2 1 {
        Semaphore new: chopstk_\%i
    }
    for \%i 1 \%2 1 {
        (let \\\\%L \%i \\\\%R (+ (mod \%i \%2) 1))
        Philosopher new: Philosopher_\%i leftChopStick: chopstk_\%L rightChopStick: chopstk_\%R place: \%i
        \%1 add: Philosopher_\%i
    }
    (setq \\\\%n (floor (/ \%2 7)))
    (if (== 0 \%n) (setq \\\\%n 1))
    winePoolmgr new: winePool numberOfBottles: \%n
    (\%1)
}

define ASKME {
    local \%n
    while true {
        ask \%n { Number of Philosophers: }
        if not def \%n continue
        if not numeric \%n {
            echo Not numeric - "\%n"
            continue
        }
        break
    }
    if < \%n 2 {
        echo One needs at least two chopsticks to eat rice. Sorry!
        EXIT
    }
    return \%n
}

(Process 'new: 'DiningPhilosophers 'number: (askme))
DiningPhilosophers run
