Skip to content

Commit 3f46358

Browse files
committed
Increase connection timeout for compiler process
1 parent bf0659f commit 3f46358

File tree

4 files changed

+159
-39
lines changed

4 files changed

+159
-39
lines changed

impl/src/main/saker/java/compiler/impl/RemoteJavaRMIProcess.java

+62-33
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@
3333
import saker.build.thirdparty.saker.util.io.IOUtils;
3434
import saker.build.thirdparty.saker.util.io.ProcessUtils;
3535
import saker.build.thirdparty.saker.util.io.StreamUtils;
36-
import saker.build.thirdparty.saker.util.io.UnsyncByteArrayOutputStream;
3736
import saker.java.compiler.impl.compile.handler.incremental.RemoteJavaCompilerCacheKey;
3837
import saker.java.compiler.impl.util.RMICompatUtil;
3938

@@ -54,31 +53,39 @@ public RemoteJavaRMIProcess(List<String> commands, String workingdirectory, Clas
5453
}
5554
proc = pb.start();
5655

57-
final int port;
58-
56+
int port = 0;
5957
try {
6058
InputStream procin = proc.getInputStream();
61-
try (UnsyncByteArrayOutputStream portnumbuf = new UnsyncByteArrayOutputStream()) {
62-
while (true) {
63-
int r = procin.read();
64-
if (r < 0 || r == '\n' || r == '\r') {
65-
break;
66-
}
67-
portnumbuf.write(r);
59+
while (true) {
60+
int r = procin.read();
61+
if (r < 0) {
62+
throw new IOException(
63+
"Failed to read port number from remote Java compiler process, received EOF on standard output.");
64+
}
65+
if (r == '\n' || r == '\r') {
66+
//reached end of port number value
67+
break;
68+
}
69+
if (r < '0' || r > '9') {
70+
throw new IOException("Invalid character read for port number from remote Java compiler process, port: "
71+
+ port + " char: 0x" + Integer.toHexString(r));
6872
}
69-
if (portnumbuf.isEmpty()) {
70-
//XXX reify exception?
71-
throw new IOException("Failed to read port number.");
73+
port = port * 10 + (r - '0');
74+
if (port > 0xFFFF) {
75+
throw new IOException("Invalid port number read from remote Java compiler process: " + port);
7276
}
73-
port = Integer.parseInt(portnumbuf.toString());
77+
}
78+
if (port == 0) {
79+
//XXX reify exception?
80+
throw new IOException("Failed to read port number from remote Java compiler process.");
7481
}
7582
address = new InetSocketAddress(InetAddress.getLoopbackAddress(), port);
7683
RMIOptions options = new RMIOptions().classLoader(cloader).transferProperties(rmiproperties)
7784
.workerThreadGroup(connectionThreadGroup);
7885
if (RemoteJavaCompilerCacheKey.COLLECT_RMI_STATS) {
7986
options.collectStatistics(true);
8087
}
81-
connection = options.connect(address);
88+
connection = RMICompatUtil.connectWithRMISocketConfiguration(options, address);
8289
if (RemoteJavaCompilerCacheKey.COLLECT_RMI_STATS) {
8390
RMICompatUtil.addDumpCloseListener(connection);
8491
}
@@ -92,10 +99,21 @@ public void run() {
9299
} catch (Exception e) {
93100
waitDestroyForcibly();
94101
printProcessStreams();
95-
throw new IOException(
96-
"Failed to connect to remote Java process." + (address == null ? "" : " (" + address + ")")
97-
+ " (Exit code: " + ProcessUtils.getExitCodeIfExited(proc) + ")",
98-
e);
102+
StringBuilder sb = new StringBuilder("Failed to connect to remote Java compiler process.");
103+
if (address != null) {
104+
sb.append(" (");
105+
sb.append(address);
106+
sb.append(")");
107+
}
108+
sb.append(" (Exit code: ");
109+
sb.append(ProcessUtils.getExitCodeIfExited(proc));
110+
sb.append(")");
111+
if (port != 0) {
112+
sb.append(" (Port: ");
113+
sb.append(port);
114+
sb.append(")");
115+
}
116+
throw new IOException(sb.toString(), e);
99117
}
100118
}
101119

@@ -152,22 +170,33 @@ public boolean isValid() {
152170
}
153171

154172
private void printProcessStreams() {
173+
IOException e1 = null;
174+
IOException e2 = null;
155175
String cmd = StringUtils.toStringJoin("\"", "\" \"", commands, "\"");
156-
System.err.println(" ---- StdOut from command: " + cmd);
157-
try {
158-
StreamUtils.copyStream(proc.getInputStream(), System.err);
159-
} catch (IOException e) {
160-
e.printStackTrace();
161-
}
162-
System.err.println();
163-
System.err.println(" ---- StdErr from command: " + cmd);
164-
try {
165-
StreamUtils.copyStream(proc.getErrorStream(), System.err);
166-
} catch (IOException e) {
167-
e.printStackTrace();
176+
synchronized (System.err) {
177+
System.err.println(" ---- StdOut from command: " + cmd);
178+
try {
179+
StreamUtils.copyStream(proc.getInputStream(), System.err);
180+
} catch (IOException e) {
181+
e1 = e;
182+
}
183+
System.err.println();
184+
System.err.println(" ---- StdErr from command: " + cmd);
185+
try {
186+
StreamUtils.copyStream(proc.getErrorStream(), System.err);
187+
} catch (IOException e) {
188+
e2 = e;
189+
}
190+
System.err.println();
191+
System.err.println(" -------- ");
192+
if (e1 != null) {
193+
e1.printStackTrace(System.err);
194+
}
195+
if (e2 != null) {
196+
e2.printStackTrace(System.err);
197+
}
168198
}
169-
System.err.println();
170-
System.err.println(" -------- ");
199+
171200
}
172201

173202
public RMIConnection getConnection() {

impl/src/main/saker/java/compiler/impl/compile/handler/incremental/RemoteJavaCompilerCacheKey.java

+8-5
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@
3636
public class RemoteJavaCompilerCacheKey implements CacheKey<RemoteCompiler, RemoteJavaRMIProcess> {
3737
private static final Class<JavaCompilerDaemon> JAVA_COMPILER_DAEMON_MAIN_CLASS = saker.java.compiler.impl.launching.JavaCompilerDaemon.class;
3838
private static final String JAVA_COMPILER_DAEMON_MAIN_CLASS_NAME = JAVA_COMPILER_DAEMON_MAIN_CLASS.getName();
39+
private static final String MAIN_CLASS_BUNDLE_IDENTIFIER = NestUtils
40+
.getClassBundleIdentifier(JAVA_COMPILER_DAEMON_MAIN_CLASS).toString();
3941

4042
public static final boolean COLLECT_RMI_STATS = saker.build.meta.Versions.VERSION_FULL_COMPOUND >= 8_015
4143
&& (System.getProperty(PropertyNames.PROPERTY_COLLECT_RMI_STATISTICS) != null || TestFlag.ENABLED);
@@ -72,8 +74,9 @@ public RemoteJavaRMIProcess allocate() throws Exception {
7274
if (COLLECT_RMI_STATS) {
7375
commands.add("-D" + PropertyNames.PROPERTY_COLLECT_RMI_STATISTICS + "=true");
7476
}
75-
NestBundleClassLoader cl = (NestBundleClassLoader) IncrementalCompilationHandler.class.getClassLoader();
76-
RepositoryEnvironment repoenv = cl.getRepository().getRepositoryEnvironment();
77+
ClassLoader classloader = IncrementalCompilationHandler.class.getClassLoader();
78+
NestBundleClassLoader nestbundlecl = (NestBundleClassLoader) classloader;
79+
RepositoryEnvironment repoenv = nestbundlecl.getRepository().getRepositoryEnvironment();
7780
commands.add(saker.build.launching.Main.class.getName());
7881
commands.add("action");
7982
commands.add("-storage-dir");
@@ -87,9 +90,9 @@ public RemoteJavaRMIProcess allocate() throws Exception {
8790
commands.add("-class");
8891
commands.add(JAVA_COMPILER_DAEMON_MAIN_CLASS_NAME);
8992
commands.add("-bundle");
90-
commands.add(NestUtils.getClassBundleIdentifier(JAVA_COMPILER_DAEMON_MAIN_CLASS).toString());
91-
return new RemoteJavaRMIProcess(commands, IncrementalCompilationHandler.class.getClassLoader(),
92-
JavaUtil.getCompilationRMIProperties(), environment.getEnvironmentThreadGroup());
93+
commands.add(MAIN_CLASS_BUNDLE_IDENTIFIER);
94+
return new RemoteJavaRMIProcess(commands, classloader, JavaUtil.getCompilationRMIProperties(),
95+
environment.getEnvironmentThreadGroup());
9396
}
9497

9598
@Override

impl/src/main/saker/java/compiler/impl/launching/SakerRMIDaemon.java

+17-1
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,21 @@ public void run() throws IOException {
5858
System.setErr(stderrps);
5959
System.setIn(StreamUtils.nullInputStream());
6060

61-
runServer(prevout, prevout, preverr, stdout, stderr);
61+
try {
62+
runServer(prevout, prevout, preverr, stdout, stderr);
63+
} catch (Throwable e) {
64+
try {
65+
System.setOut(prevout);
66+
System.setErr(preverr);
67+
} catch (Throwable e2) {
68+
try {
69+
e.addSuppressed(e2);
70+
} catch (Throwable e3) {
71+
// failed to suppress, don't try, ignore. some serious error has happened
72+
}
73+
}
74+
throw e;
75+
}
6276
}
6377
}
6478
}
@@ -86,6 +100,8 @@ protected void setupConnection(Socket acceptedsocket, RMIConnection connection)
86100
}
87101
}
88102
}) {
103+
RMICompatUtil.setRMIServerConnectionTimeoutToPropertyOrDefault(server);
104+
89105
portprintout.println(server.getPort());
90106
portprintout.flush();
91107
portprintout = null;

impl/src/main/saker/java/compiler/impl/util/RMICompatUtil.java

+72
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,71 @@
33
import java.io.IOException;
44
import java.io.OutputStream;
55
import java.io.OutputStreamWriter;
6+
import java.net.SocketAddress;
67

78
import saker.build.thirdparty.saker.rmi.connection.RMIConnection;
9+
import saker.build.thirdparty.saker.rmi.connection.RMIOptions;
10+
import saker.build.thirdparty.saker.rmi.connection.RMIServer;
11+
import saker.build.thirdparty.saker.rmi.connection.RMISocketConfiguration;
812
import saker.build.thirdparty.saker.util.io.StreamUtils;
913

1014
public class RMICompatUtil {
15+
/**
16+
* The default {@link RMIServer} connection timeout that should be used.
17+
* <p>
18+
* This value should be somewhat greater than a few seconds, as connecting and handshake could take longer under
19+
* heavy CPU load during builds.
20+
*/
21+
public static final int DEFAULT_RMI_SERVER_CONNECTION_TIMEOUT = 30000;
22+
23+
/**
24+
* Negative: don't set as saker.rmi version doesn't support it.
25+
* <p>
26+
* Zero: infinite. <br>
27+
* Positive: The default value as in {@link #DEFAULT_RMI_SERVER_CONNECTION_TIMEOUT} or the property value.
28+
*/
29+
public static final int RMI_SERVER_CONNECTION_TIMEOUT_PROPERTY_VALUE;
30+
static {
31+
int result;
32+
if (saker.build.meta.Versions.THIRDPARTY_SAKER_RMI_VERSION_FULL_COMPOUND >= 8_002) {
33+
result = DEFAULT_RMI_SERVER_CONNECTION_TIMEOUT;
34+
final String propertyname = "saker.java.compiler.rmi.server.connection.timeout";
35+
String val = System.getProperty(propertyname);
36+
if (val != null) {
37+
try {
38+
result = Integer.parseInt(val);
39+
} catch (NumberFormatException e) {
40+
// XXX: include in build trace somehow
41+
synchronized (System.err) {
42+
System.err.println(
43+
"Failed to parse property: " + propertyname + " with value: " + val + " caused by:");
44+
e.printStackTrace(System.err);
45+
}
46+
}
47+
}
48+
} else {
49+
result = -1;
50+
}
51+
RMI_SERVER_CONNECTION_TIMEOUT_PROPERTY_VALUE = result;
52+
}
53+
54+
private RMICompatUtil() {
55+
throw new UnsupportedOperationException();
56+
}
57+
58+
public static RMIConnection connectWithRMISocketConfiguration(RMIOptions options, SocketAddress address)
59+
throws IOException {
60+
if (saker.build.meta.Versions.THIRDPARTY_SAKER_RMI_VERSION_FULL_COMPOUND >= 8_002) {
61+
RMISocketConfiguration socketconfig = new RMISocketConfiguration();
62+
if (RMI_SERVER_CONNECTION_TIMEOUT_PROPERTY_VALUE >= 0) {
63+
socketconfig.setConnectionTimeout(RMI_SERVER_CONNECTION_TIMEOUT_PROPERTY_VALUE);
64+
}
65+
socketconfig.setConnectionInterruptible(true);
66+
return options.connect(address, socketconfig);
67+
}
68+
return options.connect(address);
69+
}
70+
1171
public static void addDumpCloseListener(OutputStream stderrin, RMIConnection connection) {
1272
connection.addCloseListener(new RMIConnection.CloseListener() {
1373
@Override
@@ -30,4 +90,16 @@ public void onConnectionClosed() {
3090
}
3191
});
3292
}
93+
94+
public static void setRMIServerConnectionTimeout(RMIServer server, int timeout) {
95+
if (saker.build.meta.Versions.THIRDPARTY_SAKER_RMI_VERSION_FULL_COMPOUND >= 8_002) {
96+
server.setConnectionTimeout(timeout);
97+
}
98+
}
99+
100+
public static void setRMIServerConnectionTimeoutToPropertyOrDefault(RMIServer server) {
101+
if (RMI_SERVER_CONNECTION_TIMEOUT_PROPERTY_VALUE >= 0) {
102+
server.setConnectionTimeout(RMI_SERVER_CONNECTION_TIMEOUT_PROPERTY_VALUE);
103+
}
104+
}
33105
}

0 commit comments

Comments
 (0)