我将clj-1472-2描述的here应用于clojure 1.8,但是当我使用这个修补版本的clojure来构建依赖于core.async的库时,编译失败了:
java.lang.IllegalArgumentException: Cannot assign to non-mutable: value, compiling:(clojure/core/memoize.clj:58:7)
Exception in thread "main" java.lang.IllegalArgumentException: Cannot assign to non-mutable: value, compiling:(clojure/core/memoize.clj:58:7)
由于对锁定宏的修补更改,在RetryingDelay(memoize.clj)中会发生这种情况:
(deftype RetryingDelay [fun ^:volatile-mutable available? ^:volatile-mutable value]
clojure.lang.IDeref
(deref [this]
;; first check (safe with volatile flag)
(if available?
value
(locking fun
;; second check (race condition with locking)
(if available?
value
(do
;; fun may throw - will retry on next deref
(let [v (fun)]
;; this ordering is important - MUST set value before setting available?
;; or you have a race with the first check above
(set! value v)
(set! available? true)
v)))))))
在补丁后锁定宏:
(defmacro locking
"Executes exprs in an implicit do, while holding the monitor of x.
Will release the monitor of x in all circumstances."
{:added "1.0"}
[x & body]
`(let [lockee# ~x]
(clojure.lang.Util/lock lockee# (^{:once true} fn* [] ~@body))))
Util / lock是:
static public Object lock(final Object lockee, final IFn f) {
synchronized(lockee) {
return f.invoke();
}
}
为什么会出现此错误?
解决它会有什么适当的改变?
更新
我应该提到,尝试构建修补版本的clojure的原因是能够在Android上运行用clojure编写的jar(Lollipop及更高版本)作为java应用程序的一部分(不是clojure中的完整应用程序) ),CLJ-1472提供了更多信息。
答案 0 :(得分:0)
我认为这是因为locking
宏将locking
的正文放入fn
并且fn
内部public class RetryingDelay implements clojure.lang.IDeref {
private final IFn fun;
private volatile boolean available;
private volatile Object value;
public RetryingDelay(IFn fun) {
this.fun = fun;
}
public Object deref() {
if(available) {
return value;
} else {
synchronized(fun) {
if(available) {
return value;
} else {
Object v = fun.invoke();
value = v;
available = true;
return v;
}
}
}
}
}
您无权访问{{1}}的可变字段外部定义范围。
实现像这样的低级别的东西最简单的方法就是用Java编写,而不是用Clojure编写(这样你也不需要依赖一个未应用的补丁)。
像这样(我没有尝试编译它,所以这可能有小错误):
{{1}}