Created
December 30, 2010 02:03
-
-
Save laurentpetit/759364 to your computer and use it in GitHub Desktop.
Dining philosophers solution
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
;;; Dining philosophers. Solution using Clojure STM. | |
;;; This is sort of an implementation of the "Use monitor" solution: | |
;;; http://en.wikipedia.org/wiki/Dining_philosophers_problem#Monitor_solution | |
;;; What are our identities? | |
;;; The problem talks about forks, and philosophers. | |
;;; Conceptually, forks have a "taken-by" property which can have one | |
;;; of three values: :left-philosopher, :righ-philosopher, :nobody. | |
;;; Conceptually, philosophers have a "state" property which can be | |
;;; :eating or :thinking. | |
;;; Note that with an approach using STM for getting both forks at once | |
;;; atomically or none, and synchronizing the philosopher's value, we | |
;;; will always have the "taken-by" property of the forks and the "state" | |
;;; property of the philosopher synchronized. | |
;;; So we can altogether get rid of the fork concept, use refs for | |
;;; representing philosophers, and allow the change of the state of a | |
;;; philosopher to :eating by ensuring that his neighbours have the | |
;;; :thinking value in the same transaction | |
;;; For simulating true concurrent independent philosophers, we will have | |
;;; one thread per philosopher. Using "future" is just an easy trick for | |
;;; starting a new thread, and we do not really want to use "future" beyond | |
;;; its "will run the code in a separate thread" feature. | |
;;; Implementation notes: | |
;;; * philosopher "behaviour" is basic : thinks for a while, tries to eat, | |
;;; thinks for a while, stops eating, thinks for a while, tries to eat, | |
;;; thinkgs for a while, etc. | |
;;; * could be enhanced for graceful shutdown of the dinner, etc., but this | |
;;; would clutter with no real value to the essence of the solution | |
(def phils (repeatedly 5 #(ref :thinking))) | |
(let [a (agent nil)] | |
(defn snapshot [] | |
(send a (fn [_] (prn (->> phils (map deref) doall dosync)))))) | |
(defn react [val neighbours-vals] | |
(cond | |
(= :eating val) :thinking | |
(some #{:eating} neighbours-vals) val | |
:else :eating)) | |
(defn phil-fn [p neighbours] | |
(Thread/sleep (rand-int 100)) | |
(dosync (alter p react (map deref neighbours)))) | |
(defn start-lunch [] | |
(doseq [[left-phil phil right-phil] (take (count phils) | |
(partition 3 1 (cycle phils)))] | |
(add-watch phil nil #(when-not (= %3 %4) (snapshot))) | |
(future (while true (phil-fn phil [left-phil right-phil]))))) | |
;(start-lunch) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment