Skip to content

Commit

Permalink
Merge pull request #6 from mnlipp/fix/vthreads-conf
Browse files Browse the repository at this point in the history
Allow usage of virtual threads to be disabled.
  • Loading branch information
mnlipp authored Oct 2, 2024
2 parents 92348d1 + 67724d1 commit 4921109
Show file tree
Hide file tree
Showing 8 changed files with 91 additions and 49 deletions.
32 changes: 30 additions & 2 deletions org.jgrapes.core/src/org/jgrapes/core/Components.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import java.util.WeakHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicLong;
import java.util.logging.Level;
import org.jgrapes.core.annotation.ComponentManager;
Expand All @@ -53,11 +54,37 @@
public class Components {

private static ExecutorService defaultExecutorService
= Executors.newVirtualThreadPerTaskExecutor();
= useVirtualThreads() ? Executors.newVirtualThreadPerTaskExecutor()
: Executors.newCachedThreadPool(
new ThreadFactory() {
@SuppressWarnings({ "PMD.CommentRequired",
"PMD.MissingOverride" })
public Thread newThread(Runnable runnable) {
Thread thread
= Executors.defaultThreadFactory()
.newThread(runnable);
thread.setDaemon(true);
return thread;
}
});

private static ExecutorService timerExecutorService
= defaultExecutorService;

/**
* JGrapes uses virtual thread by default. However, as of
* 2024, some debuggers still have problems with virtual
* threads. Therefore it is possible to switch back to
* platform threads by starting the JVM with property
* `-Djgrapes.useVirtualThreads=false`.
*
* @return true, if successful
*/
public static boolean useVirtualThreads() {
return Boolean.parseBoolean(
System.getProperty("jgrapes.useVirtualThreads", "true"));
}

private Components() {
}

Expand Down Expand Up @@ -452,7 +479,8 @@ private static class Scheduler extends Thread {
*/
@SuppressWarnings("PMD.ConstructorCallsOverridableMethod")
public Scheduler() {
ofVirtual().name("Components.Scheduler").start(this);
(useVirtualThreads() ? ofVirtual() : ofPlatform())
.name("Components.Scheduler").start(this);
}

/**
Expand Down
5 changes: 3 additions & 2 deletions org.jgrapes.io/src/org/jgrapes/io/InputStreamMonitor.java
Original file line number Diff line number Diff line change
Expand Up @@ -144,8 +144,9 @@ public void onStart(Start event) {
() -> {
return ByteBuffer.allocateDirect(bufferSize);
}, 2);
runner = Thread.ofVirtual().name(Components.simpleObjectName(this))
.start(this);
runner = (Components.useVirtualThreads() ? Thread.ofVirtual()
: Thread.ofPlatform()).name(Components.simpleObjectName(this))
.start(this);
}
}

Expand Down
5 changes: 3 additions & 2 deletions org.jgrapes.io/src/org/jgrapes/io/NioDispatcher.java
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,9 @@ public void onStart(Start event) {
if (runner != null && !runner.isInterrupted()) {
return;
}
runner = Thread.ofVirtual().name(Components.simpleObjectName(this))
.start(this);
runner = (Components.useVirtualThreads() ? Thread.ofVirtual()
: Thread.ofPlatform()).name(Components.simpleObjectName(this))
.start(this);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
import java.nio.Buffer;
import java.nio.charset.Charset;
import java.util.function.Consumer;

import org.jgrapes.core.Components;
import org.jgrapes.io.events.Input;

/**
Expand All @@ -42,9 +44,10 @@ public class ManagedBufferStreamer implements InputConsumer {
* @param processor the processor
*/
public ManagedBufferStreamer(Consumer<Reader> processor) {
Thread thread = Thread.ofVirtual().start(() -> {
processor.accept(reader);
});
Thread thread = (Components.useVirtualThreads() ? Thread.ofVirtual()
: Thread.ofPlatform()).start(() -> {
processor.accept(reader);
});
ThreadCleaner.watch(this, thread);
}

Expand Down
25 changes: 14 additions & 11 deletions org.jgrapes.io/src/org/jgrapes/io/util/ThreadCleaner.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
import java.util.HashSet;
import java.util.Set;

import org.jgrapes.core.Components;

/**
* Cleans up threads if some object has been garbage collected.
*
Expand Down Expand Up @@ -71,18 +73,19 @@ public RefWithThread(Object referent, Thread thread) {
}

static {
Thread.ofVirtual().name("ThreadCleaner").start(() -> {
while (true) {
try {
ThreadCleaner.RefWithThread ref
= (ThreadCleaner.RefWithThread) abandoned.remove();
ref.watched.interrupt();
watched.remove(ref);
} catch (InterruptedException e) { // NOPMD
// Nothing to do
(Components.useVirtualThreads() ? Thread.ofVirtual()
: Thread.ofPlatform()).name("ThreadCleaner").start(() -> {
while (true) {
try {
ThreadCleaner.RefWithThread ref
= (ThreadCleaner.RefWithThread) abandoned.remove();
ref.watched.interrupt();
watched.remove(ref);
} catch (InterruptedException e) { // NOPMD
// Nothing to do
}
}
}
});
});
}

/**
Expand Down
3 changes: 2 additions & 1 deletion org.jgrapes.io/src/org/jgrapes/net/SocketServer.java
Original file line number Diff line number Diff line change
Expand Up @@ -417,7 +417,8 @@ public void onRegistered(NioRegistration.Completed event)
return;
}
registration = event.event().get();
purger = Thread.ofVirtual().start(new Purger());
purger = (Components.useVirtualThreads() ? Thread.ofVirtual()
: Thread.ofPlatform()).start(new Purger());
fire(new Ready(serverSocketChannel.getLocalAddress()));
return;
}
Expand Down
34 changes: 18 additions & 16 deletions org.jgrapes.util/src/org/jgrapes/util/FileSystemWatcher.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
import java.util.stream.StreamSupport;
import org.jgrapes.core.Channel;
import org.jgrapes.core.Component;
import org.jgrapes.core.Components;
import org.jgrapes.core.Event;
import org.jgrapes.core.Manager;
import org.jgrapes.core.annotation.Handler;
Expand Down Expand Up @@ -195,25 +196,26 @@ private Watcher(FileSystem fileSystem) throws IOException {
.stream(fileSystem.getRootDirectories().spliterator(), false)
.map(Path::toString)
.collect(Collectors.joining(File.pathSeparator));
Thread.ofVirtual().name(roots + " watcher")
.start(() -> {
while (true) {
try {
WatchKey key = watchService.take();
// Events have to be consumed
key.pollEvents();
if (!(key.watchable() instanceof Path)) {
(Components.useVirtualThreads() ? Thread.ofVirtual()
: Thread.ofPlatform()).name(roots + " watcher")
.start(() -> {
while (true) {
try {
WatchKey key = watchService.take();
// Events have to be consumed
key.pollEvents();
if (!(key.watchable() instanceof Path)) {
key.reset();
continue;
}
handleWatchEvent((Path) key.watchable());
key.reset();
continue;
} catch (InterruptedException e) {
logger.log(Level.WARNING, e,
() -> "No WatchKey: " + e.getMessage());
}
handleWatchEvent((Path) key.watchable());
key.reset();
} catch (InterruptedException e) {
logger.log(Level.WARNING, e,
() -> "No WatchKey: " + e.getMessage());
}
}
});
});
}
}

Expand Down
27 changes: 15 additions & 12 deletions org.jgrapes.util/src/org/jgrapes/util/Password.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
import java.util.Arrays;
import java.util.Optional;

import org.jgrapes.core.Components;

/**
* Stores a password in such a way that it can be cleared. Automatically
* clears the storage if an object of this type becomes weakly reachable.
Expand All @@ -50,19 +52,20 @@ public class Password {
public Password(char[] password) {
synchronized (Password.class) {
if (purger == null) {
purger = Thread.ofVirtual().name("PasswordPurger").start(() -> {
while (true) {
try {
Reference<? extends Password> passwordRef
= toBeCleared.remove();
Optional.ofNullable(passwordRef.get())
.ifPresent(Password::clear);
passwordRef.clear();
} catch (InterruptedException e) {
break;
purger = (Components.useVirtualThreads() ? Thread.ofVirtual()
: Thread.ofPlatform()).name("PasswordPurger").start(() -> {
while (true) {
try {
Reference<? extends Password> passwordRef
= toBeCleared.remove();
Optional.ofNullable(passwordRef.get())
.ifPresent(Password::clear);
passwordRef.clear();
} catch (InterruptedException e) {
break;
}
}
}
});
});
}
}
this.password = password;
Expand Down

0 comments on commit 4921109

Please sign in to comment.