Skip to content

Commit

Permalink
fixed bug in world-commit, added testcase to better cover this function
Browse files Browse the repository at this point in the history
  • Loading branch information
Tom Van Cutsem committed Aug 17, 2011
1 parent d8f68d0 commit 23676b0
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 23 deletions.
38 changes: 15 additions & 23 deletions src/worlds_v0.clj
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -57,32 +57,18 @@
(defn- known? [val] (defn- known? [val]
(not (identical? val DontKnow))) (not (identical? val DontKnow)))


;; This function ensures the "no surprises" property
;; i.e. a ref does not appear to change spontaneously in *this-world* when
;; it is updated in one of its parents.
;; This function assumes that *this-world* is not bound to nil (i.e. it should
;; not be called in the top-level world).
;; This is currently guaranteed as it is only invoked from lookup-in-parent-world,
;; which is itself only called when *this-world* is not nil (see w-deref)
(defn- mark-as-read [ref val]
;; if ref's :reads value does not exist or is bound to DontKnow
;; in this world, mark it as read before returning it
(if (identical? (get @(:reads *this-world*) ref DontKnow) DontKnow)
(swap! (:reads *this-world*) assoc ref val))
val)

;; NOTE: this function assumes that current-world is never *this-world*. ;; NOTE: this function assumes that current-world is never *this-world*.
;; Always call (deref ref) instead of (world-lookup *this-world* ref) ;; Always call (deref ref) instead of (lookup-in-parent-world *this-world* ref)
(defn- lookup-in-parent-world [current-world ref] (defn- lookup-in-parent-world [current-world ref]
(if (nil? current-world) (if (nil? current-world)
;; in top-level world, latest value is stored in ref itself ;; in top-level world, latest value is stored in ref itself
(mark-as-read ref @ref) @ref
(let [val (get @(:writes current-world) ref DontKnow)] (let [val (get @(:writes current-world) ref DontKnow)]
(if (known? val) (if (known? val)
(mark-as-read ref val) val
(let [val (get @(:reads current-world) ref DontKnow)] (let [val (get @(:reads current-world) ref DontKnow)]
(if (known? val) (if (known? val)
(mark-as-read ref val) val
(recur (:parent current-world) ref))))))) (recur (:parent current-world) ref)))))))


;; Note: this function requires that parent-world is non-nil ;; Note: this function requires that parent-world is non-nil
Expand All @@ -105,7 +91,7 @@
(fn [reads] (fn [reads]
(if (contains? reads ref) (if (contains? reads ref)
(assoc reads ref val) (assoc reads ref val)
reads)) ref val)) reads))))
;; clear child-world's :reads and :writes ;; clear child-world's :reads and :writes
(reset! (:reads child-world) {}) (reset! (:reads child-world) {})
(reset! (:writes child-world) {})) (reset! (:writes child-world) {}))
Expand All @@ -126,8 +112,7 @@
(reset! ref val)) (reset! ref val))
;; propagate all of child-world's :reads to parent-world's :reads, ;; propagate all of child-world's :reads to parent-world's :reads,
;; except for refs that have already been read from in parent-world ;; except for refs that have already been read from in parent-world

;; Not necessary when committing to top-level, which has no :reads
;; SKIP when committing to top-level?


;; clear child-world's :reads and :writes ;; clear child-world's :reads and :writes
(reset! (:reads child-world) {}) (reset! (:reads child-world) {})
Expand All @@ -150,7 +135,14 @@
(let [val (get @(:reads *this-world*) ref DontKnow)] (let [val (get @(:reads *this-world*) ref DontKnow)]
(if (known? val) (if (known? val)
val val
(lookup-in-parent-world (:parent *this-world*) ref))))))) (let [val (lookup-in-parent-world (:parent *this-world*) ref)]
;; if ref's :reads value does not exist or is bound to DontKnow
;; in this world, mark it as read before returning it.
;; This ensures the "no surprises" property,
;; i.e. a ref does not appear to change spontaneously in
;; *this-world* when it is updated in one of its parents.
(swap! (:reads *this-world*) assoc ref val)
val)))))))


(defn w-ref-set (defn w-ref-set
[ref val] [ref val]
Expand Down Expand Up @@ -184,4 +176,4 @@
(if (not (nil? world)) (if (not (nil? world))
(if (nil? (:parent world)) (if (nil? (:parent world))
(world-commit-to-top world) (world-commit-to-top world)
(world-commit world (:parent world))))) (world-commit world (:parent world)))))
19 changes: 19 additions & 0 deletions test/test-worlds.clj
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -66,6 +66,25 @@
(in-world child (in-world child
(is (= 1 (w-deref r)))))) (is (= 1 (w-deref r))))))


(deftest test-commit-read-only
(let [parent (sprout (this-world))
child (sprout parent)
r (w-ref 0)]
(is (= parent (:parent child)))
(in-world child
(is (= 0 (w-deref r))))
(in-world parent
(is (= 0 (w-deref r))))
(in-world (this-world)
(is (= 0 (w-deref r))))
(commit child)
(in-world parent
(is (= 0 (w-deref r))))
(in-world (this-world)
(is (= 0 (w-deref r))))
(in-world child
(is (= 0 (w-deref r))))))

(deftest test-top-level-rw (deftest test-top-level-rw
(let [r (w-ref 0)] (let [r (w-ref 0)]
(is (= 0 (w-deref r))) (is (= 0 (w-deref r)))
Expand Down

0 comments on commit 23676b0

Please sign in to comment.