<div dir="ltr"><div dir="ltr"><div class="gmail_default" style="font-family:arial,helvetica,sans-serif"><span style="font-family:Arial,Helvetica,sans-serif">On Wed, Oct 5, 2022 at 4:06 AM Alan Bateman <<a href="mailto:Alan.Bateman@oracle.com">Alan.Bateman@oracle.com</a>> wrote:</span><br></div></div><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">

  
  <div>
    On 04/10/2022 15:15, Jonas Schlecht wrote:<br>
    <blockquote type="cite">
      
      
      
      <div>
        <p class="MsoNormal">Hi everybody,<br>
          <br>
          I am currently writing my thesis which, for some part, also
          covers Virtual Threads. So far, I get how everything works but
          I don’t understand how the work-stealing nature of the
          ForkJoinPool is used by Virtual Threads. <br>
          I know the benefits of a work-stealing scheduler and how the
          ForkJoinPool uses ForkJoinTasks. But how do Virtual Threads
          „fork“ tasks which can be stolen by other threads? Do they
          even do that? As far as I understand it, the same scheduling
          result could be achieved by using any other thread pool with
          the amount of available CPU cores as the number of threads.
          After all, the ForkJoinPool needs ForkJoinTasks to use the
          work-stealing logic. Or am I mistaken here? </p>
        <p class="MsoNormal"><u></u> <u></u></p>
        <p class="MsoNormal">Could you maybe point me to some ressources
          that explain why you decided to use the ForkJoinPool and how
          it is used? I couldn’t find any online. <br>
        </p>
      </div>
    </blockquote>
    <br>
    For starters, think of ForkJoinPool as a "better thread pool". It
    has many advantages over a thread pool that uses a shared blocking
    queue for all tasks.<br>
    <br>
    Another thing is a ForkJoinPool can be created in "async mode" which
    is local FIFO scheduling. This is good for applications doing
    message passing and also good for scheduling the tasks for virtual
    threads.<br>
    <br>
    As others have pointed out, scheduling a virtual thread to execute
    causes a special task for the thread to be pushed to one of the FJP
    submission queues. A virtual thread T1 unparking virtual thread T2
    will push the task for T2 to the submission queue of T1's carrier
    (worker thread). It may be that T2's task is executed by the that
    worker thread or it may be that some worker thread steals the task.<br>
    <br>
    There are a few other features of ForkJoinPool that you might want
    to look into. One is that it parallelism can be dynamically changed
    when workers are blocked - you'll see this is used to smooth over
    cases where virtual threads temporarily pin their carrier during
    file I/O operations. Another recent addition to ForkJoinPool is the
    ability to submit a task without signalling, look for "lazySubmit". 
    This is used to reduce steals in very specific cases such as when a
    thread is unparked while parking.</div></blockquote><div><br></div><div class="gmail_default" style="font-family:arial,helvetica,sans-serif">There are some disadvantages to this approach as well: this means that at present, Thread.yield() is counter-intuitively unfair (and thus not useful) for cooperatively switching among busy tasks [1]. As a workaround, one can use e.g. LockSupport.parkNanos(1) or similar, which causes a task to be sent to an external scheduled thread pool that immediately re-queues the task, at the cost of ping-ponging between threads. Hacking [2] the Loom implementation to use single-threaded executors avoids this problem and a few others (at the possible cost of additional dispatch latency compared to FJP for some workloads and CPU configurations).</div><div class="gmail_default" style="font-family:arial,helvetica,sans-serif"><br></div><div class="gmail_default" style="font-family:arial,helvetica,sans-serif">[1] <a href="https://www.morling.dev/blog/loom-and-thread-fairness/">https://www.morling.dev/blog/loom-and-thread-fairness/</a> (towards the bottom) - but note that the test in this blog post allocates fairly heavily inside of the loop, which might obscure the effect due to contention on the allocator; to reproduce the issue I changed the test to use a pure arithmetic computation.</div><div class="gmail_default" style="font-family:arial,helvetica,sans-serif">[2] <a href="https://github.com/dmlloyd/loomania">https://github.com/dmlloyd/loomania</a> - at your own risk, don't say I didn't warn you</div></div></div>