Welcome! Please see the About page for a little more info on how this works.

+5 votes
in Namespaces and vars by
recategorized by

Hello! I've been using a few of the functions from clojure.tools.build.api in a future and I sporadically see this error...

Execution error (IllegalStateException) at clojure.tools.build.api/create-basis (api.clj:154).
Attempting to call unbound fn: #'clojure.tools.build.tasks.create-basis/create-basis

From what I see in tools.build...

(defn create-basis
  ([]
   ((requiring-resolve 'clojure.tools.build.tasks.create-basis/create-basis)))
  ([params]
   ((requiring-resolve 'clojure.tools.build.tasks.create-basis/create-basis) params)))

...the code is only really using requiring-resolve to execute another function. Diving deeper into requiring-resolve I can see that the first call to resolve is not synchronized with the same lock that the serialized-require function uses...

(defn- serialized-require
  [& args]
  (locking clojure.lang.RT/REQUIRE_LOCK
    (apply require args)))

(defn requiring-resolve
  [sym]
  (if (qualified-symbol? sym)
    (or (resolve sym)
        (do (-> sym namespace symbol serialized-require)
            (resolve sym)))
    (throw (IllegalArgumentException. (str "Not a qualified symbol: " sym)))))

First, is it possible requiring-resolve is actually returning clojure.lang.Var$Unbound from the first call to resolve when another thread is in the process of requiring that namespace via serialized-require? Assuming so, should tools.build (and I guess by extension most Clojure code) avoid using the ((requiring-resolve 'some-other/function)) pattern? Is it possible requiring-resolve could be modified to be thread safe?

Thanks!

1 Answer

0 votes
by

The intent of requiring-resolve is to be thread-safe, so if anything, this should be treated as an issue for Clojure. Really, requiring-resolve is a halfway point to making require itself thread-safe which is a bigger and more complicated issue (example report at https://clojure.atlassian.net/browse/CLJ-1876). The tendrils of this go pretty deep and are ultimately related to namespaces not being inmutable (we should know better!).

Anyhow, I have filed this here:

https://clojure.atlassian.net/browse/CLJ-2735

...