Skip to content

Commit 7578c20

Browse files
authored
Cleanups for ServerLauncher.scala (#5071)
* Fix `DoubleLock` hierarchical locking discipline. This makes the locking/unlocking order correct, and seems to make some non-deterministic `OverlappingFileLockException`s stop happening in my manual testing * Re-organize `ServerLauncher.run`, folding in `acquireLocksAndRun` into the main `run` body and breaking out parts of it into helper methods. It could be greatly simplified since we no longer need to loop over the possible server dirs to claim one * Remove `setJnaNoSys` handling, which I *think* is not necessary anymore now that we have cleaner classpath/process isolation between the Mill client and server
1 parent 51ca834 commit 7578c20

File tree

7 files changed

+59
-67
lines changed

7 files changed

+59
-67
lines changed

runner/client/src/mill/client/ServerCouldNotBeStarted.java

Lines changed: 0 additions & 7 deletions
This file was deleted.

runner/client/src/mill/client/ServerLauncher.java

Lines changed: 38 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package mill.client;
22

3+
import java.io.IOException;
34
import java.io.InputStream;
45
import java.io.OutputStream;
56
import java.io.PrintStream;
@@ -47,9 +48,9 @@ public static class Result {
4748

4849
final int serverInitWaitMillis = 10000;
4950

50-
public abstract void initServer(Path serverDir, boolean b, Locks locks) throws Exception;
51+
public abstract void initServer(Path serverDir, Locks locks) throws Exception;
5152

52-
public abstract void preRun(Path serverDir) throws Exception;
53+
public abstract void prepareServerDir(Path serverDir) throws Exception;
5354

5455
InputStream stdin;
5556
PrintStream stdout;
@@ -81,25 +82,34 @@ public ServerLauncher(
8182
this.forceFailureForTestingMillisDelay = forceFailureForTestingMillisDelay;
8283
}
8384

84-
public Result acquireLocksAndRun(Path serverDir) throws Exception {
85-
final boolean setJnaNoSys = System.getProperty("jna.nosys") == null;
86-
if (setJnaNoSys) System.setProperty("jna.nosys", "true");
85+
public Result run(Path serverDir) throws Exception {
8786

8887
Files.createDirectories(serverDir);
8988

89+
prepareServerDir(serverDir);
90+
91+
Socket ioSocket = launchConnectToServer(serverDir);
92+
93+
try {
94+
Thread outPumperThread = startStreamPumpers(ioSocket);
95+
forceTestFailure(serverDir);
96+
outPumperThread.join();
97+
} finally {
98+
ioSocket.close();
99+
}
100+
90101
Result result = new Result();
91-
preRun(serverDir);
92-
result.exitCode = run(serverDir, setJnaNoSys);
102+
result.exitCode = readExitCode(serverDir);
93103
result.serverDir = serverDir;
94104
return result;
95105
}
96106

97-
int run(Path serverDir, boolean setJnaNoSys) throws Exception {
107+
Socket launchConnectToServer(Path serverDir) throws Exception {
98108

99109
try (Locks locks = memoryLock != null ? memoryLock : Locks.files(serverDir.toString());
100110
mill.client.lock.Locked locked = locks.clientLock.lock()) {
101111

102-
if (locks.serverLock.probe()) initServer(serverDir, setJnaNoSys, locks);
112+
if (locks.serverLock.probe()) initServer(serverDir, locks);
103113
while (locks.serverLock.probe()) Thread.sleep(1);
104114
}
105115
long retryStart = System.currentTimeMillis();
@@ -114,11 +124,20 @@ int run(Path serverDir, boolean setJnaNoSys) throws Exception {
114124
Thread.sleep(1);
115125
}
116126
}
117-
118127
if (ioSocket == null) {
119128
throw new Exception("Failed to connect to server", socketThrowable);
120129
}
130+
return ioSocket;
131+
}
121132

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+
}
139+
140+
Thread startStreamPumpers(Socket ioSocket) throws Exception {
122141
InputStream outErr = ioSocket.getInputStream();
123142
OutputStream in = ioSocket.getOutputStream();
124143
in.write(Util.hasConsole() ? 1 : 0);
@@ -133,23 +152,16 @@ int run(Path serverDir, boolean setJnaNoSys) throws Exception {
133152
inThread.setDaemon(true);
134153
outPumperThread.start();
135154
inThread.start();
155+
return outPumperThread;
156+
}
136157

137-
try {
138-
if (forceFailureForTestingMillisDelay > 0) {
139-
Thread.sleep(forceFailureForTestingMillisDelay);
140-
throw new Exception("Force failure for testing: " + serverDir);
141-
}
142-
outPumperThread.join();
143-
144-
Path exitCodeFile = serverDir.resolve(ServerFiles.exitCode);
145-
if (Files.exists(exitCodeFile)) {
146-
return Integer.parseInt(Files.readAllLines(exitCodeFile).get(0));
147-
} else {
148-
System.err.println("mill-server/ exitCode file not found");
149-
return 1;
150-
}
151-
} finally {
152-
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;
153165
}
154166
}
155167
}

runner/client/src/mill/client/lock/DoubleLock.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,10 @@ public TryLocked tryLock() throws Exception {
2222
if (l1.isLocked() && l2.isLocked()) {
2323
return new DoubleTryLocked(l1, l2);
2424
} else {
25-
l1.release();
25+
// Unlock the locks in the opposite order in which we originally took them
2626
l2.release();
27+
l1.release();
28+
2729
return new DoubleTryLocked(null, null);
2830
}
2931
}
@@ -40,8 +42,9 @@ public boolean probe() throws Exception {
4042

4143
@Override
4244
public void close() throws Exception {
43-
lock1.close();
45+
// Unlock the locks in the opposite order in which we originally took them
4446
lock2.close();
47+
lock1.close();
4548
}
4649

4750
@Override

runner/client/src/mill/client/lock/MemoryLock.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,10 @@ public Locked lock() {
1919

2020
@Override
2121
public MemoryTryLocked tryLock() {
22-
if (innerLock.tryLock()) return new MemoryTryLocked(innerLock);
23-
else return new MemoryTryLocked(null);
22+
MemoryTryLocked res =
23+
innerLock.tryLock() ? new MemoryTryLocked(innerLock) : new MemoryTryLocked(null);
24+
25+
return res;
2426
}
2527

2628
@Override

runner/launcher/src/mill/launcher/MillLauncherMain.java

Lines changed: 5 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -50,35 +50,23 @@ public static void main(String[] args) throws Exception {
5050
optsArgs.toArray(new String[0]),
5151
null,
5252
-1) {
53-
public void initServer(Path serverDir, boolean setJnaNoSys, Locks locks)
54-
throws Exception {
55-
MillProcessLauncher.launchMillServer(serverDir, setJnaNoSys);
53+
public void initServer(Path serverDir, Locks locks) throws Exception {
54+
MillProcessLauncher.launchMillServer(serverDir);
5655
}
5756

58-
public void preRun(Path serverDir) throws Exception {
57+
public void prepareServerDir(Path serverDir) throws Exception {
5958
MillProcessLauncher.prepareMillRunFolder(serverDir);
6059
}
6160
};
6261

6362
final String versionAndJvmHomeEncoding =
6463
Util.md5hex(mill.client.BuildInfo.millVersion + MillProcessLauncher.javaHome());
6564
Path serverDir0 = Paths.get(OutFiles.out, OutFiles.millServer, versionAndJvmHomeEncoding);
66-
int exitCode = launcher.acquireLocksAndRun(serverDir0).exitCode;
65+
int exitCode = launcher.run(serverDir0).exitCode;
6766
if (exitCode == ClientUtil.ExitServerCodeWhenVersionMismatch()) {
68-
exitCode = launcher.acquireLocksAndRun(serverDir0).exitCode;
67+
exitCode = launcher.run(serverDir0).exitCode;
6968
}
7069
System.exit(exitCode);
71-
} catch (ServerCouldNotBeStarted e) {
72-
// TODO: try to run in-process
73-
System.err.println("Could not start a Mill server process.\n"
74-
+ "This could be caused by too many already running Mill instances "
75-
+ "or by an unsupported platform.\n"
76-
+ e.getMessage() + "\n");
77-
78-
System.err.println(
79-
"Loading Mill in-process isn't possible.\n" + "Please check your Mill installation!");
80-
throw e;
81-
8270
} catch (Exception e) {
8371
System.err.println("Mill client failed with unknown exception");
8472
e.printStackTrace();

runner/launcher/src/mill/launcher/MillProcessLauncher.java

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,11 @@
2323
public class MillProcessLauncher {
2424

2525
static int launchMillNoServer(String[] args) throws Exception {
26-
final boolean setJnaNoSys = System.getProperty("jna.nosys") == null;
2726
final String sig = String.format("%08x", UUID.randomUUID().hashCode());
2827
final Path processDir = Paths.get(".").resolve(out).resolve(millNoServer).resolve(sig);
2928

3029
final List<String> l = new ArrayList<>();
31-
l.addAll(millLaunchJvmCommand(setJnaNoSys));
30+
l.addAll(millLaunchJvmCommand());
3231
l.add("mill.daemon.MillMain");
3332
l.add(processDir.toAbsolutePath().toString());
3433
l.addAll(millOpts());
@@ -57,9 +56,9 @@ static int launchMillNoServer(String[] args) throws Exception {
5756
}
5857
}
5958

60-
static void launchMillServer(Path serverDir, boolean setJnaNoSys) throws Exception {
59+
static void launchMillServer(Path serverDir) throws Exception {
6160
List<String> l = new ArrayList<>();
62-
l.addAll(millLaunchJvmCommand(setJnaNoSys));
61+
l.addAll(millLaunchJvmCommand());
6362
l.add("mill.daemon.MillServerMain");
6463
l.add(serverDir.toFile().getCanonicalPath());
6564

@@ -202,17 +201,12 @@ static String javaExe() throws Exception {
202201
}
203202
}
204203

205-
static List<String> millLaunchJvmCommand(boolean setJnaNoSys) throws Exception {
204+
static List<String> millLaunchJvmCommand() throws Exception {
206205
final List<String> vmOptions = new ArrayList<>();
207206

208207
// Java executable
209208
vmOptions.add(javaExe());
210209

211-
// jna
212-
if (setJnaNoSys) {
213-
vmOptions.add("-Djna.nosys=true");
214-
}
215-
216210
// sys props
217211
final Properties sysProps = System.getProperties();
218212
for (final String k : sysProps.stringPropertyNames()) {

runner/server/test/src/mill/server/ClientServerTests.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -95,8 +95,8 @@ object ClientServerTests extends TestSuite {
9595
memoryLock,
9696
forceFailureForTestingMillisDelay
9797
) {
98-
def preRun(serverDir: Path) = { /*do nothing*/ }
99-
def initServer(serverDir: Path, b: Boolean, locks: Locks) = {
98+
def prepareServerDir(serverDir: Path) = { /*do nothing*/ }
99+
def initServer(serverDir: Path, locks: Locks) = {
100100
val processId = "server-" + nextServerId
101101
nextServerId += 1
102102
new Thread(new EchoServer(
@@ -106,7 +106,7 @@ object ClientServerTests extends TestSuite {
106106
testLogEvenWhenServerIdWrong
107107
)).start()
108108
}
109-
}.acquireLocksAndRun((outDir / "server-0").relativeTo(os.pwd).toNIO)
109+
}.run((outDir / "server-0").relativeTo(os.pwd).toNIO)
110110

111111
ClientResult(
112112
result.exitCode,

0 commit comments

Comments
 (0)