typedef phil = intThe sole purpose of using [phil] instead of [int] as the type for philosophers is to make the presented code easier to follow.
The forks mentioned in the description of the problem refer to shared resources. Let us next introduce a linear abstract type for the forks:
absvt@ype fork = int
This declaration means that [fork] is an abstract viewtype (that is, a
linear abstract type) whose size equals the size of [int]. Please note
that the information on [fork] and [int] being of the same size is not
available until after typechecking when abstract types are replaced with
concrete types.
For a philosopher to acquire and release his left fork, the following functions can be called:
fun phil_acquire_lfork (n: phil): fork fun phil_release_lfork (n: phil, f: fork): voidFor a philosopher to acquire and release his right fork, the following functions can be called:
fun phil_acquire_rfork (n: phil): fork fun phil_release_rfork (n: phil, f: fork): voidAs forks are shared resources, these functions for acquiring and releasing forks should in general involve some sort of locking mechanism. If a fork is acquired, then it should be released after its use. By assigning a linear type to forks, we can rely on the type system of ATS to keep track of forks, ensuring their being handled properly.
extern fun phil_loop (n: phil): void implement phil_loop (n) = let val () = phil_think (n) val ((*void*)) = phil_dine (n) in phil_loop (n) end // end of [phil_loop]The function [phil_think], which requires no resources, can be implemented as follows:
extern fun phil_think (n: phil): void implement phil_think (n) = ( randsleep (10) // for sleeping up to 10 secs )The function [phil_dine], which involves acquiring and releasing resources, can be implemented as follows:
extern fun phil_dine (n: phil): void implement phil_dine (n) = let // val lf = phil_acquire_lfork (n) val () = randsleep (1) // for sleeping up to 1 secs val rf = phil_acquire_rfork (n) // val () = randsleep (3) // for sleeping up to 3 secs // val () = phil_release_lfork (n, lf) val () = phil_release_rfork (n, rf) // in // nothing end // end of [phil_dine]Note that both [lf] and [rf] are assigned the linear type [fork], making them linear values. As every linear value must be consumed in some way or returned, removing the call to [phil_release_lfork] (or [phil_release_rfork]) causes a type-error that can be readily detected during typechecking. This is a great advantage of assigning linear types to values representing resources.
The problem of 5-Dining-Philosphers was precisely introduced to study deadlocks and deadlock avoidance. If one tests the given implementation, he or she should likely to see a deadlock occurring in a few minutes. Please use the command [kill] to remove all of the deadlocked processes.
This article is written by Hongwei Xi.