aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/main/java/com/google/devtools/build/lib/server
diff options
context:
space:
mode:
authorGravatar Lukacs Berki <lberki@google.com>2016-10-06 14:20:04 +0000
committerGravatar Damien Martin-Guillerez <dmarting@google.com>2016-10-07 08:05:28 +0000
commit3a3c4833dc24b90b203c637b36b0d4901a5b1e81 (patch)
tree7d55e3adea58e45eb35970a33e1a646ccf3c820c /src/main/java/com/google/devtools/build/lib/server
parentf341bc4f6e918b6a41c1536c111bbf24f14f967b (diff)
Remove support for using AF_UNIX to communicate between the Bazel cient and the Bazel server.
RELNOTES[INC]: --command_port=-1 to use AF_UNIX for client/server communications is not supported anymore. -- MOS_MIGRATED_REVID=135355673
Diffstat (limited to 'src/main/java/com/google/devtools/build/lib/server')
-rw-r--r--src/main/java/com/google/devtools/build/lib/server/AfUnixServer.java558
-rw-r--r--src/main/java/com/google/devtools/build/lib/server/GrpcServerImpl.java55
-rw-r--r--src/main/java/com/google/devtools/build/lib/server/RPCServer.java67
3 files changed, 58 insertions, 622 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/server/AfUnixServer.java b/src/main/java/com/google/devtools/build/lib/server/AfUnixServer.java
deleted file mode 100644
index 4444d8424e..0000000000
--- a/src/main/java/com/google/devtools/build/lib/server/AfUnixServer.java
+++ /dev/null
@@ -1,558 +0,0 @@
-// Copyright 2014 The Bazel Authors. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.devtools.build.lib.server;
-
-import com.google.common.base.Preconditions;
-import com.google.common.base.Splitter;
-import com.google.common.collect.ImmutableList;
-import com.google.common.io.ByteStreams;
-import com.google.devtools.build.lib.runtime.BlazeCommandDispatcher.ShutdownMethod;
-import com.google.devtools.build.lib.server.RPCService.UnknownCommandException;
-import com.google.devtools.build.lib.unix.LocalClientSocket;
-import com.google.devtools.build.lib.unix.LocalServerSocket;
-import com.google.devtools.build.lib.unix.LocalSocketAddress;
-import com.google.devtools.build.lib.unix.NativePosixFiles;
-import com.google.devtools.build.lib.util.Clock;
-import com.google.devtools.build.lib.util.ThreadUtils;
-import com.google.devtools.build.lib.util.io.OutErr;
-import com.google.devtools.build.lib.util.io.StreamMultiplexer;
-import com.google.devtools.build.lib.vfs.Path;
-import java.io.EOFException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.net.Socket;
-import java.net.SocketTimeoutException;
-import java.nio.charset.Charset;
-import java.util.List;
-import java.util.Random;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicLong;
-import java.util.logging.Logger;
-
-/**
- * An RPCServer server is a Java object that sits and waits for RPC requests
- * (the sit-and-wait is implemented in {@link #serve()}). These requests
- * arrive via UNIX file sockets. The RPCServer then calls the application
- * (which implements ServerCommand) to handle the request. (Since the Blaze
- * server may need to stat hundreds of directories during initialization, this
- * is a significant speedup.) The server thread will terminate after idling
- * for a user-specified time.
- *
- * Note: If you are contemplating to call into the RPCServer from
- * within Java, consider using the {@link RPCService} class instead.
- */
-// TODO(bazel-team): Signal handling.
-// TODO(bazel-team): Gives clients status information when the server is busy. One
-// way to do this is to put the server status in a file (pid, the current
-// target, etc) in the server directory. Alternatively, we can have a separate
-// thread taking care of the server socket and put the information into socket
-// handshakes.
-// TODO(bazel-team): Use Reporter for server-side messages.
-public final class AfUnixServer extends RPCServer {
- private final Clock clock;
- private final RPCService rpcService;
- private final LocalServerSocket serverSocket;
- private final long maxIdleMillis;
- private final long statusCheckMillis;
- private final Path serverDirectory;
- private final Path workspaceDir;
- private static final Logger LOG = Logger.getLogger(AfUnixServer.class.getName());
- private volatile boolean lameDuck;
-
- private static final long STATUS_CHECK_PERIOD_MILLIS = 1000 * 60; // 1 minute.
- private static final Splitter NULLTERMINATOR_SPLITTER = Splitter.on('\0');
-
- /**
- * Create a new server instance. After creating the server, you can start it
- * by calling the {@link #serve()} method.
- *
- * @param clock The clock to take time measurements
- * @param rpcService The underlying service object, which takes
- * care of dispatching to the {@link ServerCommand}
- * instances, as requests arrive.
- * @param maxIdleMillis The maximum time the server will wait idly.
- * @param statusCheckPeriodMillis How long to wait between system status checks.
- * @param serverDirectory Directory to put file socket and pid files, etc.
- * @param workspaceDir The workspace. Used solely to ensure it persists.
- * @throws IOException
- */
- public AfUnixServer(Clock clock, RPCService rpcService,
- long maxIdleMillis, long statusCheckPeriodMillis,
- Path serverDirectory, Path workspaceDir)
- throws IOException {
- super(serverDirectory);
- this.clock = clock;
- this.rpcService = rpcService;
- this.maxIdleMillis = maxIdleMillis;
- this.statusCheckMillis = statusCheckPeriodMillis;
- this.serverDirectory = serverDirectory;
- this.workspaceDir = workspaceDir;
-
- this.serverSocket = openServerSocket();
- serverSocket.setSoTimeout(Math.min(maxIdleMillis, statusCheckMillis));
- lameDuck = false;
- }
-
- /**
- * Create a new server instance. After creating the server, you can start it
- * by calling the {@link #serve()} method.
- *
- * @param clock The clock to take time measurements
- * @param rpcService The underlying service object, which takes
- * care of dispatching to the {@link ServerCommand}
- * instances, as requests arrive.
- * @param maxIdleMillis The maximum time the server will wait idly.
- * @param serverDirectory Directory to put file socket and pid files, etc.
- * @param workspaceDir The workspace. Used solely to ensure it persists.
- * @throws IOException
- */
- public AfUnixServer(Clock clock, RPCService rpcService,
- long maxIdleMillis, Path serverDirectory, Path workspaceDir)
- throws IOException {
- this(clock, rpcService, maxIdleMillis, STATUS_CHECK_PERIOD_MILLIS,
- serverDirectory, workspaceDir);
- }
-
-
- private final AtomicBoolean inAction = new AtomicBoolean(false);
- private final AtomicBoolean allowingInterrupt = new AtomicBoolean(true);
- private final AtomicLong cmdNum = new AtomicLong();
- private final Thread mainThread = Thread.currentThread();
- private final Object interruptLock = new Object();
-
- @Override
- public void interrupt() {
- // Only interrupt during actions - otherwise we may end up setting the interrupt bit
- // at the end of a build and responding to it at the beginning of the subsequent build.
- synchronized (interruptLock) {
- if (allowingInterrupt.get()) {
- mainThread.interrupt();
- }
- }
-
- if (inAction.get()) {
- Runnable interruptWatcher =
- new Runnable() {
- @Override
- public void run() {
- try {
- long originalCmd = cmdNum.get();
- Thread.sleep(10 * 1000);
- if (inAction.get() && cmdNum.get() == originalCmd) {
- // We're still operating on the same command.
- // Interrupt took too long.
- ThreadUtils.warnAboutSlowInterrupt();
- }
- } catch (InterruptedException e) {
- // Ignore.
- }
- }
- };
- Thread interruptWatcherThread =
- new Thread(interruptWatcher, "interrupt-watcher-" + cmdNum);
- interruptWatcherThread.setDaemon(true);
- interruptWatcherThread.start();
- }
- }
-
- /**
- * Wait on a socket for business (answer requests). Note that this
- * method won't return until the server shuts down.
- */
- @Override
- public void serve() {
- try {
- while (!lameDuck) {
- try {
- IdleServerTasks idleChecker = new IdleServerTasks(workspaceDir);
- idleChecker.idle();
- RequestIo requestIo;
-
- long startTime = clock.currentTimeMillis();
- while (true) {
- try {
- allowingInterrupt.set(true);
- Socket socket = serverSocket.accept();
- long firstContactTime = clock.currentTimeMillis();
- requestIo = new RequestIo(socket, firstContactTime);
- break;
- } catch (SocketTimeoutException e) {
- long idleTime = clock.currentTimeMillis() - startTime;
- if (lameDuck) {
- closeServerSocket();
- return;
- } else if (idleTime > maxIdleMillis
- || (idleTime > statusCheckMillis && !idleChecker.continueProcessing(idleTime))) {
- enterLameDuck();
- }
- }
- }
- idleChecker.busy();
-
-
- List<String> request = null;
- try {
- request = extractRequest(requestIo);
- cmdNum.incrementAndGet();
- inAction.set(true);
- if (request != null) {
- executeRequest(request, requestIo);
- }
- } finally {
- inAction.set(false);
- // Don't reset interruption unless we executed a request. Otherwise this is just a
- // ping from the client verifying our existence, in which case we should retain the
- // interrupt status for the subsequent request.
- if (request != null) {
- synchronized (interruptLock) {
- allowingInterrupt.set(false);
- Thread.interrupted(); // clears thread interrupted status
- }
- }
- requestIo.shutdown();
- switch (rpcService.getShutdown()) {
- case NONE:
- break;
-
- case CLEAN:
- return;
-
- case EXPUNGE:
- disableShutdownHooks();
- return;
- }
- }
- } catch (EOFException e) {
- LOG.info("Connection to the client lost: "
- + e.getMessage());
- } catch (IOException e) {
- // Something else happened. Print a stack trace for debugging.
- printStack(e);
- }
- }
- } finally {
- rpcService.shutdown(ShutdownMethod.CLEAN);
- LOG.info("Logging finished");
- }
- }
-
- private void closeServerSocket() {
- LOG.info("Closing serverSocket.");
- try {
- serverSocket.close();
- } catch (IOException e) {
- printStack(e);
- }
-
- if (!lameDuck) {
- try {
- getSocketPath().delete();
- } catch (IOException e) {
- printStack(e);
- }
- }
- }
-
- /**
- * Allow one last request to be serviced.
- */
- private void enterLameDuck() {
- lameDuck = true;
- try {
- getSocketPath().delete();
- } catch (IOException e) {
- e.printStackTrace();
- }
- serverSocket.setSoTimeout(1);
- }
-
- /**
- * Returns the path of the socket file to be used.
- */
- public Path getSocketPath() {
- return serverDirectory.getRelative("server.socket");
- }
-
- /**
- * Ensures no other server is running for the current socket file. This
- * guarantees that no two servers are running against the same output
- * directory.
- *
- * @throws IOException if another server holds the lock for the socket file.
- */
- public static void ensureExclusiveAccess(Path socketFile) throws IOException {
- LocalSocketAddress address =
- new LocalSocketAddress(socketFile.getPathFile());
- if (socketFile.exists()) {
- try {
- new LocalClientSocket(address).close();
- } catch (IOException e) {
- // The previous server process is dead--unlink the file:
- socketFile.delete();
- return;
- }
- // TODO(bazel-team): (2009) Read the previous server's pid from the "hello" message
- // and add it to the message.
- throw new IOException("Socket file " + socketFile.getPathString()
- + " is locked by another server");
- }
- }
-
- /**
- * Opens a UNIX local server socket.
- * @throws IOException if the socket file is used by another server or can
- * not be made exclusive.
- */
- private LocalServerSocket openServerSocket() throws IOException {
- // This is the "well known" socket path via which the server is found...
- Path socketFile = getSocketPath();
-
- // ...but it may have a name that's too long for AF_UNIX, in which case we
- // make it a symlink to /tmp/something. This typically only happens in
- // tests where the --output_base is beneath a very deep temp dir.
- // (All this extra complexity is just used in tests... *sigh*).
- if (socketFile.toString().length() >= 104) { // = UNIX_PATH_MAX
- Path socketLink = socketFile;
- String tmpDirDefault = System.getenv("TMPDIR");
- if (tmpDirDefault == null
- || tmpDirDefault.length() > 104 - "/blaze-4294967296/server.socket".length()) {
- // Default for unset TMPDIR, or if TMPDIR is so that the resulting
- // path would be too long.
- tmpDirDefault = "/tmp";
- }
- String tmpDir = System.getProperty("blaze.rpcserver.tmpdir", tmpDirDefault);
- socketFile = createTempSocketDirectory(socketFile.getRelative(tmpDir)).
- getRelative("server.socket");
- LOG.info("Using symlinked socket at " + socketFile);
-
- socketLink.delete(); // Remove stale symlink, if any.
- socketLink.createSymbolicLink(socketFile);
-
- deleteAtExit(socketLink, /*deleteParent=*/false);
- deleteAtExit(socketFile, /*deleteParent=*/true);
- } else {
- deleteAtExit(socketFile, /*deleteParent=*/false);
- }
-
- ensureExclusiveAccess(socketFile);
-
-
- LocalServerSocket serverSocket = new LocalServerSocket();
- serverSocket.bind(new LocalSocketAddress(socketFile.getPathFile()));
- NativePosixFiles.chmod(socketFile.getPathFile(), 0600); // Lock it down.
- serverSocket.listen(/*backlog=*/50);
- return serverSocket;
- }
-
- // Atomically create a new directory in the (assumed sticky) /tmp directory for use with a
- // Unix domain socket. The directory will be mode 0700. Retries indefinitely until it
- // succeeds.
- private static Path createTempSocketDirectory(Path tempDir) {
- Random random = new Random();
- while (true) {
- Path socketDir = tempDir.getRelative(String.format("blaze-%d", random.nextInt()));
- try {
- if (socketDir.createDirectory()) {
- // Make sure it's private; unfortunately, createDirectory() doesn't take a mode
- // argument.
- socketDir.chmod(0700);
- return socketDir; // Created.
- }
- // Already existed; try again.
- } catch (IOException e) {
- // Failed; try again.
- }
- }
- }
-
- /**
- * Read a string in platform default encoding and split it into a list of
- * NUL-separated words.
- *
- * <p>Blaze consistently uses the platform default encoding (defined in
- * blaze.cc) to interface with Unix APIs.
- */
- private static List<String> readRequest(InputStream input) throws IOException {
- byte[] sizeBuffer = new byte[4];
- ByteStreams.readFully(input, sizeBuffer);
- int size = ((sizeBuffer[0] & 0xff) << 24)
- + ((sizeBuffer[1] & 0xff) << 16)
- + ((sizeBuffer[2] & 0xff) << 8)
- + (sizeBuffer[3] & 0xff);
- byte[] inputBytes = new byte[size];
- ByteStreams.readFully(input, inputBytes);
-
- String s = new String(inputBytes, Charset.defaultCharset());
- return ImmutableList.copyOf(NULLTERMINATOR_SPLITTER.split(s));
- }
-
- private static List<String> extractRequest(RequestIo requestIo) throws IOException {
- List<String> request = readRequest(requestIo.in);
- if (request == null) {
- LOG.info("Short-circuiting empty request");
- return null;
- }
- return request;
- }
-
- private void executeRequest(List<String> request, RequestIo requestIo) {
- Preconditions.checkNotNull(request);
- int exitStatus = 2;
- try {
- exitStatus = rpcService.executeRequest(request, requestIo.requestOutErr,
- requestIo.firstContactTime);
- LOG.info("Finished executing request");
- } catch (UnknownCommandException e) {
- requestIo.requestOutErr.printErrLn("SERVER ERROR: " + e.getMessage());
- LOG.severe("SERVER ERROR: " + e.getMessage());
- } catch (Exception e) {
- // Stacktrace for unknown exception.
- StringWriter trace = new StringWriter();
- e.printStackTrace(new PrintWriter(trace, true));
- requestIo.requestOutErr.printErr("SERVER ERROR: " + trace);
- LOG.severe("SERVER ERROR: " + trace);
- }
-
- if (rpcService.getShutdown() != ShutdownMethod.NONE) {
- // In case of shutdown, disable the listening socket *before* we write
- // the last part of the response. Otherwise, a sufficiently fast client
- // could read the response and exit, and a new client could make a
- // connection to this server, which is still in the listening state, even
- // though it is about to shut down imminently.
- closeServerSocket();
- }
-
- requestIo.writeExitStatus(exitStatus);
- }
-
- /**
- * Because it's a little complicated, this class factors out all the IO Hook
- * up we need per request, that is, in
- * {@link AfUnixServer#executeRequest(List, RequestIo)}.
- * It's unfortunately complicated, so it's explained here.
- */
- private static class RequestIo {
-
- // Used by the client code
- private final InputStream in;
- private final OutErr requestOutErr;
- private final OutputStream controlChannel;
-
- // just used by this class to keep the state around
- private final Socket requestSocket;
- private final OutputStream requestOut;
- private final long firstContactTime;
-
- RequestIo(Socket requestSocket, long firstContactTime) throws IOException {
- this.requestSocket = requestSocket;
- this.firstContactTime = firstContactTime;
- this.in = requestSocket.getInputStream();
- this.requestOut = requestSocket.getOutputStream();
-
- // We encode the response sent to the client with a multiplexer so
- // we can send three streams (out / err / control) over one wire stream
- // (requestOut).
- StreamMultiplexer multiplexer = new StreamMultiplexer(requestOut);
-
- // We'll be writing control messages (exit code + out of date message)
- // to this control channel.
- controlChannel = multiplexer.createControl();
-
- // This is the outErr part of the multiplexed output.
- requestOutErr = OutErr.create(multiplexer.createStdout(),
- multiplexer.createStderr());
- // We hook up System.out / System.err to our IO object. Stuff written to
- // System.out / System.err will show up on the user's screen, prefixed
- // with "System.out "/"System.err ".
- requestOutErr.addSystemOutErrAsSource();
- }
-
- public void writeExitStatus(int exitStatus) {
- // Make sure to flush the output / error streams prior to writing the exit status.
- // The client may stop reading that direction of the socket immediately upon reading the
- // exit code.
- flushOutErr();
- try {
- controlChannel.write((exitStatus >> 24) & 0xff);
- controlChannel.write((exitStatus >> 16) & 0xff);
- controlChannel.write((exitStatus >> 8) & 0xff);
- controlChannel.write(exitStatus & 0xff);
- controlChannel.flush();
- LOG.info("" + exitStatus);
- } catch (IOException ignored) {
- // This exception is historically ignored.
- }
- }
-
- private void flushOutErr() {
- try {
- requestOutErr.getOutputStream().flush();
- } catch (IOException e) {
- printStack(e);
- }
- try {
- requestOutErr.getErrorStream().flush();
- } catch (IOException e) {
- printStack(e);
- }
- }
-
- public void shutdown() {
- try {
- requestOut.close();
- } catch (IOException e) {
- printStack(e);
- }
- try {
- in.close();
- } catch (IOException e) {
- printStack(e);
- }
- try {
- requestSocket.close();
- } catch (IOException e) {
- printStack(e);
- }
- }
- }
-
- /**
- * Creates and returns a new RPC server.
- * Use {@link AfUnixServer#serve()} to start the server.
- *
- * @param appCommand The application's ServerCommand implementation.
- * @param serverDirectory The directory for server-related files. The caller
- * must ensure the directory has been created.
- * @param workspaceDir The workspace, used solely to ensure it persists.
- * @param maxIdleSeconds The idle time in seconds after which the rpc
- * server will die unless it receives a request.
- */
- public static AfUnixServer newServerWith(Clock clock,
- ServerCommand appCommand,
- Path serverDirectory,
- Path workspaceDir,
- int maxIdleSeconds)
- throws IOException {
- // Creates and starts the RPC server.
- RPCService service = new RPCService(appCommand);
-
- return new AfUnixServer(clock, service, maxIdleSeconds * 1000L,
- serverDirectory, workspaceDir);
- }
-
-}
diff --git a/src/main/java/com/google/devtools/build/lib/server/GrpcServerImpl.java b/src/main/java/com/google/devtools/build/lib/server/GrpcServerImpl.java
index a3678c72c9..ffdf473a78 100644
--- a/src/main/java/com/google/devtools/build/lib/server/GrpcServerImpl.java
+++ b/src/main/java/com/google/devtools/build/lib/server/GrpcServerImpl.java
@@ -46,6 +46,8 @@ import io.grpc.stub.ServerCallStreamObserver;
import io.grpc.stub.StreamObserver;
import java.io.IOException;
import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
import java.net.InetSocketAddress;
import java.nio.charset.Charset;
import java.security.SecureRandom;
@@ -96,7 +98,7 @@ import javax.annotation.concurrent.GuardedBy;
* to cancel it. Cancellation is done by the client sending the server a {@code cancel()} RPC call
* which results in the main thread of the command being interrupted.
*/
-public class GrpcServerImpl extends RPCServer {
+public class GrpcServerImpl implements RPCServer {
private static final Logger log = Logger.getLogger(GrpcServerImpl.class.getName());
// UTF-8 won't do because we want to be able to pass arbitrary binary strings.
@@ -421,6 +423,8 @@ public class GrpcServerImpl extends RPCServer {
private static final String REQUEST_COOKIE_FILE = "request_cookie";
private static final String RESPONSE_COOKIE_FILE = "response_cookie";
+ private static final AtomicBoolean runShutdownHooks = new AtomicBoolean(true);
+
@GuardedBy("runningCommands")
private final Map<String, RunningCommand> runningCommands = new HashMap<>();
private final CommandExecutor commandExecutor;
@@ -439,7 +443,14 @@ public class GrpcServerImpl extends RPCServer {
public GrpcServerImpl(CommandExecutor commandExecutor, Clock clock, int port,
Path serverDirectory, int maxIdleSeconds) throws IOException {
- super(serverDirectory);
+ // server.pid was written in the C++ launcher after fork() but before exec() .
+ // The client only accesses the pid file after connecting to the socket
+ // which ensures that it gets the correct pid value.
+ Path pidFile = serverDirectory.getRelative("server.pid.txt");
+ Path pidSymlink = serverDirectory.getRelative("server.pid");
+ deleteAtExit(pidFile, /*deleteParent=*/ false);
+ deleteAtExit(pidSymlink, /*deleteParent=*/ false);
+
this.commandExecutor = commandExecutor;
this.clock = clock;
this.serverDirectory = serverDirectory;
@@ -610,6 +621,46 @@ public class GrpcServerImpl extends RPCServer {
deleteAtExit(file, false);
}
+ protected void disableShutdownHooks() {
+ runShutdownHooks.set(false);
+ }
+
+ /**
+ * Schedule the specified file for (attempted) deletion at JVM exit.
+ */
+ protected static void deleteAtExit(final Path path, final boolean deleteParent) {
+ Runtime.getRuntime().addShutdownHook(new Thread() {
+ @Override
+ public void run() {
+ if (!runShutdownHooks.get()) {
+ return;
+ }
+
+ try {
+ path.delete();
+ if (deleteParent) {
+ path.getParentDirectory().delete();
+ }
+ } catch (IOException e) {
+ printStack(e);
+ }
+ }
+ });
+ }
+
+ static void printStack(IOException e) {
+ /*
+ * Hopefully this never happens. It's not very nice to just write this
+ * to the user's console, but I'm not sure what better choice we have.
+ */
+ StringWriter err = new StringWriter();
+ PrintWriter printErr = new PrintWriter(err);
+ printErr.println("=======[BLAZE SERVER: ENCOUNTERED IO EXCEPTION]=======");
+ e.printStackTrace(printErr);
+ printErr.println("=====================================================");
+ log.severe(err.toString());
+ }
+
private void executeCommand(
RunRequest request, StreamObserver<RunResponse> observer, GrpcSink sink) {
sink.setCommandThread(Thread.currentThread());
diff --git a/src/main/java/com/google/devtools/build/lib/server/RPCServer.java b/src/main/java/com/google/devtools/build/lib/server/RPCServer.java
index 40ccdeb915..8b7aefbef4 100644
--- a/src/main/java/com/google/devtools/build/lib/server/RPCServer.java
+++ b/src/main/java/com/google/devtools/build/lib/server/RPCServer.java
@@ -17,85 +17,28 @@ import com.google.devtools.build.lib.runtime.CommandExecutor;
import com.google.devtools.build.lib.util.Clock;
import com.google.devtools.build.lib.vfs.Path;
import java.io.IOException;
-import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.logging.Logger;
/**
- * A server instance. Can either an AF_UNIX or a gRPC one.
+ * A gRPC server instance.
*/
-public abstract class RPCServer {
- private static final Logger LOG = Logger.getLogger(RPCServer.class.getName());
- private static AtomicBoolean runShutdownHooks = new AtomicBoolean(true);
-
+public interface RPCServer {
/**
* Factory class for the gRPC server.
*
* Present so that we don't need to invoke a constructor with multiple arguments by reflection.
*/
- public interface Factory {
+ interface Factory {
RPCServer create(CommandExecutor commandExecutor, Clock clock, int port, Path serverDirectory,
int maxIdleSeconds) throws IOException;
}
- protected RPCServer(Path serverDirectory) throws IOException {
- // server.pid was written in the C++ launcher after fork() but before exec() .
- // The client only accesses the pid file after connecting to the socket
- // which ensures that it gets the correct pid value.
- Path pidFile = serverDirectory.getRelative("server.pid.txt");
- Path pidSymlink = serverDirectory.getRelative("server.pid");
- RPCServer.deleteAtExit(pidFile, /*deleteParent=*/ false);
- RPCServer.deleteAtExit(pidSymlink, /*deleteParent=*/ false);
- }
-
- protected void disableShutdownHooks() {
- runShutdownHooks.set(false);
- }
-
- /**
- * Schedule the specified file for (attempted) deletion at JVM exit.
- */
- protected static void deleteAtExit(final Path path, final boolean deleteParent) {
- Runtime.getRuntime().addShutdownHook(new Thread() {
- @Override
- public void run() {
- if (!runShutdownHooks.get()) {
- return;
- }
-
- try {
- path.delete();
- if (deleteParent) {
- path.getParentDirectory().delete();
- }
- } catch (IOException e) {
- printStack(e);
- }
- }
- });
- }
-
- static void printStack(IOException e) {
- /*
- * Hopefully this never happens. It's not very nice to just write this
- * to the user's console, but I'm not sure what better choice we have.
- */
- StringWriter err = new StringWriter();
- PrintWriter printErr = new PrintWriter(err);
- printErr.println("=======[BLAZE SERVER: ENCOUNTERED IO EXCEPTION]=======");
- e.printStackTrace(printErr);
- printErr.println("=====================================================");
- LOG.severe(err.toString());
- }
-
/**
* Start serving and block until the a shutdown command is received.
*/
- public abstract void serve() throws IOException;
+ void serve() throws IOException;
/**
* Called when the server receives a SIGINT.
*/
- public abstract void interrupt();
+ void interrupt();
}