leonoel / missionary

A functional effect and streaming system for Clojure/Script
Eclipse Public License 2.0
620 stars 25 forks source link

watch can discard latest value #89

Open leonoel opened 11 months ago

leonoel commented 11 months ago

Current implementation of watch uses the fourth argument of the watch callback to update its state. It is wrong, because references do not enforce an execution order on watches : if threads t1 and t2 update the same atom in state x with respectively f1 and f2 such that new state is (f2 (f1 x)), then a watch on this atom may serialize watch callbacks as t2 -> t1 and therefore discard (f2 (f1 x)), leaving final state to (f1 x).

Repro : should always return 2, but currently non-deterministically returns 1 or 2.

(let [!x (atom 0)
      inc! (m/via m/cpu (swap! !x inc))]
  (m/?
    (m/reduce (comp reduced {}) nil
      (m/sample (fn [x _] x)
        (m/watch !x)
        (m/ap (m/? (m/join {} inc! inc!)))))))

A correct implementation of watch should ignore the value passed to the callback and deref on transfer.

awb99 commented 1 week ago

Uiii .. This is a serious issue. Is it still unfixed? Thanks!

leonoel commented 1 week ago

It is still unfixed but if you're impacted you can use this implementation https://github.com/leonoel/missionary/issues/93#issuecomment-2067603294