88import java .net .InetAddress ;
99import java .net .ServerSocket ;
1010import java .time .Duration ;
11- import java .util .ArrayList ;
1211import java .util .Collection ;
1312import java .util .Collections ;
1413import java .util .List ;
1514import java .util .Map ;
1615import java .util .Objects ;
1716import java .util .concurrent .CopyOnWriteArrayList ;
17+ import java .util .concurrent .Executors ;
18+ import java .util .concurrent .ScheduledExecutorService ;
19+ import java .util .concurrent .TimeUnit ;
1820import java .util .function .BiFunction ;
1921import java .util .function .Consumer ;
2022
23+ import com .google .common .util .concurrent .ThreadFactoryBuilder ;
24+
2125import com .sun .jdi .VirtualMachine ;
2226import jdk .jshell .execution .JdiInitiator ;
2327import jdk .jshell .execution .RemoteExecutionControl ;
3337public final class ForkingExecutionControlProvider
3438 implements ExecutionControlProvider {
3539
36- private static final Duration defaultTimeout = Duration .ofMillis (3000 );
37-
3840 public static ForkingExecutionControlProvider create () {
39- return create (List .of (), EmptyClassFileStore .create ());
41+ return create (
42+ List .of (),
43+ EmptyClassFileStore .create (),
44+ Executors .newScheduledThreadPool (
45+ 1 ,
46+ new ThreadFactoryBuilder ().setDaemon (true ).build ()
47+ )
48+ );
4049 }
4150
51+ private static final Duration defaultTimeout = Duration .ofMillis (3000 );
52+ private static final Duration defaultExecutionTimeout =
53+ Duration .ofSeconds (30 );
54+
4255 public static ForkingExecutionControlProvider create (
4356 Collection <String > rawVirtualMachineOptions ,
44- ClassFileStore classFileStore
57+ ClassFileStore classFileStore ,
58+ ScheduledExecutorService scheduler
4559 ) {
4660 Objects .requireNonNull (classFileStore , "classFileStore" );
4761 Objects .requireNonNull (rawVirtualMachineOptions , "rawVirtualMachineOptions" );
48- var host = InetAddress .getLoopbackAddress ().getHostName ();
4962 return new ForkingExecutionControlProvider (
50- host ,
63+ defaultExecutionTimeout ,
5164 defaultTimeout ,
5265 List .copyOf (rawVirtualMachineOptions ),
53- classFileStore
66+ classFileStore ,
67+ scheduler
5468 );
5569 }
5670
57- private final String host ;
58- private final Duration timeout ;
71+ private final Duration connectTimeout ;
72+ private final Duration executionTimeout ;
5973 private final List <String > rawVirtualMachineOptions ;
74+ private final ScheduledExecutorService scheduler ;
6075 private final ClassFileStore classFileStore ;
6176
6277 private ForkingExecutionControlProvider (
63- String host ,
64- Duration timeout ,
78+ Duration executionTimeout ,
79+ Duration connectTimeout ,
6580 List <String > rawVirtualMachineOptions ,
66- ClassFileStore classFileStore
81+ ClassFileStore classFileStore ,
82+ ScheduledExecutorService scheduler
6783 ) {
68- this .host = host ;
69- this .timeout = timeout ;
84+ this .executionTimeout = executionTimeout ;
85+ this .connectTimeout = connectTimeout ;
7086 this .rawVirtualMachineOptions = rawVirtualMachineOptions ;
7187 this .classFileStore = classFileStore ;
88+ this .scheduler = scheduler ;
7289 }
7390
7491 @ Override
@@ -101,7 +118,7 @@ private Box initiate(int port) {
101118 /* remoteAgentClassName */ remoteAgentClassName ,
102119 /* controlledLaunch */ false ,
103120 /* host */ "" ,
104- /* timeout */ (int ) timeout .toMillis (),
121+ /* timeout */ (int ) connectTimeout .toMillis (),
105122 /* connectorOptions*/ Collections .emptyMap ()
106123 );
107124 return new Box (
@@ -115,7 +132,7 @@ private Box initiate(int port) {
115132 ExecutionControl create (ExecutionEnv environment ) throws IOException {
116133 var address = InetAddress .getLoopbackAddress ();
117134 try (var listener = new ServerSocket (0 , backlog , address )) {
118- listener .setSoTimeout ((int ) timeout .toMillis ());
135+ listener .setSoTimeout ((int ) connectTimeout .toMillis ());
119136 var box = initiate (listener .getLocalPort ());
120137 return accept (listener , environment , box );
121138 }
@@ -164,10 +181,19 @@ private BiFunction<ObjectInput, ObjectOutput, ExecutionControl> createControl(
164181 );
165182 hooks .add (event -> environment .closeDown ());
166183 hooks .add (event -> control .disposeMachine ());
184+ scheduleExecutionTimeout (control );
167185 return control ;
168186 };
169187 }
170188
189+ private void scheduleExecutionTimeout (ForkedExecutionControl control ) {
190+ scheduler .schedule (() -> {
191+ if (!control .isClosed ()) {
192+ control .close ();
193+ }
194+ }, executionTimeout .toMillis (), TimeUnit .MILLISECONDS );
195+ }
196+
171197 private Map <String , OutputStream > createOutputs (ExecutionEnv environment ) {
172198 return Map .of (
173199 "out" , environment .userOut (),
0 commit comments