After a fork is used, it becomes a "dirty" fork and needs to be put in a tray for dirty forks. There is a cleaner who cleans dirty forks and then puts them back on the table.
fun{a:vt0p} channel_insert (channel (a), a): void fun{a:vt0p} channel_takeout (chan: channel (a)): (a)If [channel_insert] is called on a channel that is full, then the caller is blocked until an element is taken out of the channel. If [channel_takeout] is called on a channel that is empty, then the caller is blocked until an element is inserted into the channel.
fun fork_changet (n: nphil): channel(fork)
where the type [nphil] is defined to be [natLt(5)] (for natural numbers
less than 5). The channels for storing forks are chosen to be of capacity
2. The reason that channels of capacity 2 are chosen to store at most one
element (in each of them) is to guarantee that these channels can never be
full (so that there is no attempt made to send signals to awake callers
supposedly being blocked due to channels being full).
fun forktray_changet ((*void*)): channel(fork)
The capacity chosen for the channel is 6 (instead of 5) so that it can
never become full (as there are only 5 forks in total).
implement phil_loop (n) = let // val () = phil_think (n) // val nl = phil_left (n) // = n val nr = phil_right (n) // = (n+1) % 5 // val ch_lfork = fork_changet (nl) val ch_rfork = fork_changet (nr) // val lf = channel_takeout (ch_lfork) val () = println! ("phil_loop(", n, ") picks left fork") // val () = randsleep (2) // sleep up to 2 seconds // val rf = channel_takeout (ch_rfork) val () = println! ("phil_loop(", n, ") picks right fork") // val () = phil_dine (n, lf, rf) // val ch_forktray = forktray_changet () val () = channel_insert (ch_forktray, lf) // left fork to dirty tray val () = channel_insert (ch_forktray, rf) // right fork to dirty tray // in phil_loop (n) end // end of [phil_loop]It should be straighforward to follow the code for [phil_loop].
implement cleaner_loop () = let // val ch = forktray_changet () val f0 = channel_takeout (ch) // [f0] is dirty // val () = cleaner_wash (f0) // washes dirty [f0] val () = cleaner_return (f0) // puts back cleaned [f0] // in cleaner_loop () end // end of [cleaner_loop]The function [cleaner_return] first finds out the number of a given fork and then uses the number to locate the channel for storing the fork. Its actual implementation is given as follows:
implement
cleaner_return (f) =
{
val n = fork_get_num (f)
val ch = fork_changet (n)
val () = channel_insert (ch, f)
}
It should now be straighforward to follow the code for [cleaner_loop].
DiningPhil2.sats DiningPhil2.dats DiningPhil2_fork.dats DiningPhil2_mylib.datsThere is also a Makefile available for compiling the ATS source code into an excutable for testing. One should be able to encounter a deadlock after running the simulation for a while.
This article is written by Hongwei Xi.