mirror of
https://github.com/KevinMidboe/linguist.git
synced 2025-10-29 09:40:21 +00:00
216 lines
5.3 KiB
Plaintext
216 lines
5.3 KiB
Plaintext
%{
|
|
#include "./../ATEXT/atextfun.hats"
|
|
%}
|
|
|
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
|
|
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
|
|
<html xmlns="http://www.w3.org/1999/xhtml">
|
|
|
|
<head>
|
|
<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
|
|
<title>EFFECTIVATS-DiningPhil2</title>
|
|
#patscode_style()
|
|
</head>
|
|
|
|
<body>
|
|
|
|
<h1>
|
|
Effective ATS: Dining Philosophers
|
|
</h1>
|
|
|
|
In this article, I present an implementation of a slight variant of the
|
|
famous problem of 5-Dining-Philosophers by Dijkstra that makes simple but
|
|
convincing use of linear types.
|
|
|
|
<h2>
|
|
The Original Problem
|
|
</h2>
|
|
|
|
There are five philosophers sitting around a table and there are also 5
|
|
forks placed on the table such that each fork is located between the left
|
|
hand of a philosopher and the right hand of another philosopher. Each
|
|
philosopher does the following routine repeatedly: thinking and dining. In
|
|
order to dine, a philosopher needs to first acquire two forks: one located
|
|
on his left-hand side and the other on his right-hand side. After
|
|
finishing dining, a philosopher puts the two acquired forks onto the table:
|
|
one on his left-hand side and the other on his right-hand side.
|
|
|
|
<h2>
|
|
A Variant of the Original Problem
|
|
</h2>
|
|
|
|
The following twist is added to the original version:
|
|
|
|
<p>
|
|
|
|
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.
|
|
|
|
<h2>
|
|
Channels for Communication
|
|
</h2>
|
|
|
|
A channel is just a shared queue of fixed capacity. The following two
|
|
functions are for inserting an element into and taking an element out of a
|
|
given channel:
|
|
|
|
<pre
|
|
class="patsyntax">
|
|
#pats2xhtml_sats("\
|
|
fun{a:vt0p} channel_insert (channel (a), a): void
|
|
fun{a:vt0p} channel_takeout (chan: channel (a)): (a)
|
|
")</pre>
|
|
|
|
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.
|
|
|
|
<h2>
|
|
A Channel for Each Fork
|
|
</h2>
|
|
|
|
Forks are resources given a linear type. Each fork is initially stored in a
|
|
channel, which can be obtained by calling the following function:
|
|
|
|
<pre
|
|
class="patsyntax">
|
|
#pats2xhtml_sats("\
|
|
fun fork_changet (n: nphil): channel(fork)
|
|
")</pre>
|
|
|
|
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).
|
|
|
|
|
|
<h2>
|
|
A Channel for the Fork Tray
|
|
</h2>
|
|
|
|
A tray for storing "dirty" forks is also a channel, which can be obtained
|
|
by calling the following function:
|
|
|
|
<pre
|
|
class="patsyntax">
|
|
#pats2xhtml_sats("\
|
|
fun forktray_changet ((*void*)): channel(fork)
|
|
")</pre>
|
|
|
|
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).
|
|
|
|
<h2>
|
|
Philosopher Loop
|
|
</h2>
|
|
|
|
Each philosopher is implemented as a loop:
|
|
|
|
<pre
|
|
class="patsyntax">
|
|
#pats2xhtml_dats('\
|
|
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]
|
|
')</pre>
|
|
|
|
It should be straighforward to follow the code for [phil_loop].
|
|
|
|
<h2>
|
|
Fork Cleaner Loop
|
|
</h2>
|
|
|
|
A cleaner is implemented as a loop:
|
|
|
|
<pre
|
|
class="patsyntax">
|
|
#pats2xhtml_dats('\
|
|
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]
|
|
')</pre>
|
|
|
|
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:
|
|
|
|
<pre
|
|
class="patsyntax">
|
|
#pats2xhtml_dats('\
|
|
implement
|
|
cleaner_return (f) =
|
|
{
|
|
val n = fork_get_num (f)
|
|
val ch = fork_changet (n)
|
|
val () = channel_insert (ch, f)
|
|
}
|
|
')</pre>
|
|
|
|
It should now be straighforward to follow the code for [cleaner_loop].
|
|
|
|
<h2>
|
|
Testing
|
|
</h2>
|
|
|
|
The entire code of this implementation is stored in the following files:
|
|
|
|
<pre>
|
|
DiningPhil2.sats
|
|
DiningPhil2.dats
|
|
DiningPhil2_fork.dats
|
|
DiningPhil2_thread.dats
|
|
</pre>
|
|
|
|
There 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.
|
|
|
|
<hr size="2">
|
|
|
|
This article is written by <a href="http://www.cs.bu.edu/~hwxi/">Hongwei Xi</a>.
|
|
|
|
</body>
|
|
</html>
|
|
|
|
%{
|
|
implement main () = fprint_filsub (stdout_ref, "main_atxt.txt")
|
|
%}
|