<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  </head>
  <body>
    <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 class="moz-cite-prefix">On 1/20/23 02:40, Dr Heinz M. Kabutz
      wrote:<br>
    </div>
    <blockquote type="cite"
      cite="mid:20fdac7a-1a73-dc12-c021-a492f13de2b9@javaspecialists.eu">
      <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
      <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:#2b2b2b;color:#a9b7c6;font-family:'JetBrains Mono NL',monospace;font-size:30,0pt;"><span style="color:#cc7832;">import </span>java.util.Map<span style="color:#cc7832;">;
</span><span style="color:#cc7832;">import </span>java.util.concurrent.CancellationException<span style="color:#cc7832;">;
</span><span style="color:#cc7832;">import </span>java.util.concurrent.ConcurrentHashMap<span style="color:#cc7832;">;
</span><span style="color:#cc7832;">
</span><span style="color:#cc7832;">public class </span>CHMPinning {
    <span style="color:#cc7832;">public static void </span><span style="color:#ffc66d;">main</span>(String... args) <span style="color:#cc7832;">throws </span>InterruptedException {
        Map<Integer<span style="color:#cc7832;">, </span>Integer> map = <span style="color:#cc7832;">new </span>ConcurrentHashMap<>()<span style="color:#cc7832;">;
</span><span style="color:#cc7832;">        for </span>(<span style="color:#cc7832;">int </span>i = <span style="color:#6897bb;">0</span><span style="color:#cc7832;">; </span>i < <span style="color:#6897bb;">1_000</span><span style="color:#cc7832;">; </span>i++) {
            <span style="color:#cc7832;">int </span>finalI = i<span style="color:#cc7832;">;
</span><span style="color:#cc7832;">            </span>Thread.<span style="font-style:italic;">startVirtualThread</span>(() ->
                    <span style="color:#b389c5;">map</span>.computeIfAbsent(<span style="color:#b389c5;">finalI </span>% <span style="color:#6897bb;">3</span><span style="color:#cc7832;">, </span>key -> {
                        <span style="color:#cc7832;">try </span>{
                            Thread.<span style="font-style:italic;">sleep</span>(<span style="color:#6897bb;">2_000</span>)<span style="color:#cc7832;">;
</span><span style="color:#cc7832;">                        </span>} <span style="color:#cc7832;">catch </span>(InterruptedException e) {
                            <span style="color:#cc7832;">throw new </span>CancellationException(<span style="color:#6a8759;">"interrupted"</span>)<span style="color:#cc7832;">;
</span><span style="color:#cc7832;">                        </span>}
                        <span style="color:#cc7832;">return </span><span style="color:#b389c5;">finalI</span><span style="color:#cc7832;">;
</span><span style="color:#cc7832;">                    </span>}))<span style="color:#cc7832;">;
</span><span style="color:#cc7832;">        </span>}
        <span style="color:#cc7832;">long </span>time = System.<span style="font-style:italic;">nanoTime</span>()<span style="color:#cc7832;">;
</span><span style="color:#cc7832;">        try </span>{
            Thread.<span style="font-style:italic;">startVirtualThread</span>(() ->
                            System.<span style="color:#9876aa;font-style:italic;">out</span>.println(
                                    <span style="color:#6a8759;">"Hi, I'm an innocent virtual thread"</span>))
                    .join()<span style="color:#cc7832;">;
</span><span style="color:#cc7832;">        </span>} <span style="color:#cc7832;">finally </span>{
            time = System.<span style="font-style:italic;">nanoTime</span>() - time<span style="color:#cc7832;">;
</span><span style="color:#cc7832;">            </span>System.<span style="color:#9876aa;font-style:italic;">out</span>.printf(<span style="color:#6a8759;">"time = %dms%n"</span><span style="color:#cc7832;">, </span>(time / <span style="color:#6897bb;">1_000_000</span>))<span style="color:#cc7832;">;
</span><span style="color:#cc7832;">        </span>}
        System.<span style="color:#9876aa;font-style:italic;">out</span>.println(<span style="color:#6a8759;">"map = " </span>+ map)<span style="color:#cc7832;">;
</span><span style="color:#cc7832;">
</span><span style="color:#cc7832;">    </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 class="moz-signature" cols="72">Regards

Heinz
-- 
Dr Heinz M. Kabutz (PhD CompSci)
Author of "The Java™ Specialists' Newsletter" - <a class="moz-txt-link-abbreviated" href="http://www.javaspecialists.eu" moz-do-not-send="true">www.javaspecialists.eu</a>
Java Champion - <a class="moz-txt-link-abbreviated" href="http://www.javachampions.org" moz-do-not-send="true">www.javachampions.org</a>
JavaOne Rock Star Speaker
Tel: +30 69 75 595 262
Skype: kabutz
</pre>
      <div class="moz-cite-prefix">On 2023/01/20 01:22, mikmoila wrote:<br>
      </div>
      <blockquote type="cite"
cite="mid:CAAosdaBM8_=DX-7QBu-ZuWU3-=i=Q1Dd17n123K7z5Qi3wRXnw@mail.gmail.com">
        <meta http-equiv="content-type" content="text/html;
          charset=UTF-8">
        <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)"
              moz-do-not-send="true" class="moz-txt-link-freetext">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>
  </body>
</html>