1
1
package mill .client ;
2
2
3
+ import java .io .IOException ;
3
4
import java .io .InputStream ;
4
5
import java .io .OutputStream ;
5
6
import java .io .PrintStream ;
9
10
import java .nio .file .Path ;
10
11
import java .util .Map ;
11
12
import mill .client .lock .Locks ;
12
- import mill .client .lock .TryLocked ;
13
13
import mill .constants .InputPumper ;
14
14
import mill .constants .ProxyStream ;
15
15
import mill .constants .ServerFiles ;
23
23
*
24
24
* - Client:
25
25
* - Take clientLock
26
- * - If processLock is not yet taken, it means server is not running, so spawn a server
26
+ * - If serverLock is not yet taken, it means server is not running, so spawn a server
27
27
* - Wait for server socket to be available for connection
28
28
* - Server:
29
- * - Take processLock .
29
+ * - Take serverLock .
30
30
* - If already taken, it means another server was running
31
31
* (e.g. spawned by a different client) so exit immediately
32
32
* - Server: loop:
@@ -46,19 +46,18 @@ public static class Result {
46
46
public Path serverDir ;
47
47
}
48
48
49
- final int serverProcessesLimit = 5 ;
50
49
final int serverInitWaitMillis = 10000 ;
51
50
52
- public abstract void initServer (Path serverDir , boolean b , Locks locks ) throws Exception ;
51
+ public abstract void initServer (Path serverDir , Locks locks ) throws Exception ;
53
52
54
- public abstract void preRun (Path serverDir ) throws Exception ;
53
+ public abstract void prepareServerDir (Path serverDir ) throws Exception ;
55
54
56
55
InputStream stdin ;
57
56
PrintStream stdout ;
58
57
PrintStream stderr ;
59
58
Map <String , String > env ;
60
59
String [] args ;
61
- Locks [] memoryLocks ;
60
+ Locks memoryLock ;
62
61
int forceFailureForTestingMillisDelay ;
63
62
64
63
public ServerLauncher (
@@ -67,7 +66,7 @@ public ServerLauncher(
67
66
PrintStream stderr ,
68
67
Map <String , String > env ,
69
68
String [] args ,
70
- Locks [] memoryLocks ,
69
+ Locks memoryLock ,
71
70
int forceFailureForTestingMillisDelay ) {
72
71
this .stdin = stdin ;
73
72
this .stdout = stdout ;
@@ -78,56 +77,41 @@ public ServerLauncher(
78
77
// For testing in memory, we need to pass in the locks separately, so that the
79
78
// locks can be shared between the different instances of `ServerLauncher` the
80
79
// same way file locks are shared between different Mill client/server processes
81
- this .memoryLocks = memoryLocks ;
80
+ this .memoryLock = memoryLock ;
82
81
83
82
this .forceFailureForTestingMillisDelay = forceFailureForTestingMillisDelay ;
84
83
}
85
84
86
- public Result acquireLocksAndRun (Path serverDir0 ) throws Exception {
85
+ public Result run (Path serverDir ) throws Exception {
87
86
88
- final boolean setJnaNoSys = System .getProperty ("jna.nosys" ) == null ;
89
- if (setJnaNoSys ) {
90
- System .setProperty ("jna.nosys" , "true" );
91
- }
87
+ Files .createDirectories (serverDir );
92
88
93
- int serverIndex = 0 ;
94
- while (serverIndex < serverProcessesLimit ) { // Try each possible server process (-1 to -5)
95
- serverIndex ++;
96
- final Path serverDir =
97
- serverDir0 .getParent ().resolve (serverDir0 .getFileName () + "-" + serverIndex );
98
-
99
- Files .createDirectories (serverDir );
100
-
101
- try (Locks locks = memoryLocks != null
102
- ? memoryLocks [serverIndex - 1 ]
103
- : Locks .files (serverDir .toString ());
104
- TryLocked clientLocked = locks .clientLock .tryLock ()) {
105
- if (clientLocked .isLocked ()) {
106
- Result result = new Result ();
107
- preRun (serverDir );
108
- result .exitCode = run (serverDir , setJnaNoSys , locks );
109
- result .serverDir = serverDir ;
110
- return result ;
111
- }
112
- }
113
- }
114
- throw new ServerCouldNotBeStarted (
115
- "Reached max server processes limit: " + serverProcessesLimit );
116
- }
89
+ prepareServerDir (serverDir );
117
90
118
- int run ( Path serverDir , boolean setJnaNoSys , Locks locks ) throws Exception {
91
+ Socket ioSocket = launchConnectToServer ( serverDir );
119
92
120
- try (OutputStream f = Files .newOutputStream (serverDir .resolve (ServerFiles .runArgs ))) {
121
- f .write (Util .hasConsole () ? 1 : 0 );
122
- ClientUtil .writeString (f , BuildInfo .millVersion );
123
- ClientUtil .writeArgs (args , f );
124
- ClientUtil .writeMap (env , f );
93
+ try {
94
+ Thread outPumperThread = startStreamPumpers (ioSocket );
95
+ forceTestFailure (serverDir );
96
+ outPumperThread .join ();
97
+ } finally {
98
+ ioSocket .close ();
125
99
}
126
100
127
- if (locks .processLock .probe ()) initServer (serverDir , setJnaNoSys , locks );
101
+ Result result = new Result ();
102
+ result .exitCode = readExitCode (serverDir );
103
+ result .serverDir = serverDir ;
104
+ return result ;
105
+ }
106
+
107
+ Socket launchConnectToServer (Path serverDir ) throws Exception {
128
108
129
- while (locks .processLock .probe ()) Thread .sleep (1 );
109
+ try (Locks locks = memoryLock != null ? memoryLock : Locks .files (serverDir .toString ());
110
+ mill .client .lock .Locked locked = locks .clientLock .lock ()) {
130
111
112
+ if (locks .serverLock .probe ()) initServer (serverDir , locks );
113
+ while (locks .serverLock .probe ()) Thread .sleep (1 );
114
+ }
131
115
long retryStart = System .currentTimeMillis ();
132
116
Socket ioSocket = null ;
133
117
Throwable socketThrowable = null ;
@@ -140,13 +124,26 @@ int run(Path serverDir, boolean setJnaNoSys, Locks locks) throws Exception {
140
124
Thread .sleep (1 );
141
125
}
142
126
}
143
-
144
127
if (ioSocket == null ) {
145
128
throw new Exception ("Failed to connect to server" , socketThrowable );
146
129
}
130
+ return ioSocket ;
131
+ }
132
+
133
+ private void forceTestFailure (Path serverDir ) throws Exception {
134
+ if (forceFailureForTestingMillisDelay > 0 ) {
135
+ Thread .sleep (forceFailureForTestingMillisDelay );
136
+ throw new Exception ("Force failure for testing: " + serverDir );
137
+ }
138
+ }
147
139
140
+ Thread startStreamPumpers (Socket ioSocket ) throws Exception {
148
141
InputStream outErr = ioSocket .getInputStream ();
149
142
OutputStream in = ioSocket .getOutputStream ();
143
+ in .write (Util .hasConsole () ? 1 : 0 );
144
+ ClientUtil .writeString (in , BuildInfo .millVersion );
145
+ ClientUtil .writeArgs (args , in );
146
+ ClientUtil .writeMap (env , in );
150
147
ProxyStream .Pumper outPumper = new ProxyStream .Pumper (outErr , stdout , stderr );
151
148
InputPumper inPump = new InputPumper (() -> stdin , () -> in , true );
152
149
Thread outPumperThread = new Thread (outPumper , "outPump" );
@@ -155,23 +152,16 @@ int run(Path serverDir, boolean setJnaNoSys, Locks locks) throws Exception {
155
152
inThread .setDaemon (true );
156
153
outPumperThread .start ();
157
154
inThread .start ();
155
+ return outPumperThread ;
156
+ }
158
157
159
- if (forceFailureForTestingMillisDelay > 0 ) {
160
- Thread .sleep (forceFailureForTestingMillisDelay );
161
- throw new Exception ("Force failure for testing: " + serverDir );
162
- }
163
- outPumperThread .join ();
164
-
165
- try {
166
- Path exitCodeFile = serverDir .resolve (ServerFiles .exitCode );
167
- if (Files .exists (exitCodeFile )) {
168
- return Integer .parseInt (Files .readAllLines (exitCodeFile ).get (0 ));
169
- } else {
170
- System .err .println ("mill-server/ exitCode file not found" );
171
- return 1 ;
172
- }
173
- } finally {
174
- ioSocket .close ();
158
+ int readExitCode (Path serverDir ) throws IOException {
159
+ Path exitCodeFile = serverDir .resolve (ServerFiles .exitCode );
160
+ if (Files .exists (exitCodeFile )) {
161
+ return Integer .parseInt (Files .readAllLines (exitCodeFile ).get (0 ));
162
+ } else {
163
+ System .err .println ("mill-server/ exitCode file not found" );
164
+ return 1 ;
175
165
}
176
166
}
177
167
}
0 commit comments