[fix] [#21] Work around issue with use in Cljs core.async/go bodies

Problem:
  (clojure.core.async/go (taoensso.telemere/log! "hello")) ; Compiles fine
  (cljs.core.async/go    (taoensso.telemere/log! "hello")) ; Compile fails

I could try to get to the bottom of exactly what's going on - but ultimately
IOC mechanisms like `go` are always going to be a bit fragile, especially for
heavily-optimized/unusual code.

In this case, the problem is thankfully only with Cljs - and Telemere's Cljs
performance isn't too critical - so I think we can afford to just bypass any
potential fiddling by the `go` macro by wrapping Cljs Telemere expansions in
an IIFE ((fn [] ...)).

Downside is the (small) added cost of a function construction and call.
Upside   is avoiding potential issues with core.async and other similar
IOC-style systems (Electric Clojure, etc.)
This commit is contained in:
Peter Taoussanis 2024-09-20 10:08:54 +02:00
parent 568906c96b
commit cbab57be66
3 changed files with 41 additions and 28 deletions

View file

@ -46,7 +46,8 @@
*unchecked-math* false #_:warn-on-boxed} *unchecked-math* false #_:warn-on-boxed}
:dependencies :dependencies
[[org.clojure/test.check "1.1.1"] [[org.clojure/core.async "1.6.681"]
[org.clojure/test.check "1.1.1"]
[org.clojure/tools.logging "1.3.0"] [org.clojure/tools.logging "1.3.0"]
[org.slf4j/slf4j-api "2.0.16"] [org.slf4j/slf4j-api "2.0.16"]
[com.taoensso/telemere-shell "1.0.0-SNAPSHOT"] [com.taoensso/telemere-shell "1.0.0-SNAPSHOT"]

View file

@ -687,6 +687,13 @@
(sig-middleware# signal#) ; Apply signal middleware, can throw (sig-middleware# signal#) ; Apply signal middleware, can throw
(do signal#))))) (do signal#)))))
;; Could avoid double `run-form` expansion with a fn wrap (>0 cost)
;; (let [run-fn-form (when run-form `(fn [] (~run-form)))]
;; `(let [~'run-fn-form ~run-fn-form]
;; (if-not ~allow?
;; (run-fn-form)
;; (let [...]))))
into-let-form into-let-form
(enc/cond! (enc/cond!
(not trace?) ; Don't trace (not trace?) ; Don't trace
@ -734,15 +741,9 @@
(enc/try* (enc/try*
(do (RunResult. ~run-form nil (- (enc/now-nano*) t0#))) (do (RunResult. ~run-form nil (- (enc/now-nano*) t0#)))
(catch :all t# (RunResult. nil t# (- (enc/now-nano*) t0#))) (catch :all t# (RunResult. nil t# (- (enc/now-nano*) t0#)))
(finally (.close otel-scope#))))))])] (finally (.close otel-scope#))))))])
;; Could avoid double `run-form` expansion with a fn wrap (>0 cost)
;; (let [run-fn-form (when run-form `(fn [] (~run-form)))]
;; `(let [~'run-fn-form ~run-fn-form]
;; (if-not ~allow?
;; (run-fn-form)
;; (let [...]))))
final-form
;; Unless otherwise specified, allow errors to throw on call ;; Unless otherwise specified, allow errors to throw on call
`(let [~'__kind ~kind-form `(let [~'__kind ~kind-form
~'__ns ~ns-form ~'__ns ~ns-form
@ -766,7 +767,11 @@
(if ~'__run-result (if ~'__run-result
( ~'__run-result signal#) ( ~'__run-result signal#)
true))))))))) true))))]
(if cljs?
`((fn [] ~final-form)) ; IIFE wrap for use in `go` and other IOC-style bodies
(do final-form)))))))
(comment (comment
(with-signal (signal! {:level :warn :let [x :x] :msg ["Test" "message" x] :data {:a :A :x x} :run (+ 1 2)})) (with-signal (signal! {:level :warn :let [x :x] :msg ["Test" "message" x] :data {:a :A :x x} :run (+ 1 2)}))

View file

@ -1,6 +1,7 @@
(ns taoensso.telemere-tests (ns taoensso.telemere-tests
(:require (:require
[clojure.test :as test :refer [deftest testing is]] [clojure.test :as test :refer [deftest testing is]]
[clojure.core.async :as async]
[taoensso.encore :as enc :refer [throws? submap?] :rename {submap? sm?}] [taoensso.encore :as enc :refer [throws? submap?] :rename {submap? sm?}]
[taoensso.encore.signals :as sigs] [taoensso.encore.signals :as sigs]
[taoensso.telemere :as tel] [taoensso.telemere :as tel]
@ -658,6 +659,12 @@
;; ;;
(do (enc/set-var-root! impl/*sig-handlers* nil) :unset-handler)])))]) (do (enc/set-var-root! impl/*sig-handlers* nil) :unset-handler)])))])
;;;;
(deftest _core-async
(testing "Signals in go macros"
[(async/go (tel/log! "hello"))]))
;;;; Interop ;;;; Interop
(comment (def ^org.slf4j.Logger sl (org.slf4j.LoggerFactory/getLogger "my.class"))) (comment (def ^org.slf4j.Logger sl (org.slf4j.LoggerFactory/getLogger "my.class")))