core-async

cch1 2024-09-26T14:17:44.416949Z

Beware: I recently ran into an issue where the root cause was that creating two timeout channels actually only created one which was returned by the second call. To illustrate:

(identical? (async/timeout 10) (async/timeout 9)) => true
The fact that the resolution (on the JVM) is only 10ms is not a problem itself, but the "recycling" of existing timeout objects bit me hard when I used them as keys in a map to lookup an associated "event" to complete. In a value-based world, I can kinda see why this is the case, but it was still surprising given the lack of documentation.

👀 2
Alex Miller (Clojure team) 2024-09-26T15:36:24.220429Z

there is only 1 timeout channel currently

2024-09-26T15:48:14.125449Z

huh?

cch1 2024-09-26T15:48:45.176099Z

one per instant (with 10ms resolution), right?

2024-09-26T15:48:58.917539Z

a quick fix work around is to replace (async/timeout ...) with (async/go (async/<! (async/timeout ...)))

☝️ 1
cch1 2024-09-26T15:49:40.550549Z

Yes, hiredman, that would work. But it seems wasteful... perhaps I need to get over that.

cch1 2024-09-26T15:50:35.198839Z

Ultimately I refactored my code to associate multiple events with a single channel to overcome the lack of uniqueness by timeout channel.

2024-09-26T15:50:45.827979Z

and for designs where you use timeout as a key like {some-timeout some-data} and do an alts! over a bunch of timeouts and then lookup the data in the map, something like a set of (async/go (async/<! (async/timeout ...)) some-data) is often nicer

cch1 2024-09-26T15:52:01.834589Z

That ^ is pretty much exactly my use case. My solution ended up being {(async/timeout n) [collection of things associated with the same timeout].

2024-09-26T15:52:39.279879Z

yeah, too much work to figure out how to associate things with the same timeout 🙂

cch1 2024-09-26T15:53:53.955949Z

It was added complexity, for sure. But I couldn't bring myself to add the overhead of the "unique relay channel" approach you conceived -even if I had been quick enough to come up with it on my own last night.

2024-09-26T15:56:22.668789Z

https://gist.github.com/hiredman/a68a2add9751eb8de3d2776363219e13 is an example of wrapping timeouts to have them convey data when they timeout, although maybe be obfuscated

2024-09-26T15:57:23.525029Z

I think I have 1 or 2 patches in the core.async jira that incidentally change timeouts to be unique

➕ 2
2024-09-26T16:01:46.516719Z

like, I think the commit that changed timeouts to behave like they do was aimed at reducing the memory usage from using timeouts in tight loops, but my patches on https://clojure.atlassian.net/browse/ASYNC-234 solve that much more comprehensively. but apparently rich hates anything called a "nack", which is the mechanism added there to clean up timeouts that aren't being actively waited on.

2024-09-26T16:03:55.945779Z

https://clojure.atlassian.net/browse/ASYNC-109 is a much smaller patch that basically just wraps the existing timeout stuff in a non-closable ReadPort, which would also cause timeouts to be unique objects

👀 1