Fork me on GitHub
#core-async
<
2020-06-10
>
Joshua Suskalo00:06:31

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)))

Joshua Suskalo00:06:21

The problem is thus: I want to be able to include nil values in the potential values to be put on here.

Joshua Suskalo00:06:39

The simple solution to this is to make it so that putting nil will instead close the channel.

Joshua Suskalo00:06:15

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

Joshua Suskalo00:06:48

Is there something I need to do in particular to get this to work correctly?

Alex Miller (Clojure team)01:06:35

nils are not valid channel values

Joshua Suskalo01:06:42

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.

Joshua Suskalo01:06:32

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.

Alex Miller (Clojure team)01:06:55

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

Alex Miller (Clojure team)01:06:45

Not at a computer so can’t say for sure, but I think you’re fighting the design

Joshua Suskalo01:06:47

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.

Joshua Suskalo01:06:01

Just trying to avoid a breaking change.

Joshua Suskalo01:06:56

Seems like I can just make the logic which is conditional on nil move up the call stack a little.

hiredman02:06:37

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