Skip to content

Commit b124dbe

Browse files
author
Vladimir Kotal
committed
use Future+Executor to enforce the timeout
1 parent b82c5e9 commit b124dbe

File tree

5 files changed

+84
-38
lines changed

5 files changed

+84
-38
lines changed

opengrok-indexer/src/main/java/org/opengrok/indexer/analysis/Ctags.java

Lines changed: 45 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,19 @@
3232
import java.io.OutputStreamWriter;
3333
import java.io.StringReader;
3434
import java.nio.charset.StandardCharsets;
35+
import java.time.Duration;
3536
import java.util.ArrayList;
3637
import java.util.List;
37-
import java.util.Timer;
38-
import java.util.TimerTask;
38+
import java.util.concurrent.ExecutionException;
39+
import java.util.concurrent.ExecutorService;
40+
import java.util.concurrent.Future;
41+
import java.util.concurrent.TimeUnit;
42+
import java.util.concurrent.TimeoutException;
3943
import java.util.logging.Level;
4044
import java.util.logging.Logger;
45+
46+
import org.opengrok.indexer.configuration.RuntimeEnvironment;
47+
import org.opengrok.indexer.index.IndexerParallelizer;
4148
import org.opengrok.indexer.logger.LoggerFactory;
4249
import org.opengrok.indexer.util.IOUtils;
4350
import org.opengrok.indexer.util.SourceSplitter;
@@ -59,7 +66,7 @@ public class Ctags implements Resettable {
5966
private String binary;
6067
private String CTagsExtraOptionsFile = null;
6168
private int tabSize;
62-
private int timeout = 0; // in seconds
69+
private Duration timeout = Duration.ofSeconds(10);
6370

6471
private boolean junit_testing = false;
6572

@@ -93,12 +100,12 @@ public void setCTagsExtraOptionsFile(String CTagsExtraOptionsFile) {
93100
this.CTagsExtraOptionsFile = CTagsExtraOptionsFile;
94101
}
95102

96-
public void setTimeout(int timeout) {
97-
this.timeout = timeout;
103+
public void setTimeout(long timeout) {
104+
this.timeout = Duration.ofSeconds(timeout);
98105
}
99106

100-
public int getTimeout() {
101-
return this.timeout;
107+
public long getTimeout() {
108+
return this.timeout.getSeconds();
102109
}
103110

104111
/**
@@ -363,6 +370,13 @@ private void addScalaSupport(List<String> command) {
363370
command.add("--regex-scala=/^[[:space:]]*package[[:space:]]+([a-zA-Z0-9_.]+)/\\1/p,packages/");
364371
}
365372

373+
/**
374+
* Run ctags on a file.
375+
* @param file file path to process
376+
* @return Definitions
377+
* @throws IOException I/O exception
378+
* @throws InterruptedException interrupted command
379+
*/
366380
public Definitions doCtags(String file) throws IOException,
367381
InterruptedException {
368382
if (file.length() < 1 || "\n".equals(file)) {
@@ -388,7 +402,7 @@ public Definitions doCtags(String file) throws IOException,
388402
CtagsReader rdr = new CtagsReader();
389403
rdr.setSplitterSupplier(() -> trySplitSource(file));
390404
rdr.setTabSize(tabSize);
391-
Definitions ret;
405+
Definitions ret = null;
392406
try {
393407
ctagsIn.write(file + "\n");
394408
if (Thread.interrupted()) {
@@ -399,35 +413,35 @@ public Definitions doCtags(String file) throws IOException,
399413
throw new InterruptedException("flush()");
400414
}
401415

402-
int timeout = this.timeout * 1000;
403-
Timer timer = null;
404416
/*
405-
* Setup timer thread to make sure the ctags process completes and the indexer can
417+
* Run the ctags reader in a time bound thread to make sure
418+
* the ctags process completes so that the indexer can
406419
* make progress instead of hanging the whole operation.
407420
*/
408-
if (timeout != 0) {
409-
timer = new Timer();
410-
timer.schedule(new TimerTask() {
411-
@Override public void run() {
412-
LOGGER.log(Level.WARNING,
413-
String.format("Terminating ctags process for file '%s' " +
414-
"due to timeout %d seconds", file, timeout / 1000));
415-
try {
416-
close();
417-
} catch (IOException e) {
418-
LOGGER.log(Level.WARNING, "Failed to terminate overly long ctags command");
419-
}
420-
}
421-
}, timeout);
422-
}
421+
IndexerParallelizer parallelizer = RuntimeEnvironment.getInstance().getIndexerParallelizer();
422+
ExecutorService executor = parallelizer.getCtagsWatcherExecutor();
423+
Future<Definitions> future = executor.submit(() -> {
424+
readTags(rdr);
425+
return rdr.getDefinitions();
426+
});
423427

424-
readTags(rdr);
428+
try {
429+
ret = future.get(getTimeout(), TimeUnit.SECONDS);
430+
} catch (ExecutionException ex) {
431+
LOGGER.log(Level.WARNING, "execution exception", ex);
432+
} catch (TimeoutException ex) {
433+
LOGGER.log(Level.WARNING,
434+
String.format("Terminating ctags process for file '%s' " +
435+
"due to timeout %d seconds", file, getTimeout()));
436+
try {
437+
close();
438+
} catch (IOException e) {
439+
LOGGER.log(Level.WARNING, "Failed to terminate overly long ctags command");
440+
}
425441

426-
if (timer != null) {
427-
timer.cancel();
442+
// Allow for retry in IndexDatabase.
443+
throw new InterruptedException("ctags timeout");
428444
}
429-
430-
ret = rdr.getDefinitions();
431445
} catch (IOException ex) {
432446
/*
433447
* In case the ctags process had to be destroyed, possibly pre-empt

opengrok-indexer/src/main/java/org/opengrok/indexer/configuration/Configuration.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@ public final class Configuration {
207207
private int tabSize;
208208
private int commandTimeout; // in seconds
209209
private int interactiveCommandTimeout; // in seconds
210-
private int ctagsTimeout; // in seconds
210+
private long ctagsTimeout; // in seconds
211211
private boolean scopesEnabled;
212212
private boolean projectsEnabled;
213213
private boolean foldingEnabled;
@@ -377,7 +377,7 @@ public void setInteractiveCommandTimeout(int commandTimeout) throws IllegalArgum
377377
this.interactiveCommandTimeout = commandTimeout;
378378
}
379379

380-
public int getCtagsTimeout() {
380+
public long getCtagsTimeout() {
381381
return ctagsTimeout;
382382
}
383383

@@ -387,7 +387,7 @@ public int getCtagsTimeout() {
387387
* @param timeout the new value
388388
* @throws IllegalArgumentException when the timeout is negative
389389
*/
390-
public void setCtagsTimeout(int timeout) throws IllegalArgumentException {
390+
public void setCtagsTimeout(long timeout) throws IllegalArgumentException {
391391
if (commandTimeout < 0) {
392392
throw new IllegalArgumentException(
393393
String.format(NEGATIVE_NUMBER_ERROR, "ctagsTimeout", timeout));
@@ -446,7 +446,7 @@ public Configuration() {
446446
setContextLimit((short) 10);
447447
//contextSurround is default(short)
448448
//ctags is default(String)
449-
setCtagsTimeout(30);
449+
setCtagsTimeout(10);
450450
setCurrentIndexedCollapseThreshold(27);
451451
setDataRoot(null);
452452
setDisplayRepositories(true);

opengrok-indexer/src/main/java/org/opengrok/indexer/configuration/ConfigurationHelp.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,8 @@ private static Object getSampleValue(Method setter, Object defaultValue) {
140140
return "user-specified-value";
141141
} else if (paramType == int.class) {
142142
return 1 + (int) defaultValue;
143+
} else if (paramType == long.class) {
144+
return 1 + (long) defaultValue;
143145
} else if (paramType == short.class) {
144146
return (short) (1 + (short) defaultValue);
145147
} else if (paramType == boolean.class) {

opengrok-indexer/src/main/java/org/opengrok/indexer/configuration/RuntimeEnvironment.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -302,11 +302,11 @@ public void setInteractiveCommandTimeout(int timeout) {
302302
setConfigurationValue("interactiveCommandTimeout", timeout);
303303
}
304304

305-
public int getCtagsTimeout() {
306-
return (int) getConfigurationValue("ctagsTimeout");
305+
public long getCtagsTimeout() {
306+
return (long) getConfigurationValue("ctagsTimeout");
307307
}
308308

309-
public void setCtagsTimeout(int timeout) {
309+
public void setCtagsTimeout(long timeout) {
310310
setConfigurationValue("ctagsTimeout", timeout);
311311
}
312312

opengrok-indexer/src/main/java/org/opengrok/indexer/index/IndexerParallelizer.java

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import java.util.concurrent.ExecutorService;
2727
import java.util.concurrent.Executors;
2828
import java.util.concurrent.ForkJoinPool;
29+
import java.util.concurrent.ScheduledThreadPoolExecutor;
2930
import java.util.logging.Logger;
3031
import org.opengrok.indexer.analysis.Ctags;
3132
import org.opengrok.indexer.analysis.CtagsValidator;
@@ -68,6 +69,9 @@ public class IndexerParallelizer implements AutoCloseable {
6869
private LazilyInstantiate<ExecutorService> lzHistoryRenamedExecutor;
6970
private ExecutorService historyRenamedExecutor;
7071

72+
private LazilyInstantiate<ExecutorService> lzCtagsWatcherExecutor;
73+
private ExecutorService ctagsWatcherExecutor;
74+
7175
/**
7276
* Initializes a new instance using settings from the specified environment
7377
* instance.
@@ -89,6 +93,7 @@ public IndexerParallelizer(RuntimeEnvironment env) {
8993
createLazyFixedExecutor();
9094
createLazyHistoryExecutor();
9195
createLazyHistoryRenamedExecutor();
96+
createLazyCtagsWatcherExecutor();
9297
}
9398

9499
/**
@@ -136,6 +141,15 @@ public ExecutorService getHistoryRenamedExecutor() {
136141
return result;
137142
}
138143

144+
/**
145+
* @return the Executor used for ctags parallelism
146+
*/
147+
public ExecutorService getCtagsWatcherExecutor() {
148+
ExecutorService result = lzCtagsWatcherExecutor.get();
149+
ctagsWatcherExecutor = result;
150+
return result;
151+
}
152+
139153
/**
140154
* Calls {@link #bounce()}, which prepares for -- but does not start -- new
141155
* pools.
@@ -195,6 +209,13 @@ public void bounce() {
195209
createLazyHistoryRenamedExecutor();
196210
formerFixedExecutor.shutdown();
197211
}
212+
213+
ExecutorService formerCtagsWatcherExecutor = ctagsWatcherExecutor;
214+
if (formerCtagsWatcherExecutor != null) {
215+
ctagsWatcherExecutor = null;
216+
createLazyCtagsWatcherExecutor();
217+
formerCtagsWatcherExecutor.shutdown();
218+
}
198219
}
199220

200221
private void createLazyForkJoinPool() {
@@ -208,6 +229,15 @@ private void createLazyCtagsPool() {
208229
new CtagsValidator(), new CtagsObjectFactory(env)));
209230
}
210231

232+
private void createLazyCtagsWatcherExecutor() {
233+
lzCtagsWatcherExecutor = LazilyInstantiate.using(() ->
234+
new ScheduledThreadPoolExecutor(1, runnable -> {
235+
Thread thread = Executors.defaultThreadFactory().newThread(runnable);
236+
thread.setName("ctags-watcher-" + thread.getId());
237+
return thread;
238+
}));
239+
}
240+
211241
private void createLazyFixedExecutor() {
212242
lzFixedExecutor = LazilyInstantiate.using(() ->
213243
Executors.newFixedThreadPool(indexingParallelism));

0 commit comments

Comments
 (0)