11package mill .client ;
22
3+ import java .io .IOException ;
34import java .io .InputStream ;
45import java .io .OutputStream ;
56import java .io .PrintStream ;
910import java .nio .file .Path ;
1011import java .util .Map ;
1112import mill .client .lock .Locks ;
12- import mill .client .lock .TryLocked ;
1313import mill .constants .InputPumper ;
1414import mill .constants .ProxyStream ;
1515import mill .constants .ServerFiles ;
2323 *
2424 * - Client:
2525 * - 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
2727 * - Wait for server socket to be available for connection
2828 * - Server:
29- * - Take processLock .
29+ * - Take serverLock .
3030 * - If already taken, it means another server was running
3131 * (e.g. spawned by a different client) so exit immediately
3232 * - Server: loop:
@@ -46,19 +46,18 @@ public static class Result {
4646 public Path serverDir ;
4747 }
4848
49- final int serverProcessesLimit = 5 ;
5049 final int serverInitWaitMillis = 10000 ;
5150
52- public abstract void initServer (Path serverDir , boolean b , Locks locks ) throws Exception ;
51+ public abstract void initServer (Path serverDir , Locks locks ) throws Exception ;
5352
54- public abstract void preRun (Path serverDir ) throws Exception ;
53+ public abstract void prepareServerDir (Path serverDir ) throws Exception ;
5554
5655 InputStream stdin ;
5756 PrintStream stdout ;
5857 PrintStream stderr ;
5958 Map <String , String > env ;
6059 String [] args ;
61- Locks [] memoryLocks ;
60+ Locks memoryLock ;
6261 int forceFailureForTestingMillisDelay ;
6362
6463 public ServerLauncher (
@@ -67,7 +66,7 @@ public ServerLauncher(
6766 PrintStream stderr ,
6867 Map <String , String > env ,
6968 String [] args ,
70- Locks [] memoryLocks ,
69+ Locks memoryLock ,
7170 int forceFailureForTestingMillisDelay ) {
7271 this .stdin = stdin ;
7372 this .stdout = stdout ;
@@ -78,56 +77,41 @@ public ServerLauncher(
7877 // For testing in memory, we need to pass in the locks separately, so that the
7978 // locks can be shared between the different instances of `ServerLauncher` the
8079 // same way file locks are shared between different Mill client/server processes
81- this .memoryLocks = memoryLocks ;
80+ this .memoryLock = memoryLock ;
8281
8382 this .forceFailureForTestingMillisDelay = forceFailureForTestingMillisDelay ;
8483 }
8584
86- public Result acquireLocksAndRun (Path serverDir0 ) throws Exception {
85+ public Result run (Path serverDir ) throws Exception {
8786
88- final boolean setJnaNoSys = System .getProperty ("jna.nosys" ) == null ;
89- if (setJnaNoSys ) {
90- System .setProperty ("jna.nosys" , "true" );
91- }
87+ Files .createDirectories (serverDir );
9288
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 );
11790
118- int run ( Path serverDir , boolean setJnaNoSys , Locks locks ) throws Exception {
91+ Socket ioSocket = launchConnectToServer ( serverDir );
11992
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 ();
12599 }
126100
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 {
128108
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 ()) {
130111
112+ if (locks .serverLock .probe ()) initServer (serverDir , locks );
113+ while (locks .serverLock .probe ()) Thread .sleep (1 );
114+ }
131115 long retryStart = System .currentTimeMillis ();
132116 Socket ioSocket = null ;
133117 Throwable socketThrowable = null ;
@@ -140,13 +124,26 @@ int run(Path serverDir, boolean setJnaNoSys, Locks locks) throws Exception {
140124 Thread .sleep (1 );
141125 }
142126 }
143-
144127 if (ioSocket == null ) {
145128 throw new Exception ("Failed to connect to server" , socketThrowable );
146129 }
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+ }
147139
140+ Thread startStreamPumpers (Socket ioSocket ) throws Exception {
148141 InputStream outErr = ioSocket .getInputStream ();
149142 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 );
150147 ProxyStream .Pumper outPumper = new ProxyStream .Pumper (outErr , stdout , stderr );
151148 InputPumper inPump = new InputPumper (() -> stdin , () -> in , true );
152149 Thread outPumperThread = new Thread (outPumper , "outPump" );
@@ -155,23 +152,16 @@ int run(Path serverDir, boolean setJnaNoSys, Locks locks) throws Exception {
155152 inThread .setDaemon (true );
156153 outPumperThread .start ();
157154 inThread .start ();
155+ return outPumperThread ;
156+ }
158157
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 ;
175165 }
176166 }
177167}
0 commit comments