Fork me on GitHub
#core-async
<
2024-05-02
>
Nim Sadeh13:05:56

Is there a way to send a signal between go-blocks or threads? I know the typical communication piece is a channel, but for what I am thinking I am not sure that can work. Suppose you have the following go block:

(go
  (loop [next-item (
If I use a separate queue to signal the end of the processing loop, I'll have to park waiting for a signal on each loop, which is not what I want (there may be only one signal). I can't end the channel, because it may have an arbitrary number of consumers, not all of whom should be ending. I can use an atom, but I am looking to see if there's a way to do it without holding state

jjttjj13:05:26

If I understand correctly, This is what a/alt! is for

(go
  (loop []
    (a/alt!
      c ([next-item] (process next-item) (recur))
      signal-tripped-ch :end)))

Nim Sadeh13:05:32

Ohh I think I completely misunderstood how alt! works until now

jjttjj13:05:34

It's the coolest/most powerful thing in core.async :)

Nim Sadeh13:05:49

Thank you, TIL!

Ben Sless14:05:06

You can also just close the upstream channel

Ben Sless15:05:36

A nil from the upstream channel signals it is closed

Nim Sadeh15:05:42

> I can't end the channel, because it may have an arbitrary number of consumers, not all of whom should be ending

Ben Sless15:05:49

Don't mean to make more work for you, but multiple consumers and multiple producers are an anti pattern, you should use merge and tap

Nim Sadeh15:05:15

Can you expand on that? I usually use mult. For example, I commonly work with streams from LLM outputs represented as channels. I typically use mult to split the channel in 2, one going to the client over SSE, and the other going to a reducer that posts the reduced output to a database.

Nim Sadeh15:05:44

For the use case for which I asked about tap, I have a channel of data coming in from the client over websockets. Outputs from an ML streaming algorithm trigger certain async processes to either happen or stop, but I don't want to stop the data coming in from the socket, which has a lot of listeners on it

hiredman17:05:44

hard disagree about multiple consumers and producers being an antipattern, merge is convenient in many cases, but merge runs a normal go loop doing a copy and the behavior of doing multiple takes via alts your self vs. the indirection of merge doing it and presenting results over a channel are different.

Ben Sless14:05:13

It's a very opinionated anti pattern (and I was Wong about tap there) but I find multiple processes producing to multiple channels which are merged cleaner than multiple producers to same channel, buys you the overhead of clean shutdown

hiredman20:05:26

@alexmiller I just noticed https://clojure.atlassian.net/browse/ASYNC-209 is closed because I had some code that on review was calling >! outside of a go block, and I knew there was an issue that would have turned that into a compile time error(macro expand time) instead of a runtime error. Having just tripped over this issue, it is a real bummer to see the ticket is both closed as a wontfix and has 0 commentary about why.

Alex Miller (Clojure team)20:05:41

I can reopen it, don't remember why

hiredman21:05:16

I can imagine it not being as simple suggested in the ticket, switch from functions to macros might cause problems with the way the go macro transformation happens, or maybe the risk of breaking the build of exist projects was too high