<div dir="ltr"><div><div>Just spitballing,<br></div>there's already a ReservationNode type, adding a ReentrantLock instance variable to it should address the overhead issue (since each ReservationNode is very short-lived, only temporarily in the #table)<br><br></div><div>although the else branch in computeIfAbsent wouldn't be pretty and would require instance of checks</div><div><br></div><div>-T<br></div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Fri, Jan 20, 2023 at 4:51 AM Doug Lea <<a href="mailto:dl@cs.oswego.edu">dl@cs.oswego.edu</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
<div>
<p>Updates to ConcurrentHashMap to avoid builtin synchronization are
on the todo list. As you've noticed, using a ReentrantLock per
node would add unacceptable overhead, and would be especially
unwelcome for the huge number of non-loom usages. I have a few
ideas in mind, but it will surely be a while until arriving at a
replacement good enough to release.</p>
<p>-Doug<br>
</p>
<div>On 1/20/23 02:40, Dr Heinz M. Kabutz
wrote:<br>
</div>
<blockquote type="cite">
<p>Hi Mika,</p>
<p>I was thinking about that yesterday. Since CHM gives a
guarantee that the compute() functions are called atomically, it
might be worth looking at the implications of moving (back) to
ReentrantLock. However, that would also mean that we need to
allocate a lot of ReentrantLock instances, and those are 40
bytes per lock. Here is a demo that shows the issue:</p>
<pre style="background-color:rgb(43,43,43);color:rgb(169,183,198);font-family:"JetBrains Mono NL",monospace"><span style="color:rgb(204,120,50)">import </span>java.util.Map<span style="color:rgb(204,120,50)">;
</span><span style="color:rgb(204,120,50)">import </span>java.util.concurrent.CancellationException<span style="color:rgb(204,120,50)">;
</span><span style="color:rgb(204,120,50)">import </span>java.util.concurrent.ConcurrentHashMap<span style="color:rgb(204,120,50)">;
</span><span style="color:rgb(204,120,50)">
</span><span style="color:rgb(204,120,50)">public class </span>CHMPinning {
<span style="color:rgb(204,120,50)">public static void </span><span style="color:rgb(255,198,109)">main</span>(String... args) <span style="color:rgb(204,120,50)">throws </span>InterruptedException {
Map<Integer<span style="color:rgb(204,120,50)">, </span>Integer> map = <span style="color:rgb(204,120,50)">new </span>ConcurrentHashMap<>()<span style="color:rgb(204,120,50)">;
</span><span style="color:rgb(204,120,50)"> for </span>(<span style="color:rgb(204,120,50)">int </span>i = <span style="color:rgb(104,151,187)">0</span><span style="color:rgb(204,120,50)">; </span>i < <span style="color:rgb(104,151,187)">1_000</span><span style="color:rgb(204,120,50)">; </span>i++) {
<span style="color:rgb(204,120,50)">int </span>finalI = i<span style="color:rgb(204,120,50)">;
</span><span style="color:rgb(204,120,50)"> </span>Thread.<span style="font-style:italic">startVirtualThread</span>(() ->
<span style="color:rgb(179,137,197)">map</span>.computeIfAbsent(<span style="color:rgb(179,137,197)">finalI </span>% <span style="color:rgb(104,151,187)">3</span><span style="color:rgb(204,120,50)">, </span>key -> {
<span style="color:rgb(204,120,50)">try </span>{
Thread.<span style="font-style:italic">sleep</span>(<span style="color:rgb(104,151,187)">2_000</span>)<span style="color:rgb(204,120,50)">;
</span><span style="color:rgb(204,120,50)"> </span>} <span style="color:rgb(204,120,50)">catch </span>(InterruptedException e) {
<span style="color:rgb(204,120,50)">throw new </span>CancellationException(<span style="color:rgb(106,135,89)">"interrupted"</span>)<span style="color:rgb(204,120,50)">;
</span><span style="color:rgb(204,120,50)"> </span>}
<span style="color:rgb(204,120,50)">return </span><span style="color:rgb(179,137,197)">finalI</span><span style="color:rgb(204,120,50)">;
</span><span style="color:rgb(204,120,50)"> </span>}))<span style="color:rgb(204,120,50)">;
</span><span style="color:rgb(204,120,50)"> </span>}
<span style="color:rgb(204,120,50)">long </span>time = System.<span style="font-style:italic">nanoTime</span>()<span style="color:rgb(204,120,50)">;
</span><span style="color:rgb(204,120,50)"> try </span>{
Thread.<span style="font-style:italic">startVirtualThread</span>(() ->
System.<span style="color:rgb(152,118,170);font-style:italic">out</span>.println(
<span style="color:rgb(106,135,89)">"Hi, I'm an innocent virtual thread"</span>))
.join()<span style="color:rgb(204,120,50)">;
</span><span style="color:rgb(204,120,50)"> </span>} <span style="color:rgb(204,120,50)">finally </span>{
time = System.<span style="font-style:italic">nanoTime</span>() - time<span style="color:rgb(204,120,50)">;
</span><span style="color:rgb(204,120,50)"> </span>System.<span style="color:rgb(152,118,170);font-style:italic">out</span>.printf(<span style="color:rgb(106,135,89)">"time = %dms%n"</span><span style="color:rgb(204,120,50)">, </span>(time / <span style="color:rgb(104,151,187)">1_000_000</span>))<span style="color:rgb(204,120,50)">;
</span><span style="color:rgb(204,120,50)"> </span>}
System.<span style="color:rgb(152,118,170);font-style:italic">out</span>.println(<span style="color:rgb(106,135,89)">"map = " </span>+ map)<span style="color:rgb(204,120,50)">;
</span><span style="color:rgb(204,120,50)">
</span><span style="color:rgb(204,120,50)"> </span>}
}
</pre>
<p>Output is something like:</p>
<p>Hi, I'm an innocent virtual thread<br>
time = 2002ms<br>
map = {0=0, 1=7, 2=2}<br>
</p>
<p>IMHO, I would not like to see the CHM memory usage increase by
40 x # nodes bytes to cater for an edge case of the compute()
function taking a bit longer.<br>
</p>
<p><br>
</p>
<pre cols="72">Regards
Heinz
--
Dr Heinz M. Kabutz (PhD CompSci)
Author of "The Java™ Specialists' Newsletter" - <a href="http://www.javaspecialists.eu" target="_blank">www.javaspecialists.eu</a>
Java Champion - <a href="http://www.javachampions.org" target="_blank">www.javachampions.org</a>
JavaOne Rock Star Speaker
Tel: +30 69 75 595 262
Skype: kabutz
</pre>
<div>On 2023/01/20 01:22, mikmoila wrote:<br>
</div>
<blockquote type="cite">
<div dir="ltr">Hi.
<div><br>
</div>
<div>As often mentioned in this mailing-list a feedback about
preview/incubator features is appreciated, so here's one: </div>
<div><br>
</div>
<div>I was experimenting with a caching system utilising
ConcurrentHashMap as cache store and Structured Concurrency
API for refreshing the entries from multiple sources (
StructuredTaskScope.ShutdownOnSuccess ). The idea was to
make http-requests for getting the fresh values but the
first implementation simply uses UUID::randomUUID for
simulating that.</div>
<div> </div>
<div>I noticed that the programs halts In a test case where
"N" concurrent calls (where "N" >= number of cpu cores)
running on virtual threads end-up calling the
ConcurrentHashMap::computeIfAbsent for the same
(non-existing) key.<br>
</div>
<div><br>
</div>
<div>"-Djdk.tracePinnedThreads=full" reveals that there is a
pinned carrier thread:</div>
<div>
<div>
java.base/java.util.concurrent.ConcurrentHashMap.computeIfAbsent(ConcurrentHashMap.java:1708)
<== monitors:1<br>
</div>
<div><br>
</div>
</div>
<div>The documentation ( <a href="https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/util/concurrent/ConcurrentHashMap.html#computeIfAbsent(K,java.util.function.Function)" target="_blank">https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/util/concurrent/ConcurrentHashMap.html#computeIfAbsent(K,java.util.function.Function)</a>
) says:</div>
<div>
<div><br>
</div>
</div>
<div> "Some attempted update operations on this map by other
threads may be blocked while computation is in progress, so
the computation should be short and simple."</div>
<div><br>
</div>
<div>This is clear but I still found it as a surprise that it
uses synchronized instead of "virtual-thread friendly"
constructs.</div>
<div><br>
</div>
<div>If needed I can post a small demo program.</div>
<div><br>
</div>
<div>JDK used is latest OpenJDK-19 GA, OS is Windows:</div>
<div> openjdk version "19.0.2" 2023-01-17<br>
OpenJDK Runtime Environment (build 19.0.2+7-44)<br>
OpenJDK 64-Bit Server VM (build 19.0.2+7-44, mixed mode,
sharing)<br>
</div>
<div><br>
</div>
<div>Best Regards,</div>
<div> Mika</div>
</div>
</blockquote>
</blockquote>
</div>
</blockquote></div>