Fork me on GitHub
#announcements
<
2023-10-21
>
weavejester17:10:45

https://github.com/ring-clojure/ring-websocket-transit 0.1.0-beta1 has been released. This library provides Ring middleware for automatically decoding and encoding WebSocket messages using the Transit data format. https://github.com/ring-clojure/ring-websocket-async 0.1.0-beta2 has also been released. This library provides functions and macros for using core.async with Ring's WebSocket API.

🎉 21
Alex Miller (Clojure team)19:10:10

https://clojure.org/news/2023/10/20/clojure-1-12-alpha5 Use locks instead of synchronized blocks around user code in lazy-seq and delay Clojure users want to use virtual threads on JDK 21. Prior to 1.12, Clojure lazy-seqs and delays, in order to enforce run-once behavior, ran user code under synchronized blocks, which don’t participate in cooperative blocking. Thus if that code did e.g. blocking I/O it would pin a real thread. JDK 21 may emit warnings for this when using -Djdk.tracePinnedThreads=full. To avoid this pinning, in 1.12 we’ve changed lazy-seqs and delay to use locks instead of synchronized blocks.

❤️ 50
🎉 36
clojure 5
Alex Miller (Clojure team)19:10:32

Even if you're not on Java 21, this change may affect allocation and GC rates, so feedback is welcome if that's something you are able to A/B test

borkdude20:10:29

Tested with bb and ecosystem libs, no problems found so far:

{:test 2356, :pass 14713, :fail 0, :error 0}

seancorfield21:10:20

Pushed it into our CI. If there are no problems we'll run it on QA for a bit, then push to production. We're on JDK 20 right now (awaiting official JDK 21 support from New Relic before we can update) but we have --enable-preview and could also test vthreads...

seancorfield22:10:44

Passes all our tests so it's on our QA system now.

staypufd15:10:31

For those testing, you may want to check performance as well if you have portions of your code where that is an important concern. I mention this because I was reading about this a bit more and one article I read said their performance testing showed between 2 - 7.5x performance difference between synchronized and locks. It may be just that their test was simple or whatnot, but I thought it may be worth mentioning. The performance difference may be offset by the gains you get via virtual threads as well. Additionally, we all know that performance tends to be problem specific and such as well. @U064X3EF3 probably has better info on what they see in regards to performance with locks.

Alex Miller (Clojure team)15:10:26

That does not match what we found in testing but it is definitely complicated, and varies significantly depending on jdk

staypufd15:10:00

Thanks Alex. I knew you’d know more

Alex Miller (Clojure team)15:10:38

In Java 21, we found generally that lock/unlock was significantly faster with RL but reentrant locking (which we are no longer doing) was slower. There is a cost to allocation though and generally I think the seq realization is a bit slower, but seq walking is now faster (due to refactoring to streamline the realized path)

Alex Miller (Clojure team)15:10:25

But those numbers vary on older jvms, particularly pre-15 with biased locking

Alex Miller (Clojure team)15:10:31

In summary, it is really complicated and hard to predict exactly how it affects any particular application, so please test!

seancorfield16:10:55

> reentrant locking (which we are no longer doing) was slower. > Could you clarify/elaborate on this? The change in the code seems to be replacing synchronized with use of a ReentrantLock...

Alex Miller (Clojure team)17:10:52

Prior, seq() called sval(), both were synchronized so you reenter the lock (lock something already locked), that never happens now. Reentrant locking with synchronized seems basically free but reentrant locking with RL seems to still have some costs with unlock signaling

1
staypufd17:10:46

Thanks Alex. That makes it clearer.

Alex Miller (Clojure team)17:10:32

Tbh, it is kind of shocking that virtual threads went out without consideration of all this. Lots of Java code just assumes reentrant synchronization is cheap/free

đź‘Ť 4
seancorfield17:10:32

I wonder if fewer people tested them in preview in real world situations than they expected? It seems that they knew about the locking/pinning issues but considered them minor...

staypufd17:10:49

From the JEP “The scheduler does not compensate for pinning by expanding its parallelism. Instead, avoid frequent and long-lived pinning by revising synchronized blocks or methods that run frequently and guard potentially long I/O operations to use java.util.concurrent.locks.ReentrantLock instead. There is no need to replace synchronized blocks and methods that are used infrequently (e.g., only performed at startup) or that guard in-memory operations. As always, strive to keep locking policies simple and clear.” Still not that clear and their solution is ReentrantLocks, which as Alex pointed out seems to still have issues. Seqs would seem to be in memory guarded objects wouldn’t they? Would they consider our “user code” frequent or long lived? I guess so since in Clojure we use seqs for so much. Regardless, thanks to the team for bringing this capability to the Clojure Community. One more amazing reason that using Clojure is so amazing.

đź’Ż 1
staypufd17:10:45

This comment on StackOverflow seems to talk about the implementation a bit. https://stackoverflow.com/a/76788835

staypufd17:10:37

Thanks again for the info Alex

staypufd17:10:04

Just found this from an August of this year. Maybe they mention some of these things. I’m gonna watch, and thought you all may enjoy it as well. https://youtu.be/WsCJYQDPrrE?si=S401YxgC8ocNNwue

Alex Miller (Clojure team)18:10:42

> Would they consider our “user code” frequent or long lived? Since this is user code, it can be anything, so it must be presumed that it can be long lived (certainly also true for delay)

Alex Miller (Clojure team)18:10:25

There are a number of other uses of synchronized in Clojure but most of those are protecting field access (short lived) and we have no plans to modify those, they should be fine.

Alex Miller (Clojure team)18:10:13

that talk is a good overview. another related issue you might ask about after watching that is ThreadLocals and I have another small change for that queued up in https://clojure.atlassian.net/browse/CLJ-2803, but I don't think it's critical

clojure 1
staypufd18:10:36

Thanks again for chatting about this. I learned quite a bit about this stuff from investigating this and all the comments.