# From: Dat Thuc Nguyen
# Date: Sun, 02 May 2004 18:09:41 -0400
# Subject: The Art of Programming in C-Kermit
#
# From time to time I sent in C-Kermit scripts that solve some problems people
# also solved in other programming languages.
#
# Today I am sending in a program that's difficult since it requires
# the notions of semaphore, process, timer and concurrency.
#
# The "dinning philosophers" problem is stated in Operating System Concepts by
# Peterson and Silberschatz, 1985, pp. 347-348.
#
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 {
    local s
    (setq s (++ \%1.Semaphore.stat))
    if > s 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)
    }
    (! s)
}

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 Philosopher

define Philosopher::new:leftChopStick:rightChopStick:number: {
# \%2 leftChopStick
# \%3 rightChopStick
    (setq \%1.leftChopStick \%2)
    (setq \%1.rightChopStick \%3)
    (setq \%1.Philosopher.Number \%4)
    _asg \%1.\%2.ChopStick left ChopStick
    _asg \%1.\%3.ChopStick right ChopStick
    _asg \%1.Philosopher.act Philosopher>>thinking
    (setq \%1.Philosopher.timeRepeat (randnum 15))
    (\%1)
}

define Philosopher>>act {
    (-- \%1.Philosopher.timeRepeat)
    if == \m(\%1.Philosopher.timeRepeat) 0 {
        (setq \%1.Philosopher.timeRepeat (randnum 50))
        return \fsexpr(\%1 '\m(\%1.Philosopher.act))
    } else {
        return 1    # line up in the process queue
    }
}

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

define Philosopher>>getFirstChopStick {
    local gotChopStick
    if {\fsexpr(mod \%1.Philosopher.Number 2)} {    # odd
        (setq gotChopStick (\m(\%1.leftChopStick) 'acquire \%1))
        if gotChopStick {
            _asg \%1.Philosopher.act Philosopher>>eat
            (setq gotChopStick (\m(\%1.rightChopStick) 'acquire \%1))
            if gotChopStick {
                return 1
            } else {
                return 0
            }
        }
    } else {    # even
        (setq gotChopStick (\m(\%1.rightChopStick) 'acquire \%1))
        if gotChopStick {
            _asg \%1.Philosopher.act Philosopher>>eat
            (setq gotChopStick (\m(\%1.leftChopStick) 'acquire \%1))
            if gotChopStick {
                return 1
            } else {
                return 0
            }
        }
    }
    _asg \%1.Philosopher.act Philosopher>>getSecondChopStick
    return 0    # wait for semaphore
}

define Philosopher>>getSecondChopStick {
    local gotChopStick
    _asg \%1.Philosopher.act Philosopher>>eat
    if {\fsexpr(mod \%1.Philosopher.Number 2)} {    # odd
        (setq gotChopStick (\m(\%1.rightChopStick) 'acquire \%1))
        if gotChopStick {
            return 1
        } else {
            return 0
        }
    } else {    # even
        (setq gotChopStick (\m(\%1.leftChopStick) 'acquire \%1))
        if gotChopStick {
            return 1
        } else {
            return 0
        }
    }
}

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

define Philosopher>>release_ChopSticks {
    _asg \%1.Philosopher.act Philosopher>>sleep
    (\m(\%1.leftChopStick) 'release)
    (\m(\%1.rightChopStick) 'release)
    (1)
}

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

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

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

class Process
define Process::new: {
    _asg \%1.Process.Queue |
    (\%1)
}

define Process>>run {
    while true {
        local \&w[] \%n
        asg \%n \fsplit(\m(\%1.Process.Queue),&w,|)
        if = 0 \%n break
        \%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)
}

# Create five ChopSticks:
Semaphore new: chopstk_1_2      # ChopStick between p1 and p2
Semaphore new: chopstk_2_3      # ChopStick between p2 and p3
Semaphore new: chopstk_3_4      # ChopStick between p3 and p4
Semaphore new: chopstk_4_5      # ChopStick between p4 and p5
Semaphore new: chopstk_5_1      # ChopStick between p5 and p1

# Create five Philosophers:
Philosopher new: Philosopher_1 leftChopStick: chopstk_5_1 rightChopStick: chopstk_1_2 number: 1
Philosopher new: Philosopher_2 leftChopStick: chopstk_1_2 rightChopStick: chopstk_2_3 number: 2 
Philosopher new: Philosopher_3 leftChopStick: chopstk_2_3 rightChopStick: chopstk_3_4 number: 3
Philosopher new: Philosopher_4 leftChopStick: chopstk_3_4 rightChopStick: chopstk_4_5 number: 4
Philosopher new: Philosopher_5 leftChopStick: chopstk_4_5 rightChopStick: chopstk_5_1 number: 5
 
Process new: DiningPhilosophers
for \%i 1 5 1 {
    DiningPhilosophers add: Philosopher_\%i
}
DiningPhilosophers run

# end
