Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Add constrained read/write-only ports #20

Closed
wants to merge 1 commit into from

2 participants

Brandon Bloom David Nolen
Brandon Bloom

As discussed in IRC today, this adds support for read-only and write-only "ports", which are constrained wrappers around channels. Write-only ports allow both put and close operations, were as read-only ports allow only take operations. Unsupported operations throw.

I'm open to suggestions for better names than <port and >port.

Brandon Bloom brandonbloom commented on the diff
src/main/clojure/cljs/core/async/impl/channels.cljs
... ... @@ -38,34 +38,6 @@
38 38 (do (.splice takes idx 1)
39 39 (recur idx)))))))
40 40
41   - impl/WritePort
1

This didn't change. Only moved.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Brandon Bloom brandonbloom commented on the diff
src/main/clojure/cljs/core/async/impl/protocols.cljs
((8 lines not shown))
18 16 (close! [chan]))
19 17
  18 +(defprotocol Channel)
2

This isn't in use any more. Should it stick around as a marker protocol?

This also raises the question of whether or not there should be type predicates for channels & ports.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
David Nolen
Collaborator

This got moved to JIRA

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Showing 1 unique commit by 1 author.

Jul 12, 2013
Brandon Bloom brandonbloom Add constrained read/write-only ports b24cb80
This page is out of date. Refresh to see the latest.
10 src/main/clojure/cljs/core/async.cljs
@@ -37,6 +37,16 @@
37 37 ([] (chan nil))
38 38 ([buf-or-n] (channels/chan (if (number? buf-or-n) (buffer buf-or-n) buf-or-n))))
39 39
  40 +(defn <port
  41 + "Returns a receive-only port wrapping the given channel."
  42 + [channel]
  43 + (channels/<port channel))
  44 +
  45 +(defn >port
  46 + "Returns a send-only port wrapping the given channel."
  47 + [channel]
  48 + (channels/>port channel))
  49 +
40 50 (defn timeout
41 51 "Returns a channel that will close after msecs"
42 52 [msecs]
70 src/main/clojure/cljs/core/async/impl/channels.cljs
@@ -38,34 +38,6 @@
38 38 (do (.splice takes idx 1)
39 39 (recur idx)))))))
40 40
41   - impl/WritePort
42   - (put! [this val handler]
43   - (assert (not (nil? val)) "Can't put nil in on a channel")
44   - (cleanup this)
45   - (if @closed
46   - (box nil)
47   - (let [[put-cb take-cb] (loop [taker-idx 0]
48   - (when (< taker-idx (.-length takes))
49   - (let [taker (aget takes taker-idx)]
50   - (if (and (impl/active? taker)
51   - (impl/active? handler))
52   - (do (.splice takes taker-idx 1)
53   - [(impl/commit handler) (impl/commit taker)])
54   - (recur (inc taker-idx))))))]
55   - (if (and put-cb take-cb)
56   - (do (dispatch/run (fn [] (take-cb val)))
57   - (box nil))
58   - (if (and buf (not (impl/full? buf)))
59   - (let [put-cb (and (impl/active? handler)
60   - (impl/commit handler))]
61   - (if put-cb
62   - (do (impl/add! buf val)
63   - (box nil))
64   - nil))
65   - (do
66   - (.unshift puts [handler val])
67   - nil))))))
68   -
69 41 impl/ReadPort
70 42 (take! [this handler]
71 43 (cleanup this)
@@ -93,7 +65,34 @@
93 65 nil)
94 66 (do (.unshift takes handler)
95 67 nil))))))
96   - impl/Channel
  68 +
  69 + impl/WritePort
  70 + (put! [this val handler]
  71 + (assert (not (nil? val)) "Can't put nil in on a channel")
  72 + (cleanup this)
  73 + (if @closed
  74 + (box nil)
  75 + (let [[put-cb take-cb] (loop [taker-idx 0]
  76 + (when (< taker-idx (.-length takes))
  77 + (let [taker (aget takes taker-idx)]
  78 + (if (and (impl/active? taker)
  79 + (impl/active? handler))
  80 + (do (.splice takes taker-idx 1)
  81 + [(impl/commit handler) (impl/commit taker)])
  82 + (recur (inc taker-idx))))))]
  83 + (if (and put-cb take-cb)
  84 + (do (dispatch/run (fn [] (take-cb val)))
  85 + (box nil))
  86 + (if (and buf (not (impl/full? buf)))
  87 + (let [put-cb (and (impl/active? handler)
  88 + (impl/commit handler))]
  89 + (if put-cb
  90 + (do (impl/add! buf val)
  91 + (box nil))
  92 + nil))
  93 + (do
  94 + (.unshift puts [handler val])
  95 + nil))))))
97 96 (close! [this]
98 97 (cleanup this)
99 98 (if @closed
@@ -109,3 +108,16 @@
109 108 (defn chan [buf]
110 109 (ManyToManyChannel. (make-array 0) (make-array 0) buf (atom nil)))
111 110
  111 +(defn <port [c]
  112 + (reify
  113 + impl/ReadPort
  114 + (take! [this fn1-handler]
  115 + (impl/take! c fn1-handler))))
  116 +
  117 +(defn >port [c]
  118 + (reify
  119 + impl/WritePort
  120 + (put! [this val handler]
  121 + (impl/put! c val handler))
  122 + (close! [this]
  123 + (impl/close! c))))
8 src/main/clojure/cljs/core/async/impl/protocols.cljs
@@ -12,11 +12,11 @@
12 12 (take! [port fn1-handler] "derefable val if taken, nil if take was enqueued"))
13 13
14 14 (defprotocol WritePort
15   - (put! [port val fn0-handler] "derefable nil if put, nil if put was enqueued. Must throw on nil val."))
16   -
17   -(defprotocol Channel
  15 + (put! [port val fn0-handler] "derefable nil if put, nil if put was enqueued. Must throw on nil val.")
18 16 (close! [chan]))
19 17
  18 +(defprotocol Channel)
  19 +
20 20 (defprotocol Handler
21 21 (active? [h] "returns true if has callback. Must work w/o lock")
22 22 #_(lock-id [h] "a unique id for lock acquisition order, 0 if no lock")
@@ -25,4 +25,4 @@
25 25 (defprotocol Buffer
26 26 (full? [b])
27 27 (remove! [b])
28   - (add! [b itm]))
  28 + (add! [b itm]))
10 src/main/clojure/clojure/core/async.clj
@@ -58,6 +58,16 @@
58 58 ([] (chan nil))
59 59 ([buf-or-n] (channels/chan (if (number? buf-or-n) (buffer buf-or-n) buf-or-n))))
60 60
  61 +(defn <port
  62 + "Returns a receive-only port wrapping the given channel."
  63 + [channel]
  64 + (channels/<port channel))
  65 +
  66 +(defn >port
  67 + "Returns a send-only port wrapping the given channel."
  68 + [channel]
  69 + (channels/>port channel))
  70 +
61 71 (defn timeout
62 72 "Returns a channel that will close after msecs"
63 73 [msecs]
111 src/main/clojure/clojure/core/async/impl/channels.clj
@@ -42,55 +42,6 @@
42 42 (when (.hasNext iter)
43 43 (recur (.next iter)))))))
44 44
45   - impl/WritePort
46   - (put!
47   - [this val handler]
48   - (when (nil? val)
49   - (throw (IllegalArgumentException. "Can't put nil on channel")))
50   - (.lock mutex)
51   - (cleanup this)
52   - (if @closed
53   - (do (.unlock mutex)
54   - (box nil))
55   - (let [^Lock handler handler
56   - iter (.iterator takes)
57   - [put-cb take-cb] (when (.hasNext iter)
58   - (loop [^Lock taker (.next iter)]
59   - (if (< (impl/lock-id handler) (impl/lock-id taker))
60   - (do (.lock handler) (.lock taker))
61   - (do (.lock taker) (.lock handler)))
62   - (let [ret (when (and (impl/active? handler) (impl/active? taker))
63   - [(impl/commit handler) (impl/commit taker)])]
64   - (.unlock handler)
65   - (.unlock taker)
66   - (if ret
67   - (do
68   - (.remove iter)
69   - ret)
70   - (when (.hasNext iter)
71   - (recur (.next iter)))))))]
72   - (if (and put-cb take-cb)
73   - (do
74   - (.unlock mutex)
75   - (dispatch/run (fn [] (take-cb val)))
76   - (box nil))
77   - (if (and buf (not (impl/full? buf)))
78   - (do
79   - (.lock handler)
80   - (let [put-cb (and (impl/active? handler) (impl/commit handler))]
81   - (.unlock handler)
82   - (if put-cb
83   - (do (impl/add! buf val)
84   - (.unlock mutex)
85   - (box nil))
86   - (do (.unlock mutex)
87   - nil))))
88   - (do
89   - (when (impl/active? handler)
90   - (.add puts [handler val]))
91   - (.unlock mutex)
92   - nil))))))
93   -
94 45 impl/ReadPort
95 46 (take!
96 47 [this handler]
@@ -159,7 +110,54 @@
159 110 (.unlock mutex)
160 111 nil)))))))
161 112
162   - impl/Channel
  113 + impl/WritePort
  114 + (put!
  115 + [this val handler]
  116 + (when (nil? val)
  117 + (throw (IllegalArgumentException. "Can't put nil on channel")))
  118 + (.lock mutex)
  119 + (cleanup this)
  120 + (if @closed
  121 + (do (.unlock mutex)
  122 + (box nil))
  123 + (let [^Lock handler handler
  124 + iter (.iterator takes)
  125 + [put-cb take-cb] (when (.hasNext iter)
  126 + (loop [^Lock taker (.next iter)]
  127 + (if (< (impl/lock-id handler) (impl/lock-id taker))
  128 + (do (.lock handler) (.lock taker))
  129 + (do (.lock taker) (.lock handler)))
  130 + (let [ret (when (and (impl/active? handler) (impl/active? taker))
  131 + [(impl/commit handler) (impl/commit taker)])]
  132 + (.unlock handler)
  133 + (.unlock taker)
  134 + (if ret
  135 + (do
  136 + (.remove iter)
  137 + ret)
  138 + (when (.hasNext iter)
  139 + (recur (.next iter)))))))]
  140 + (if (and put-cb take-cb)
  141 + (do
  142 + (.unlock mutex)
  143 + (dispatch/run (fn [] (take-cb val)))
  144 + (box nil))
  145 + (if (and buf (not (impl/full? buf)))
  146 + (do
  147 + (.lock handler)
  148 + (let [put-cb (and (impl/active? handler) (impl/commit handler))]
  149 + (.unlock handler)
  150 + (if put-cb
  151 + (do (impl/add! buf val)
  152 + (.unlock mutex)
  153 + (box nil))
  154 + (do (.unlock mutex)
  155 + nil))))
  156 + (do
  157 + (when (impl/active? handler)
  158 + (.add puts [handler val]))
  159 + (.unlock mutex)
  160 + nil))))))
163 161 (close!
164 162 [this]
165 163 (.lock mutex)
@@ -186,3 +184,16 @@
186 184 (defn chan [buf]
187 185 (ManyToManyChannel. (LinkedList.) (LinkedList.) buf (atom nil) (mutex/mutex)))
188 186
  187 +(defn <port [c]
  188 + (reify
  189 + impl/ReadPort
  190 + (take! [this fn1-handler]
  191 + (impl/take! c fn1-handler))))
  192 +
  193 +(defn >port [c]
  194 + (reify
  195 + impl/WritePort
  196 + (put! [this val handler]
  197 + (impl/put! c val handler))
  198 + (close! [this]
  199 + (impl/close! c))))
6 src/main/clojure/clojure/core/async/impl/protocols.clj
@@ -13,11 +13,11 @@
13 13 (take! [port fn1-handler] "derefable val if taken, nil if take was enqueued"))
14 14
15 15 (defprotocol WritePort
16   - (put! [port val fn0-handler] "derefable nil if put, nil if put was enqueued. Must throw on nil val."))
17   -
18   -(defprotocol Channel
  16 + (put! [port val fn0-handler] "derefable nil if put, nil if put was enqueued. Must throw on nil val.")
19 17 (close! [chan]))
20 18
  19 +(defprotocol Channel)
  20 +
21 21 (defprotocol Handler
22 22 (active? [h] "returns true if has callback. Must work w/o lock")
23 23 (lock-id [h] "a unique id for lock acquisition order, 0 if no lock")
7 src/main/clojure/clojure/core/async/impl/timers.clj
@@ -35,10 +35,7 @@
35 35 -1
36 36 (if (= timestamp ostamp)
37 37 0
38   - 1))))
39   - impl/Channel
40   - (close! [this]
41   - (impl/close! channel)))
  38 + 1)))))
42 39
43 40 (defn timeout
44 41 "returns a channel that will close after msecs"
@@ -59,7 +56,7 @@
59 56 (loop []
60 57 (let [^TimeoutQueueEntry tqe (.take q)]
61 58 (.remove timeouts-map (.timestamp tqe) tqe)
62   - (impl/close! tqe))
  59 + (impl/close! (.channel tqe)))
63 60 (recur))))
64 61
65 62 (defonce timeout-daemon
11 src/test/clojure/clojure/core/async_test.clj
@@ -117,3 +117,14 @@
117 117 (put! c :enqueues #(deliver p :proceeded)) ;; enqueue a put
118 118 (<!! c) ;; make room in the buffer
119 119 (deref p 250 :timeout)))))
  120 +
  121 +(deftest constrained-ports
  122 + (let [c (chan 1)
  123 + ro (<port c)
  124 + wo (>port c)]
  125 + (is (thrown? IllegalArgumentException (>!! ro :foo)))
  126 + (>!! wo :foo)
  127 + (is (thrown? IllegalArgumentException (<!! wo)))
  128 + (is (= (<!! ro) :foo))
  129 + (is (thrown? IllegalArgumentException (close! ro)))
  130 + (close! wo)))

Tip: You can add notes to lines in a file. Hover to the left of a line to make a note

Something went wrong with that request. Please try again.