Skip to content

Commit 7fe249b

Browse files
committed
Handle conditional compilation via provided scope
core.async is now a dep with Maven "provided" scope, which means it's not a transitive dependency, and consumers must supply it. Minor docstring changes and typo fixes.
1 parent 61fcd61 commit 7fe249b

File tree

4 files changed

+66
-60
lines changed

4 files changed

+66
-60
lines changed

project.clj

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@
33
:license {:name "MIT License"
44
:url "http://opensource.org/licenses/MIT"}
55
:url "https://github.com/clj-commons/manifold"
6-
:dependencies [[org.clojure/tools.logging "0.3.1" :exclusions [org.clojure/clojure]]
6+
:dependencies [[org.clojure/clojure "1.10.3" :scope "provided"]
7+
[org.clojure/tools.logging "0.3.1" :exclusions [org.clojure/clojure]]
78
[io.aleph/dirigiste "0.1.6-alpha1"]
8-
[riddley "0.1.15"]]
9-
:profiles {:dev {:dependencies [[org.clojure/clojure "1.8.0"]
10-
[criterium "0.4.4"]
11-
[org.clojure/core.async "0.4.474"]]}}
9+
[riddley "0.1.15"]
10+
[org.clojure/core.async "1.4.627" :scope "provided"]]
11+
:profiles {:dev {:dependencies [[criterium "0.4.4"]]}}
1212
:test-selectors {:default #(not
1313
(some #{:benchmark :stress}
1414
(cons (:tag %) (keys %))))

src/manifold/go_off.clj

Lines changed: 41 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
(ns ^{:author "Ryan Smith"
2-
:doc "Provide a variant of `core.async/go` that works with manifold's deferreds and executors. Utilizes core.async's state-machine generator, so core.async must be available as a dependency."}
2+
:doc "Provide a variant of `core.async/go` that works with manifold's deferreds and executors. Utilizes core.async's state-machine generator, so core.async must be provided as a dependency."}
33
manifold.go-off
44
(:require [manifold
55
[executor :as ex]
@@ -16,19 +16,19 @@
1616
d))
1717

1818
(defn <!
19-
"takes value from deferred. Must be called inside a (go ...) block. Will
20-
return nil if closed. Will park if nothing is available. If an error
21-
is thrown inside the body, that error will be placed as the return value.
19+
"Takes value from a deferred/stream. Must be called inside a (go ...) block. Will
20+
return nil if a stream is closed. Will park if nothing is available. If an error
21+
is thrown inside the body, that error will be placed as the return value.
2222
23-
N.B. To make `go-off` usage idiomatic with the rest of manifold, use `<!?`
24-
instead of this directly."
23+
N.B. To make `go-off` usage idiomatic with the rest of manifold, use `<!?`
24+
instead."
2525
[port]
2626
(assert nil "<! used not in (go-off ...) block"))
2727

2828
(defmacro <!?
29-
"takes a val from port. Must be called inside a (go-off ...) block.
30-
Will park if nothing is available. If value that is returned is
31-
a Throwable, will re-throw."
29+
"Takes a val from a deferred/stream. Must be called inside a (go-off ...) block.
30+
Will park if nothing is available. If value that is returned is a Throwable,
31+
it will re-throw."
3232
[port]
3333
`(let [r# (<! ~port)]
3434
(if (instance? Throwable r#)
@@ -93,35 +93,39 @@
9393

9494
(defmacro go-off
9595
"Asynchronously executes the body on manifold's default executor, returning
96-
immediately to the calling thread. Additionally, any visible calls to <!?
97-
and <! deferred operations within the body will block (if necessary)
98-
by 'parking' the calling thread rather than tying up an OS thread.
99-
Upon completion of the operation, the body will be resumed.
100-
101-
Returns a deferred which will receive the result of the body when
102-
completed. If the body returns a deferred, the result will be unwrapped
103-
until a non-deferable value is available to be placed onto the return deferred.
104-
105-
This method is intended to be similar to `core.async/go`, and even utilizes the
106-
underlying state machine related functions from `core.async`. It's been designed
107-
to address the following major points from core.async & vanilla manifold deferreds:
108-
109-
- `core.async/go` assumes that all of your code is able to be purely async
110-
and will never block the handling threads. go-off removes the concept of handling
111-
threads, which means blocking is not an issue, but if you spawn too many of these you
112-
can create too many threads for the OS to handle.
113-
- `core.async/go` has absolutely no way of bubbling up exceptions and assumes all
114-
code will be defensively written, which differs from how clojure code blocks work
115-
outside of the async world.
116-
- `deferred/let-flow` presumes that every deferrable needs to be resolved. This prevents
117-
more complex handling of parallelism or being able to pass deferreds into other functions
118-
from within the `let-flow` block
119-
- `deferred/chain` only works with single deferreds, which means having to write code in
120-
unnatural ways to handle multiple deferreds."
96+
immediately to the calling thread. Additionally, any visible calls to <!?
97+
and <! deferred operations within the body will block (if necessary)
98+
by 'parking' the calling thread rather than tying up an OS thread.
99+
Upon completion of the operation, the body will be resumed.
100+
101+
Returns a deferred which will receive the result of the body when
102+
completed. If the body returns a deferred, the result will be unwrapped
103+
until a non-deferrable value is available to be placed onto the return deferred.
104+
105+
This method is intended to be similar to `core.async/go`, and even utilizes the
106+
underlying state machine-related functions from `core.async`. It's been designed
107+
to address the following major points from core.async & vanilla manifold deferreds:
108+
109+
- `core.async/go` assumes that all of your code is able to be purely async
110+
and will never block the handling threads. go-off removes the concept of handling
111+
threads, which means blocking is not an issue, but if you spawn too many of these you
112+
can create too many threads for the OS to handle.
113+
- `core.async/go` has no built-in way of handling exceptions and assumes all async
114+
code will be either written defensively, or have custom error propagation, which
115+
differs from how clojure code blocks typically work outside of the async world.
116+
- `deferred/let-flow` presumes that every deferrable needs to be resolved. This prevents
117+
more complex handling of parallelism or being able to pass deferreds into other functions
118+
from within the `let-flow` block.
119+
- `deferred/chain` only works with single deferreds, which means having to write code in
120+
unnatural ways to handle multiple deferreds."
121121
[& body]
122122
`(go-off-executor (ex/execute-pool) ~@body))
123123

124-
(go-off "cat")
125124

126-
@(go-off (+ (<!? (d/future 10))
127-
(<!? (d/future 20)))) ;; ==> 30
125+
(comment
126+
(go-off "cat")
127+
128+
@(go-off (+ (<!? (d/future 10))
129+
(<!? (d/future 20)))) ;; ==> 30
130+
131+
)

src/manifold/utils.clj

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,9 +109,11 @@
109109

110110
;;;
111111

112-
(defmacro when-core-async [& body]
112+
(defmacro when-core-async
113+
"Suitable for altering behavior (like extending protocols), but not defs"
114+
[& body]
113115
(when (try
114-
(require '[clojure.core.async :as a])
116+
(require '[clojure.core.async])
115117
true
116118
(catch Exception _
117119
false))

test/manifold/go_off_test.clj

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@
1616
(testing "case with go-off"
1717
(is (= :1
1818
@(go-off (case (name :1)
19-
"0" :0
20-
"1" :1
21-
:3)))))
19+
"0" :0
20+
"1" :1
21+
:3)))))
2222

2323
(testing "nil result of go-off"
2424
(is (= nil
@@ -27,14 +27,14 @@
2727
(testing "take inside binding of loop"
2828
(is (= 42
2929
@(go-off (loop [x (<!? (d/success-deferred 42))]
30-
x)))))
30+
x)))))
3131

3232
(testing "can get from a catch"
3333
(let [c (d/success-deferred 42)]
3434
(is (= 42
3535
@(go-off (try
36-
(assert false)
37-
(catch Throwable ex (<!? c)))))))))
36+
(assert false)
37+
(catch Throwable ex (<!? c)))))))))
3838

3939
(deftest enqueued-chan-ops
4040
(testing "enqueued channel takes re-enter async properly"
@@ -45,9 +45,9 @@
4545
@async-chan)))
4646

4747
(is (= 3
48-
(let [d1 (d/deferred)
49-
d2 (d/deferred)
50-
d3 (d/deferred)
48+
(let [d1 (d/deferred)
49+
d2 (d/deferred)
50+
d3 (d/deferred)
5151
async-chan (go-off (+ (<!? d1) (<!? d2) (<!? d3)))]
5252
(d/success! d3 1)
5353
(d/success! d2 1)
@@ -84,7 +84,7 @@
8484
@(go-off (<!? (d/catch (d/future (/ 5 0)) (constantly 5))))))))
8585

8686
(deftest non-deferred-takes
87-
(testing "Can take from non-deffereds"
87+
(testing "Can take from non-deferreds"
8888
(is (= 5 @(go-off (<!? 5))))
8989
(is (= "test" @(go-off (<!? "test"))))))
9090

@@ -106,7 +106,7 @@
106106
(let [blow-up-counter (atom 0)
107107
blow-up-fn (fn [& _] (is (= 1 (swap! blow-up-counter inc))))]
108108
@(go-off (<!? "cat")
109-
(blow-up-fn))))
109+
(blow-up-fn))))
110110
;; Sleep is here to make sure that the secondary invocation of `blow-up-fn` that was happening has
111111
;; had time to report it's failure before the test finishes
112112
(Thread/sleep 500)))
@@ -134,16 +134,16 @@
134134
:stats-callback (constantly nil)})]
135135
(try (is (str/starts-with? @(go-off-executor custom-executor (.getName (Thread/currentThread))) prefix)
136136
"Running on custom executor, thread naming should be respected.")
137-
(println @(go-off-executor custom-executor (.getName (Thread/currentThread))))
137+
@(go-off-executor custom-executor (.getName (Thread/currentThread)))
138138
(finally (.shutdown custom-executor)))))
139139

140140
(deftest go-off-streams
141141
(let [test-stream (s/stream)
142142
test-d (go-off [(<!? test-stream)
143-
(<!? test-stream)
144-
(<!? test-stream)
145-
(<!? test-stream)
146-
(<!? test-stream)])]
143+
(<!? test-stream)
144+
(<!? test-stream)
145+
(<!? test-stream)
146+
(<!? test-stream)])]
147147
(dotimes [n 3]
148148
(s/put! test-stream n))
149149
(s/close! test-stream)

0 commit comments

Comments
 (0)