Skip to content

Instantly share code, notes, and snippets.

@danlentz
Last active August 29, 2015 14:14
Show Gist options
  • Save danlentz/301a0f056ffd9c6605a9 to your computer and use it in GitHub Desktop.
Save danlentz/301a0f056ffd9c6605a9 to your computer and use it in GitHub Desktop.
Thread-safe Monotonic Clock for Clojure
(ns clock)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Lock-Free Thread-safe Monotonic Clock
;; Dan Lentz <http://github.com/danlentz>
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; We use epoch stamp (milliseconds) and zero-pad the low order bits
;; of millisecond stamp and provide atomic incremental
;; uuids-this- second subcounter on the low order bits, which
;; guarantee that two timestamps never collide regardless of clock
;; precision.
;;
;; 113914335216380000 (+ (* (epoch-time) 10000) 100103040000000000)
;; 113914335216380001 first contending timestamp
;; 113914335216380002 second contending timestamp
;; ... and so forth
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(def ^:const +subcounter-resolution+ 9999)
(deftype State [^short seqid ^long millis])
(let [-state- (atom (->State 0 0))]
(defn monotonic-time ^long []
(let [^State new-state
(swap! -state-
(fn [^State current-state]
(loop [time-now (System/currentTimeMillis)]
(if-not (= (.millis current-state) time-now)
(->State 0 time-now)
(let [tt (.seqid current-state)]
(if (< tt +subcounter-resolution+)
(->State (inc tt) time-now)
(recur (System/currentTimeMillis))))))))]
(+ (.seqid new-state)
(+ (* (.millis new-state) 10000) 100103040000000000)))))
@danlentz
Copy link
Author

danlentz commented Feb 3, 2015

interesting discussion, @sbocq! Thanks for joining in!

@sbocq
Copy link

sbocq commented Feb 3, 2015

@danlentz Thanks! I'm looking forward for more ;)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment