This PR fixes the NullPointerException that occurs in Scala Native when forking a large number of fibers concurrently, as reported in #9681.
When running the PromiseSpec - waiter stack safety test on Scala Native, which forks 10,000 fibers, a NullPointerException is thrown:
Exception in thread "zio-fiber-931" java.lang.NullPointerException: null
at scala.scalanative.runtime.package$.throwNullPointer(Unknown Source)
at java.util.concurrent.ConcurrentHashMap.treeifyBin(Unknown Source)
at java.util.concurrent.ConcurrentHashMap.putVal(Unknown Source)
at java.util.concurrent.ConcurrentHashMap$KeySetView.add(Unknown Source)
at zio.internal.WeakConcurrentBag.addToLongTermStorage(Unknown Source)
...
The root cause is that the Scala Native implementation of ConcurrentHashMap has a bug in the treeifyBin method, which is called when a hash bucket needs to be converted from a linked list to a red-black tree for better performance. Under high concurrency, this operation can result in a NullPointerException.
This PR modifies the PlatformSpecific.scala file for the Native platform to use Collections.synchronizedSet(new HashSet()) instead of ConcurrentHashMap.newKeySet() for the newConcurrentSet methods.
Collections.synchronizedSet may have slightly different performance characteristics than ConcurrentHashMap.newKeySet(), it provides correct behavior under high concurrencyCollections.synchronizedSet for other concurrent collections in the Native platform (e.g., newConcurrentWeakSet)Collections.synchronizedSet uses a single lock for all operations, which may reduce throughput under very high contention compared to ConcurrentHashMap’s segment-based lockingWeakConcurrentBag (storing fiber references), the correctness benefit outweighs any potential performance impactcore/native/src/main/scala/zio/internal/PlatformSpecific.scala: Modified newConcurrentSet methods to use synchronized HashSet instead of ConcurrentHashMap.newKeySet()This fix should allow the PromiseSpec - waiter stack safety test to pass on Scala Native without throwing NullPointerException.
Fixes #9681
/claim #9681
andresctirado
@andresctirado
ZIO
@ZIO