Fork me on GitHub
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?]
  (deref [_]
    (let [res (a/<!! ch)]
      (set! realized? true)

  (deref [_ timeout timeout-val]
    (let [res (a/alt!!
                ch ([v] v)
                (a/timeout timeout) timeout-val)]
      (set! realized? true)

  (isRealized [_] realized?)

  (closed? [_] (a.proto/closed? ch))
  (close! [_]
    (set! realized? true)
    (a/close! ch))

  (take! [_ handler]
    (a.proto/take! ch handler))

  (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?


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.


Btw there is actually an old ticket to make chans derefable, unfortunately it introduced some nasty circularity issues so never landed


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.


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