Skip to content

Instantly share code, notes, and snippets.

@laurentpetit
Created December 30, 2010 02:03
Show Gist options
  • Save laurentpetit/759364 to your computer and use it in GitHub Desktop.
Save laurentpetit/759364 to your computer and use it in GitHub Desktop.
Dining philosophers solution
;;; 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