Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Experiment with fully virtual VirtualThreadPool #11501

Merged
merged 14 commits into from May 7, 2024
Merged
@@ -0,0 +1,15 @@
<?xml version="1.0"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "https://www.eclipse.org/jetty/configure_10_0.dtd">

<Configure>
<New id="threadPool" class="org.eclipse.jetty.util.thread.VirtualThreadPool">
<Set name="name" property="jetty.threadPool.namePrefix" />
</New>

<Call class="org.slf4j.LoggerFactory" name="getLogger">
<Arg>org.eclipse.jetty</Arg>
<Call name="info">
<Arg>All Virtual threads are enabled.</Arg>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be more clear as in "Virtual threads enabled, using only virtual threads".

Also the file name should replace "-all-" with "-full-" or "-only-".

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prefer "all". "full" is confusing when pools are involved. What does it mean to have a full non pooling pool? "Only" is ok, but it is just as easy to say "the threads used are all virtual", as it is to say "the threads used are only virtual". However, "only virtual" can be taken as a pejorative (which in this case might be appropriate :), so I prefer "all"

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like jetty-threadpool-only-virtual.xml

</Call>
</Call>
</Configure>
@@ -0,0 +1,19 @@
[description]
Enables and configures the Server ThreadPool with support for virtual threads to be used for all threads.
There is some risk of CPU pinning with this configuration. Only supported in Java 21 or later.

[depends]
logging

[provides]
threadpool

[xml]
etc/jetty-threadpool-all-virtual.xml

[ini-template]
# tag::documentation[]
## Platform threads name prefix.
#jetty.threadPool.namePrefix=vtp<hashCode>

# end::documentation[]
gregw marked this conversation as resolved.
Show resolved Hide resolved
@@ -1,5 +1,6 @@
[description]
Enables and configures the Server ThreadPool with support for virtual threads in Java 21 or later.
Enables and configures the Server ThreadPool with support for virtual threads to be used for blocking tasks.
Only supported in Java 21 or later.

[depends]
logging
Expand Down
Expand Up @@ -32,21 +32,9 @@
public class VirtualThreads
{
private static final Logger LOG = LoggerFactory.getLogger(VirtualThreads.class);
private static final Executor executor = probeVirtualThreadExecutor();
private static final Executor executor = getNamedVirtualThreadsExecutor(null);
private static final Method isVirtualThread = probeIsVirtualThread();

private static Executor probeVirtualThreadExecutor()
{
try
{
return (Executor)Executors.class.getMethod("newVirtualThreadPerTaskExecutor").invoke(null);
}
catch (Throwable x)
{
return null;
}
}

private static Method probeIsVirtualThread()
{
try
Expand Down Expand Up @@ -131,7 +119,8 @@ public static Executor getNamedVirtualThreadsExecutor(String namePrefix)
{
Class<?> builderClass = Class.forName("java.lang.Thread$Builder");
Object threadBuilder = Thread.class.getMethod("ofVirtual").invoke(null);
threadBuilder = builderClass.getMethod("name", String.class, long.class).invoke(threadBuilder, namePrefix, 0L);
if (StringUtil.isNotBlank(namePrefix))
threadBuilder = builderClass.getMethod("name", String.class, long.class).invoke(threadBuilder, namePrefix, 0L);
ThreadFactory factory = (ThreadFactory)builderClass.getMethod("factory").invoke(threadBuilder);
return (Executor)Executors.class.getMethod("newThreadPerTaskExecutor", ThreadFactory.class).invoke(null, factory);
}
Expand Down
Expand Up @@ -449,7 +449,7 @@ public void setReservedThreads(int reservedThreads)
}

/**
* @return the name of the this thread pool
* @return the name of this thread pool
*/
@ManagedAttribute("name of the thread pool")
public String getName()
Expand All @@ -460,7 +460,7 @@ public String getName()
/**
* <p>Sets the name of this thread pool, used as a prefix for the thread names.</p>
*
* @param name the name of the this thread pool
* @param name the name of this thread pool
*/
public void setName(String name)
{
Expand Down Expand Up @@ -835,7 +835,7 @@ public void join() throws InterruptedException

while (isStopping())
{
Thread.sleep(1);
Thread.onSpinWait();
}
}

Expand Down
@@ -0,0 +1,101 @@
//
// ========================================================================
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//

package org.eclipse.jetty.util.thread;

import java.io.IOException;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;

import org.eclipse.jetty.util.annotation.ManagedAttribute;
import org.eclipse.jetty.util.annotation.ManagedObject;
import org.eclipse.jetty.util.component.Dumpable;

@ManagedObject("Tracking Executor wrapper")
class TrackingExecutor implements Executor, Dumpable
gregw marked this conversation as resolved.
Show resolved Hide resolved
{
private final Executor _threadFactoryExecutor;
private final Set<Thread> _threads = ConcurrentHashMap.newKeySet();
private boolean _detailed;

TrackingExecutor(Executor executor, boolean detailed)
gregw marked this conversation as resolved.
Show resolved Hide resolved
{
_threadFactoryExecutor = executor;
_detailed = detailed;
}

@Override
public void execute(Runnable task)
{
_threadFactoryExecutor.execute(() ->
{
try
{
_threads.add(Thread.currentThread());
gregw marked this conversation as resolved.
Show resolved Hide resolved
task.run();
}
finally
{
_threads.remove(Thread.currentThread());
}
});
}

@Override
public void dump(Appendable out, String indent) throws IOException
{
Object[] threads = _threads.stream().map(DumpableThread::new).toArray();
Dumpable.dumpObjects(out, indent, _threadFactoryExecutor.toString() + " size=" + threads.length, threads);
}

public void setDetailedDump(boolean detailedDump)
{
_detailed = detailedDump;
}

@ManagedAttribute("reports additional details in the dump")
public boolean isDetailedDump()
{
return _detailed;
}

public int size()
{
return _threads.size();
}

private class DumpableThread implements Dumpable
{
private final Thread _thread;

private DumpableThread(Thread thread)
{
_thread = thread;
}

@Override
public void dump(Appendable out, String indent) throws IOException
{
if (_detailed)
{
Object[] stack = _thread.getStackTrace();
Dumpable.dumpObjects(out, indent, _thread.toString(), stack);
}
else
{
Dumpable.dumpObject(out, _thread);
}
}
}
}