@@ -33,9 +33,10 @@ to catch and handle."
33
33
)
34
34
(:import [java.util.concurrent.atomic AtomicLong]
35
35
[java.util.concurrent.locks Lock]
36
- [java.util.concurrent Executors Executor ThreadLocalRandom]
36
+ [java.util.concurrent Executors Executor ThreadLocalRandom ExecutorService ]
37
37
[java.util Arrays ArrayList]
38
- [clojure.lang Var]))
38
+ [clojure.lang Var]
39
+ [java.lang Thread$Builder]))
39
40
40
41
(alias 'core 'clojure.core)
41
42
@@ -465,6 +466,37 @@ to catch and handle."
465
466
(defonce ^:private ^Executor thread-macro-executor
466
467
(Executors/newCachedThreadPool (conc/counted-thread-factory " async-thread-macro-%d" true )))
467
468
469
+ (def ^ExecutorService io-thread-exec
470
+ (if (= " 21" (System/getProperty " java.vm.specification.version" ))
471
+ (eval '(Executors/newThreadPerTaskExecutor (-> (Thread/ofVirtual )
472
+ (Thread$Builder/.name " io-thread-" 0 )
473
+ .factory)))
474
+ thread-macro-executor))
475
+
476
+ (defmacro io-thread
477
+ " Asynchronously executes the body in a virtual thread, returning immediately
478
+ to the calling thread.
479
+
480
+ io-thread blocks should not (either directly or indirectly) perform operations
481
+ that may block indefinitely. Doing so risks pinning the virtual thread
482
+ to its carrier thread.
483
+
484
+ Returns a channel which will receive the result of the body when
485
+ completed"
486
+ [& body]
487
+ `(let [c# (chan 1 )
488
+ captured-bindings# (Var/getThreadBindingFrame )]
489
+ (.execute
490
+ io-thread-exec
491
+ (^:once fn* []
492
+ (Var/resetThreadBindingFrame captured-bindings#)
493
+ (try
494
+ (let [result# (do ~@body)]
495
+ (>!! c# result#))
496
+ (finally
497
+ (close! c#)))))
498
+ c#))
499
+
468
500
(defn thread-call
469
501
" Executes f in another thread, returning immediately to the calling
470
502
thread. Returns a channel which will receive the result of calling
0 commit comments