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

Use system calls to get terminal size on Linux / Mac #4497

Draft
wants to merge 7 commits into
base: 0.12.x
Choose a base branch
from

Conversation

alexarchambault
Copy link
Collaborator

This makes Mill use my native-terminal library (formerly windows-ansi) to get the terminal size. That way, Mill doesn't run tput commands every ~100 ms to query the terminal size.

@alexarchambault alexarchambault marked this pull request as ready for review February 6, 2025 21:15
Comment on lines 256 to 272
if (mill.main.client.Util.hasConsole())
try {
NativeTerminal.getSize();
canUse = true;
} catch (Throwable ex) {
canUse = false;
}
else canUse = false;

canUseNativeTerminal = canUse;
}

static void writeTerminalDims(boolean tputExists, Path serverDir) throws Exception {
String str;

try {
if (java.lang.System.console() == null) str = "0 0";
if (!mill.main.client.Util.hasConsole()) str = "0 0";
Copy link
Member

Choose a reason for hiding this comment

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

It seems we're checking mill.main.client.Util.hasConsole() twice here; once in the static blockl and once in writeTerminalDims. We can probably skip one of those checks right?

@lihaoyi
Copy link
Member

lihaoyi commented Feb 7, 2025

Trying this out on my macbook seems to add about ~200ms latency (~150ms -> ~350ms), running ./mill dist.installLocal then time ~/Github/mill/mill-assembly.jar version repeatedly in an empty folder. @alexarchambault can you see if you get similar numbers? I wonder where that 200ms is going, but if we can't figure out we may have to stick with the shelling-out-to-tput approach even if it's a bit ugly

@alexarchambault
Copy link
Collaborator Author

I'm getting the same numbers :/ Printing the duration of some of the calls to get the terminal size, I get things like

First NativeTerminal.getSize duration: 182
Second NativeTerminal.getSize duration: 0
First tput thing duration: 19
Second tput thing duration: 13

So once the native things are setup, getting the terminal size is sub-ms.

Upon initialization, jansi reads a binary file (like libjansi.jnilib) in its resources, writes it in a temporary directory, and loads it with System.load. Manually circumventing the resources / temp dir stuff speeds things up a lot, and gives back the same total time for time …/mill/mill-assembly.jar version.

@lihaoyi
Copy link
Member

lihaoyi commented Feb 7, 2025

If the resource/tempdir stuff is circumventable, does that mean that it isn't necessary for the jni functionality?

@alexarchambault
Copy link
Collaborator Author

If the resource/tempdir stuff is circumventable, does that mean that it isn't necessary for the jni functionality?

Not necessarily, we can load the native library from elsewhere on disk if we want to. I just pushed code that loads it via the coursier cache (this needs coursier/coursier#3272, so this is going to need new coursier and coursier-interface releases). If the native library is already in cache, there's no need to write a temporary file.

From native images, we can also statically link the native library, so that nothing needs to be loaded at runtime. But this needs the JNI library to push static libraries somewhere (like here or here), and jansi doesn't. It worked fine for other JNI libraries in Scala CLI, although I haven't seen that being done elsewhere.

@alexarchambault
Copy link
Collaborator Author

From Java 22 onwards, it should be possible to use FFM, JLine is able to for sure. This doesn't need to load custom native libraries upfront (although I have very little experience with it for now).

@lihaoyi
Copy link
Member

lihaoyi commented Feb 8, 2025

Unpacking the native library and caching it via coursier sounds reasonable. We just need to make sure Coursier is not on the hot path so we don't need to classload all the coursier/scala code when the library is already unpacked.

@alexarchambault alexarchambault marked this pull request as draft February 17, 2025 10:27
@alexarchambault
Copy link
Collaborator Author

(Seems this is surfacing issues with newer versions of coursier)

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