Skip to content

Instantly share code, notes, and snippets.

@bobpoekert
Last active December 12, 2023 02:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • 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.

@CreativeQuantum
Copy link

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