Skip to content

Instantly share code, notes, and snippets.

@dustingetz
Last active September 28, 2022 18:05
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 dustingetz/62425ef8733fb0daff2e8a9aef54ca15 to your computer and use it in GitHub Desktop.
Save dustingetz/62425ef8733fb0daff2e8a9aef54ca15 to your computer and use it in GitHub Desktop.
binding conveyance
(defmacro future
"Takes a body of expressions and yields a future object that will
invoke the body in another thread, and will cache the result and
return it on all subsequent calls to deref/@. If the computation has
not yet finished, calls to deref/@ will block, unless the variant of
deref with timeout is used. See also - realized?."
{:added "1.1"}
[& body] `(future-call (^{:once true} fn* [] ~@body)))
(defn future-call
"Takes a function of no args and yields a future object that will
invoke the function in another thread, and will cache the result and
return it on all subsequent calls to deref/@. If the computation has
not yet finished, calls to deref/@ will block, unless the variant
of deref with timeout is used. See also - realized?."
{:added "1.1"
:static true}
[f]
(let [f (binding-conveyor-fn f)
fut (.submit clojure.lang.Agent/soloExecutor ^Callable f)]
(reify
clojure.lang.IDeref
(deref [_] (deref-future fut))
clojure.lang.IBlockingDeref
(deref
[_ timeout-ms timeout-val]
(deref-future fut timeout-ms timeout-val))
clojure.lang.IPending
(isRealized [_] (.isDone fut))
java.util.concurrent.Future
(get [_] (.get fut))
(get [_ timeout unit] (.get fut timeout unit))
(isCancelled [_] (.isCancelled fut))
(isDone [_] (.isDone fut))
(cancel [_ interrupt?] (.cancel fut interrupt?)))))
(defn binding-conveyor-fn
{:private true
:added "1.3"}
[f]
(let [frame (clojure.lang.Var/cloneThreadBindingFrame)]
(fn
([]
(clojure.lang.Var/resetThreadBindingFrame frame)
(f))
([x]
(clojure.lang.Var/resetThreadBindingFrame frame)
(f x))
([x y]
(clojure.lang.Var/resetThreadBindingFrame frame)
(f x y))
([x y z]
(clojure.lang.Var/resetThreadBindingFrame frame)
(f x y z))
([x y z & args]
(clojure.lang.Var/resetThreadBindingFrame frame)
(apply f x y z args)))))
// https://github.com/clojure/clojure/blob/5ffe3833508495ca7c635d47ad7a1c8b820eab76/src/jvm/clojure/lang/Var.java#L71-L106
package clojure.lang;
...
public final class Var extends ARef implements IFn, IRef, Settable, Serializable{
...
static class Frame{
final static Frame TOP = new Frame(PersistentHashMap.EMPTY, null);
//Var->TBox
Associative bindings;
//Var->val
// Associative frameBindings;
Frame prev;
public Frame(Associative bindings, Frame prev){
// this.frameBindings = frameBindings;
this.bindings = bindings;
this.prev = prev;
}
protected Object clone() {
return new Frame(this.bindings, null);
}
}
static final ThreadLocal<Frame> dvals = new ThreadLocal<Frame>(){
protected Frame initialValue(){
return Frame.TOP;
}
};
...
volatile Object root;
volatile boolean dynamic = false;
transient final AtomicBoolean threadBound;
public final Symbol sym;
public final Namespace ns;
//IPersistentMap _meta;
public static Object getThreadBindingFrame(){
return dvals.get();
}
public static Object cloneThreadBindingFrame(){
return dvals.get().clone();
}
public static void resetThreadBindingFrame(Object frame){
dvals.set((Frame) frame);
}
public Var setDynamic(){
this.dynamic = true;
return this;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment