I'm trying to make a promise-chan equivalent which also implements clojure.lang.IDeref. So far it's been easy going, I have the following definition:
(deftype DerefablePromiseChannel [ch ^:unsynchronized-mutable realized?]
clojure.lang.IDeref
(deref [_]
(let [res (a/<!! ch)]
(set! realized? true)
res))
clojure.lang.IBlockingDeref
(deref [_ timeout timeout-val]
(let [res (a/alt!!
ch ([v] v)
(a/timeout timeout) timeout-val)]
(set! realized? true)
res))
clojure.lang.IPending
(isRealized [_] realized?)
a.proto/Channel
(closed? [_] (a.proto/closed? ch))
(close! [_]
(set! realized? true)
(a/close! ch))
a.proto/ReadPort
(take! [_ handler]
(a.proto/take! ch handler))
a.proto/WritePort
(put! [_ val handler]
(a.proto/put! ch val handler)))
The problem is thus: I want to be able to include nil values in the potential values to be put on here.
The simple solution to this is to make it so that putting nil will instead close the channel.
However it seems that there's some lock which is not being released if I just add a conditional on if the value is some and do a.proto/close! on the inner channel (which is always a promise-chan
Is there something I need to do in particular to get this to work correctly?
nils are not valid channel values
Yes, that's as intended, my point was that if you take off a closed channel, it will give you nil. That means that I can emulate the behavior I want with normal channel operations, I just have to make a put of nil close the channel instead of making a put. My question was more one of implementation of put! and why calling close! inside it seemed to never release a lock.
Unless there are other parts of the chain up the call stack above the call to put! which become invalid if the value is nil.
Btw there is actually an old ticket to make chans derefable, unfortunately it introduced some nasty circularity issues so never landed https://clojure.atlassian.net/browse/ASYNC-102
Not at a computer so can’t say for sure, but I think you’re fighting the design
Yeah, I was looking into that. The main thing I'm trying to do here is to provide a new api (a parking take) to my existing return values, without forcing old code to update to not use deref.
Just trying to avoid a breaking change.
Seems like I can just make the logic which is conditional on nil move up the call stack a little.
Something I was surprised to come against recently is the lock/commit/active stuff for fn-handlers is most often a noop(only actually used for alts), and safety is provided by the locks internally in channels, which means you can't depend on the lock/commit/active stuff to ensure a handler is only executed once if you extend readport/writeport yourself, which is a huge bummer