mirror of
				https://github.com/KevinMidboe/linguist.git
				synced 2025-10-29 17:50:22 +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")
 | |
| %}
 |