Skip to content

Instantly share code, notes, and snippets.

@bobpoekert
Last active December 12, 2023 02:42
Show Gist options
  • Save bobpoekert/3acc954ed24333d5319dcbad6185ca99 to your computer and use it in GitHub Desktop.
Save bobpoekert/3acc954ed24333d5319dcbad6185ca99 to your computer and use it in GitHub Desktop.
clojure thread local
(ns thread-local
(import ThreadLocalThing))
(defn make-thread-local
[generator]
(ThreadLocalThing. ::initial-val generator))
(defmacro thread-local
[& body]
`(make-thread-local (fn [] ~@body)))
import clojure.lang.IFn;
import clojure.lang.IDeref;
import java.lang.ThreadLocal;
public class ThreadLocalThing extends ThreadLocal implements IDeref {
final Object sentinelValue;
final IFn generator;
public ThreadLocalThing(Object sentinelValue, IFn generator) {
this.sentinelValue = sentinelValue;
this.generator = generator;
}
@Override
public Object initialValue() {
return this.sentinelValue;
}
public Object deref() {
Object res = this.get();
if (res == this.sentinelValue) {
res = this.generator.invoke();
this.set(res);
}
return res;
}
}
@bobpoekert
Copy link
Author

bobpoekert commented Apr 10, 2017

Usage: (thread-local {{a thing that makes a thing}})

When deref'd, if there isn't a value for the current thread, the body expression will be evaluated to create one. Useful for things like database connections.

Copy link

ghost commented Dec 12, 2023

Thanks. My implementation using proxy instead of a java class. Supports deref + swap!,reset!,compare-and-set!

(defn thread-local [init]
  (let [init-f (if (fn? init) init (constantly init))]
    (proxy [ThreadLocal clojure.lang.IDeref clojure.lang.IAtom] []
      (initialValue [] (init-f))
      (deref        [] (proxy-super get))
      (swap ([f]                     (doto (      f (proxy-super get))                (->> (proxy-super set))))
            ([f arg]                 (doto (      f (proxy-super get) arg)            (->> (proxy-super set))))
            ([f arg1 arg2]           (doto (      f (proxy-super get) arg1 arg2)      (->> (proxy-super set))))
            ([f arg1 arg2 args]      (doto (apply f (proxy-super get) arg1 arg2 args) (->> (proxy-super set)))))
      (compareAndSet [oldv newv]     (when (= oldv (proxy-super get))
                                       (doto newv (->> (proxy-super set)))))
      (reset         [newv]          (doto   newv (->> (proxy-super set))))
      ;; Implement IAtom2 to support swap-vals! and reset-vals!
      ;; Implement IRef   to support watches and validators
      )))

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