Is there a way to know when a channel has closed? Like if I want to do some cleanup in the process using it?
@colinkahn It will return nil
when you take from it, I believe.
@seancorfield right, I guess I'm talking about a channel I'm putting values into.
I saw that put!
will return false if it's closed, but that isn't immediate feedback, requires something attempts a put first
Well, it's async so you pretty much have to interact with it in order to tell whether it has closed or not.
What people often do is have an extra "control" channel and then you "alt" across the actual channel and the control channel...
(let [ch (chan)
_ (clojure.core.async/close! ch)]
(clojure.core.async.impl.protocols/closed? ch))
You’re probably not supposed to use it 🙂 but it’s there…
Whenever I’ve needed to check that, there was almost always a way around it… it’s not very idiomatic usage of CSP channels as far as I understand
My situation is like this:
(defn read-from-client []
(let [out-chan (a/chan)
client (make-client)]
(.on client "change" #(a/put! out-chan %))
out-chan))
but I was wondering if there's a way if out-chan
gets closed if there's a way to setup something in my read-from-client
function that can destroy the client etc.@raspasov yeah it's not checking necessarily that it's closed, but having something happen when it closes
Make a go loop that takes from the channel… when it returns nil -> means closed
It can only return nil if it closes
but then all values will get read in that loop, so I couldn't have consumers. I guess I could have it be a mult?
Hm, so you have multiple consumers from that channel?
it would be multiple if the original function setup a loop to read until closed
Multiple potential consumers that need to receive the same value? In that case you can use (promise-chan)
Maybe I’m not understanding the entirety of the problem
Use the put! arity that takes a callback, it will get called with true when a value is written to a channel, and false if not because the channel is closed
@hiredman good idea
@hiredman my issue with that was that if my client "change" callback isn't called again the client doesn't get cleaned up because another put is never attempted
@colinkahn how can that channel get closed? do you call (close! …) on it potentially in your code?
that was my plan, whoever calls the read-from-client
function could later close the out-chan
, and I was hoping I could have a side effect of closing it be destroying the client instance
I would say… since you control the (close! … ), just cleanup right after you call close
Wouldn’t that work?
Destroying the client on close would be a race condition
After you close a channel it can still have stuff in it
@hiredman mmmmm…. rrrright…
ah didn't think of that
yea… that’s why closing channels is just not a very good idea in general… are you sure you need to do this cleanup stuff? Maybe you can arrange it so that this “change” handler just persists for the duration of the program
Maybe there’s a way to have a long-lived/global out-chan that is just one channel and constantly receives “stuff”
not really, it needs to be created/destroyed when certain state changes, so new information can be used to create it
That way you don’t have to do constantly have to close! and setup a handler
Maybe there’s a way to have a channel with a transducer, that does some sort of logic?
When state changes…
yeah I think it's possible, since it's still a singleton more or less
Or even have a global out-chan, and then have one (go (loop … )) that does the processing and decides there and then what to do with that data
Setting up/destroying a new handler on every message or frequently is just a recipe for race conditions and further issues like @hiredman very well pointed out
You’re almost getting nothing out of core.async then vs regular callbacks… you might be even making your life harder
At least that’s my feeling from my partial information perspective about the problem 🙂
well I think the answer to the original question is pretty clearly a no, which makes sense for those reasons
Yea… the one reliable way, again, is to have a loop, and when that returns a nil -> means closed, and exhausted of all messages
Since the loop would have taken all messages before receiving that final nil
But actually thinking about what @hiredman said about the race condition… cleaning up after a (close! …) might not be a problem, since we’re only talking about removing that callback… that wouldn’t interfere with any leftover data on the channel as long as there’s active takers that will process that data (as far as I understand)… but again that can create a problem where in between calling (close! ..) and actually removing that callback you might try to put! onto a closed channel!
So it’s not really a problem about the channel… but the fact that you might accidentally drop data: 1. (close! ch) 2. DATA SHOWS UP, we try to (put! ch-that-just-closed) 3. remove handler
So it still is a problem 🙂 just a different kind of problem…
So all of that make long-lived channels better IMHO for that kind of a situation
at that point I wouldn't expect step 2 to succeed anyways
but that just might be this particular case
Yea but what do you do with that data? Drop it? if that’s ok, fine
right, dropping it in this case would be fine
In that particular case you might be OK with that then… unless @hiredman finds another problem with my logic 🙂