Skip to content

Commit fd05314

Browse files
authored
Merge pull request github#3531 from asger-semmle/js/node-version-check-notimeout
Approved by esbena
2 parents db557a4 + 823ed3b commit fd05314

File tree

1 file changed

+36
-8
lines changed

1 file changed

+36
-8
lines changed

javascript/extractor/src/com/semmle/js/parser/TypeScriptParser.java

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,12 @@ public class TypeScriptParser {
8686
*/
8787
public static final String TYPESCRIPT_TIMEOUT_VAR = "SEMMLE_TYPESCRIPT_TIMEOUT";
8888

89+
/**
90+
* An environment variable that can be set to specify a number of retries when verifying
91+
* the TypeScript installation. Default is 3.
92+
*/
93+
public static final String TYPESCRIPT_RETRIES_VAR = "SEMMLE_TYPESCRIPT_RETRIES";
94+
8995
/**
9096
* An environment variable (without the <tt>SEMMLE_</tt> or <tt>LGTM_</tt> prefix), that can be
9197
* set to indicate the maximum heap space usable by the Node.js process, in addition to its
@@ -179,9 +185,6 @@ public void verifyInstallation(boolean verbose) {
179185
public String verifyNodeInstallation() {
180186
if (nodeJsVersionString != null) return nodeJsVersionString;
181187

182-
ByteArrayOutputStream out = new ByteArrayOutputStream();
183-
ByteArrayOutputStream err = new ByteArrayOutputStream();
184-
185188
// Determine where to find the Node.js runtime.
186189
String explicitNodeJsRuntime = Env.systemEnv().get(TYPESCRIPT_NODE_RUNTIME_VAR);
187190
if (explicitNodeJsRuntime != null) {
@@ -198,12 +201,41 @@ public String verifyNodeInstallation() {
198201
nodeJsRuntimeExtraArgs = Arrays.asList(extraArgs.split("\\s+"));
199202
}
200203

204+
// Run 'node --version' with a timeout, and retry a few times if it times out.
205+
// If the Java process is suspended we may get a spurious timeout, and we want to
206+
// support long suspensions in cloud environments. Instead of setting a huge timeout,
207+
// retrying guarantees we can survive arbitrary suspensions as long as they don't happen
208+
// too many times in rapid succession.
209+
int timeout = Env.systemEnv().getInt(TYPESCRIPT_TIMEOUT_VAR, 10000);
210+
int numRetries = Env.systemEnv().getInt(TYPESCRIPT_RETRIES_VAR, 3);
211+
for (int i = 0; i < numRetries - 1; ++i) {
212+
try {
213+
return startNodeAndGetVersion(timeout);
214+
} catch (InterruptedError e) {
215+
Exceptions.ignore(e, "We will retry the call that caused this exception.");
216+
System.err.println("Starting Node.js seems to take a long time. Retrying.");
217+
}
218+
}
219+
try {
220+
return startNodeAndGetVersion(timeout);
221+
} catch (InterruptedError e) {
222+
Exceptions.ignore(e, "Exception details are not important.");
223+
throw new CatastrophicError(
224+
"Could not start Node.js (timed out after " + (timeout / 1000) + "s and " + numRetries + " attempts");
225+
}
226+
}
227+
228+
/**
229+
* Checks that Node.js is installed and can be run and returns its version string.
230+
*/
231+
private String startNodeAndGetVersion(int timeout) throws InterruptedError {
232+
ByteArrayOutputStream out = new ByteArrayOutputStream();
233+
ByteArrayOutputStream err = new ByteArrayOutputStream();
201234
Builder b =
202235
new Builder(
203236
getNodeJsRuntimeInvocation("--version"), out, err, getParserWrapper().getParentFile());
204237
b.expectFailure(); // We want to do our own logging in case of an error.
205238

206-
int timeout = Env.systemEnv().getInt(TYPESCRIPT_TIMEOUT_VAR, 10000);
207239
try {
208240
int r = b.execute(timeout);
209241
String stdout = new String(out.toByteArray());
@@ -213,10 +245,6 @@ public String verifyNodeInstallation() {
213245
"Could not start Node.js. It is required for TypeScript extraction.\n" + stderr);
214246
}
215247
return nodeJsVersionString = stdout;
216-
} catch (InterruptedError e) {
217-
Exceptions.ignore(e, "Exception details are not important.");
218-
throw new CatastrophicError(
219-
"Could not start Node.js (timed out after " + (timeout / 1000) + "s).");
220248
} catch (ResourceError e) {
221249
// In case 'node' is not found, the process builder converts the IOException
222250
// into a ResourceError.

0 commit comments

Comments
 (0)