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

Support custom ThreadFactory. #793

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

hstyi
Copy link

@hstyi hstyi commented Mar 16, 2025

This PR adds support for a custom ThreadFactory in JSch. Users can now provide their own ThreadFactory, which is particularly useful in Java 21, as it allows leveraging virtual threads for better concurrency and resource management.

Copy link

Quality Gate Failed Quality Gate failed

Failed conditions
5.7% Duplication on New Code (required ≤ 3%)

See analysis details on SonarQube Cloud

Copy link
Contributor

@norrisjeremy norrisjeremy left a comment

Choose a reason for hiding this comment

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

So a few suggestions:

  1. Instead of using Executors.defaultThreadFactory(), let's instead just use a simple lambda based Thread::new as the default, as this will functionally make this a noop change (since Executors.defaultThreadFactory() sets a Thread name, etc.).
  2. Let's initialize the new threadFactory property to a non-null value and then disallow users from ever setting it to null via setThreadFactory(), so that we never have to explicitly check for null in getThreadFactory().

I would also caution against using JSch with virtual threads, at least unless you are using the new Java 24 release that came out earlier this week, since I suspect you would likely encounter problems.
JSch makes significant use of synchronized style locking, that until the new Java 24 release results in carrier thread pinning when combined with virtual threads, which in turn can lead to difficult to diagnose deadlocks.

@@ -271,6 +273,8 @@ public class JSch {

private ConfigRepository configRepository = null;

private ThreadFactory threadFactory = null;
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
private ThreadFactory threadFactory = null;
private ThreadFactory threadFactory = Thread::new;

Comment on lines +704 to +706
public void setThreadFactory(ThreadFactory threadFactory) {
this.threadFactory = threadFactory;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
public void setThreadFactory(ThreadFactory threadFactory) {
this.threadFactory = threadFactory;
}
public void setThreadFactory(ThreadFactory threadFactory) {
this.threadFactory = Objects.requireNonNull(threadFactory);
}

Comment on lines +714 to +718
public ThreadFactory getThreadFactory() {
if (threadFactory == null)
threadFactory = Executors.defaultThreadFactory();
return threadFactory;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
public ThreadFactory getThreadFactory() {
if (threadFactory == null)
threadFactory = Executors.defaultThreadFactory();
return threadFactory;
}
public ThreadFactory getThreadFactory() {
return threadFactory;
}

Comment on lines 33 to +37
import java.util.Hashtable;
import java.util.Map;
import java.util.Vector;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
import java.util.Hashtable;
import java.util.Map;
import java.util.Vector;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.Hashtable;
import java.util.Map;
import java.util.Objects;
import java.util.Vector;
import java.util.concurrent.ThreadFactory;

Copy link
Contributor

@norrisjeremy norrisjeremy left a comment

Choose a reason for hiding this comment

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

Also, I am wondering if would it be better to put the new ThreadFactory property in the Session class instead of the JSch class, since the only places that are creating Thread objects are within the Session class or within various Channel implementations, which are strongly bound to a Session object in the first place?
What do you think?

@hstyi
Copy link
Author

hstyi commented Mar 21, 2025

@norrisjeremy Thank you for your reply.

  1. We can change synchronized to ReentrantLock.
  2. Putting threadFactory in the Session class does allow for more granular control.

In addition, JDK 21 is being used more and more, especially in high IO usage scenarios where virtual threads are very advantageous.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants