aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/main/java/com/google/devtools/build
diff options
context:
space:
mode:
authorGravatar Lukacs Berki <lberki@google.com>2016-08-05 09:44:23 +0000
committerGravatar Yun Peng <pcloudy@google.com>2016-08-05 13:32:43 +0000
commitf107debac45ddf5859b1eb963379769b5815b18f (patch)
treece4306be43dc8ccd4ed5f4b618d1694a84b233a0 /src/main/java/com/google/devtools/build
parent9a9981e7c69276e61f998bc4c67525c1e45232f9 (diff)
Remove the AF_UNIX client/server communication protocol.
It has been superseded by gRPC. RELNOTES[INC]: Blaze doesn't support Unix domain sockets for communication between its client and server anymore. Therefore, the --command_port command line argument doesn't accept -1 as a valid value anymore. -- MOS_MIGRATED_REVID=129424092
Diffstat (limited to 'src/main/java/com/google/devtools/build')
-rw-r--r--src/main/java/com/google/devtools/build/lib/runtime/BlazeRuntime.java27
-rw-r--r--src/main/java/com/google/devtools/build/lib/server/AfUnixServer.java559
-rw-r--r--src/main/java/com/google/devtools/build/lib/server/GrpcServerImpl.java60
-rw-r--r--src/main/java/com/google/devtools/build/lib/server/RPCServer.java70
-rw-r--r--src/main/java/com/google/devtools/build/lib/unix/LocalClientSocket.java117
-rw-r--r--src/main/java/com/google/devtools/build/lib/unix/LocalServerSocket.java173
-rw-r--r--src/main/java/com/google/devtools/build/lib/unix/LocalSocket.java217
-rw-r--r--src/main/java/com/google/devtools/build/lib/unix/LocalSocketAddress.java56
-rw-r--r--src/main/java/com/google/devtools/build/lib/unix/LocalSocketImpl.java171
9 files changed, 77 insertions, 1373 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/runtime/BlazeRuntime.java b/src/main/java/com/google/devtools/build/lib/runtime/BlazeRuntime.java
index 2a22272b18..f896545fd2 100644
--- a/src/main/java/com/google/devtools/build/lib/runtime/BlazeRuntime.java
+++ b/src/main/java/com/google/devtools/build/lib/runtime/BlazeRuntime.java
@@ -49,7 +49,6 @@ import com.google.devtools.build.lib.query2.output.OutputFormatter;
import com.google.devtools.build.lib.rules.test.CoverageReportActionFactory;
import com.google.devtools.build.lib.runtime.BlazeCommandDispatcher.LockingMode;
import com.google.devtools.build.lib.runtime.proto.InvocationPolicyOuterClass.InvocationPolicy;
-import com.google.devtools.build.lib.server.AfUnixServer;
import com.google.devtools.build.lib.server.RPCServer;
import com.google.devtools.build.lib.server.signal.InterruptSignalHandler;
import com.google.devtools.build.lib.shell.JavaSubprocessFactory;
@@ -783,23 +782,17 @@ public final class BlazeRuntime {
CommandExecutor commandExecutor = new CommandExecutor(runtime, dispatcher);
- if (startupOptions.commandPort != -1) {
- try {
- // This is necessary so that Bazel kind of works during bootstrapping, at which time the
- // gRPC server is not compiled in so that we don't need gRPC for bootstrapping.
- Class<?> factoryClass = Class.forName(
- "com.google.devtools.build.lib.server.GrpcServerImpl$Factory");
- RPCServer.Factory factory = (RPCServer.Factory) factoryClass.newInstance();
- return factory.create(commandExecutor, runtime.getClock(),
- startupOptions.commandPort, runtime.getServerDirectory(),
- startupOptions.maxIdleSeconds);
- } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
- throw new AbruptExitException("gRPC server not compiled in", ExitCode.BLAZE_INTERNAL_ERROR);
- }
- } else {
- return AfUnixServer.newServerWith(runtime.getClock(), commandExecutor,
- runtime.getServerDirectory(), runtime.workspace.getWorkspace(),
+ try {
+ // This is necessary so that Bazel kind of works during bootstrapping, at which time the
+ // gRPC server is not compiled in so that we don't need gRPC for bootstrapping.
+ Class<?> factoryClass = Class.forName(
+ "com.google.devtools.build.lib.server.GrpcServerImpl$Factory");
+ RPCServer.Factory factory = (RPCServer.Factory) factoryClass.newInstance();
+ return factory.create(commandExecutor, runtime.getClock(),
+ startupOptions.commandPort, runtime.getServerDirectory(),
startupOptions.maxIdleSeconds);
+ } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
+ throw new AbruptExitException("gRPC server not compiled in", ExitCode.BLAZE_INTERNAL_ERROR);
}
}
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 8c5d316210..0000000000
--- a/src/main/java/com/google/devtools/build/lib/server/AfUnixServer.java
+++ /dev/null
@@ -1,559 +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 6d22550e8a..6e1555ca00 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
@@ -40,6 +40,8 @@ import io.grpc.netty.NettyServerBuilder;
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;
@@ -48,7 +50,10 @@ import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
+import java.util.logging.Logger;
+
import javax.annotation.concurrent.GuardedBy;
/**
@@ -57,7 +62,7 @@ import javax.annotation.concurrent.GuardedBy;
* <p>Only this class should depend on gRPC so that we only need to exclude this during
* bootstrapping.
*/
-public class GrpcServerImpl extends RPCServer {
+public class GrpcServerImpl implements RPCServer {
// UTF-8 won't do because we want to be able to pass arbitrary binary strings.
// Not that the internals of Bazel handle that correctly, but why not make at least this little
// part correct?
@@ -65,6 +70,8 @@ public class GrpcServerImpl extends RPCServer {
private static final long NANOSECONDS_IN_MS = TimeUnit.MILLISECONDS.toNanos(1);
+ private static final Logger LOG = Logger.getLogger(RPCServer.class.getName());
+
private class RunningCommand implements AutoCloseable {
private final Thread thread;
private final String id;
@@ -145,6 +152,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;
@@ -161,7 +170,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;
@@ -315,6 +331,46 @@ public class GrpcServerImpl extends RPCServer {
}
+ 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 final CommandServerGrpc.CommandServerImplBase commandServer =
new CommandServerGrpc.CommandServerImplBase() {
@Override
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 1e0f8f2b66..4880349e15 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
@@ -18,85 +18,33 @@ 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 Bazel server instance.
+ *
+ * <p>Even though it only has one implementation, that implementation cannot be compiled during
+ * bootstrapping Bazel because it depends on the gRPC Java stubs, so we add a layer of abstraction
+ * so that we can still use its functionality without resorting to reflection every time.
*/
-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();
}
diff --git a/src/main/java/com/google/devtools/build/lib/unix/LocalClientSocket.java b/src/main/java/com/google/devtools/build/lib/unix/LocalClientSocket.java
deleted file mode 100644
index 10335d1a93..0000000000
--- a/src/main/java/com/google/devtools/build/lib/unix/LocalClientSocket.java
+++ /dev/null
@@ -1,117 +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.unix;
-
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.SocketException;
-
-/**
- * <p>An implementation of client Socket for local (AF_UNIX) sockets.
- *
- * <p>This class intentionally doesn't extend java.net.Socket although it
- * has some similarity to it. The java.net class hierarchy is a terrible mess
- * and is inextricably coupled to the Internet Protocol.
- *
- * <p>This code is not intended to be portable to non-UNIX platforms.
- */
-public class LocalClientSocket extends LocalSocket {
-
- /**
- * Constructs an unconnected local client socket.
- *
- * @throws IOException if the socket could not be created.
- */
- public LocalClientSocket() throws IOException {
- super();
- }
-
- /**
- * Constructs a client socket and connects it to the specified address.
- *
- * @throws IOException if either of the socket/connect operations failed.
- */
- public LocalClientSocket(LocalSocketAddress address) throws IOException {
- super();
- connect(address);
- }
-
- /**
- * Connect to the specified server. Blocks until the server accepts the
- * connection.
- *
- * @throws IOException if the connection failed.
- */
- public synchronized void connect(LocalSocketAddress address)
- throws IOException {
- checkNotClosed();
- if (state == State.CONNECTED) {
- throw new SocketException("socket is already connected");
- }
- connect(fd, address.getName().toString()); // JNI
- this.address = address;
- this.state = State.CONNECTED;
- }
-
- /**
- * Returns the input stream for reading from the server.
- *
- * @param closeSocket close the socket when this input stream is closed.
- * @throws IOException if there was a problem.
- */
- public synchronized InputStream getInputStream(final boolean closeSocket) throws IOException {
- checkConnected();
- checkInputNotShutdown();
- return new FileInputStream(fd) {
- @Override
- public void close() throws IOException {
- if (closeSocket) {
- LocalClientSocket.this.close();
- }
- }
- };
- }
-
- /**
- * Returns the input stream for reading from the server.
- *
- * @throws IOException if there was a problem.
- */
- public synchronized InputStream getInputStream() throws IOException {
- return getInputStream(false);
- }
-
- /**
- * Returns the output stream for writing to the server.
- *
- * @throws IOException if there was a problem.
- */
- public synchronized OutputStream getOutputStream() throws IOException {
- checkConnected();
- checkOutputNotShutdown();
- return new FileOutputStream(fd) {
- @Override public void close() {
- // Don't close the file descriptor.
- }
- };
- }
-
- @Override
- public String toString() {
- return "LocalClientSocket(" + address + ")";
- }
-}
diff --git a/src/main/java/com/google/devtools/build/lib/unix/LocalServerSocket.java b/src/main/java/com/google/devtools/build/lib/unix/LocalServerSocket.java
deleted file mode 100644
index 0c0bd22c3f..0000000000
--- a/src/main/java/com/google/devtools/build/lib/unix/LocalServerSocket.java
+++ /dev/null
@@ -1,173 +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.unix;
-
-import java.io.FileDescriptor;
-import java.io.IOException;
-import java.io.InterruptedIOException;
-import java.net.Socket;
-import java.net.SocketException;
-import java.net.SocketTimeoutException;
-
-/**
- * <p>An implementation of ServerSocket for local (AF_UNIX) sockets.
- *
- * <p>This class intentionally doesn't extend java.net.ServerSocket although it
- * has some similarity to it. The java.net class hierarchy is a terrible mess
- * and is inextricably coupled to the Internet Protocol.
- *
- * <p>This code is not intended to be portable to non-UNIX platforms.
- */
-public class LocalServerSocket extends LocalSocket {
-
- // Socket timeout in milliseconds. No timeout by default.
- private long soTimeoutMillis = 0;
-
- /**
- * Constructs an unbound local server socket.
- */
- public LocalServerSocket() throws IOException {
- super();
- }
-
- /**
- * Constructs a server socket, binds it to the specified address, and
- * listens for incoming connections with the specified backlog.
- *
- * @throws IOException if any of the socket/bind/listen operations failed.
- */
- public LocalServerSocket(LocalSocketAddress address, int backlog)
- throws IOException {
- this();
- bind(address);
- listen(backlog);
- }
-
- /**
- * Constructs a server socket, binds it to the specified address, and begin
- * listening for incoming connections using the default backlog.
- *
- * @throws IOException if any of the socket/bind/listen operations failed.
- */
- public LocalServerSocket(LocalSocketAddress address) throws IOException {
- this(address, 50);
- }
-
- /**
- * Specifies the timeout in milliseconds for accept(). Setting it to
- * zero means an indefinite timeout.
- */
- public void setSoTimeout(long timeoutMillis) {
- soTimeoutMillis = timeoutMillis;
- }
-
- /**
- * Returns the current timeout in milliseconds.
- */
- public long getSoTimeout() {
- return soTimeoutMillis;
- }
-
- /**
- * Binds the specified address to this socket. The socket must be unbound.
- * This causes the filesystem entry to appear.
- *
- * @throws IOException if the bind failed.
- */
- public synchronized void bind(LocalSocketAddress address)
- throws IOException {
- if (address == null) {
- throw new NullPointerException("address");
- }
- checkNotClosed();
- if (state != State.NEW) {
- throw new SocketException("socket is already bound to an address");
- }
- bind(fd, address.getName().toString()); // JNI
- this.address = address;
- this.state = State.BOUND;
- }
-
- /**
- * Listen for incoming connections on a socket using the specfied backlog.
- * The socket must be bound but not already listening.
- *
- * @throws IOException if the listen failed.
- */
- public synchronized void listen(int backlog) throws IOException {
- if (backlog < 1) {
- throw new IllegalArgumentException("backlog=" + backlog);
- }
- checkNotClosed();
- if (address == null) {
- throw new SocketException("socket has no address bound");
- }
- if (state == State.LISTENING) {
- throw new SocketException("socket is already listening");
- }
- listen(fd, backlog); // JNI
- this.state = State.LISTENING;
- }
-
- /**
- * Blocks until a connection is made to this socket and accepts it, returning
- * a new socket connected to the client.
- *
- * @return the new socket connected to the client.
- * @throws IOException if an error occurs when waiting for a connection.
- * @throws SocketTimeoutException if a timeout was previously set with
- * setSoTimeout and the timeout has been reached.
- * @throws InterruptedIOException if the thread is interrupted when the
- * method is blocked.
- */
- public synchronized Socket accept()
- throws IOException, SocketTimeoutException, InterruptedIOException {
- if (state != State.LISTENING) {
- throw new SocketException("socket is not in listening state");
- }
-
- // Throws a SocketTimeoutException if timeout.
- if (soTimeoutMillis != 0) {
- poll(fd, soTimeoutMillis); // JNI
- }
-
- FileDescriptor clientFd = new FileDescriptor();
- accept(fd, clientFd); // JNI
- final LocalSocketImpl impl = new LocalSocketImpl(clientFd);
- return new Socket(impl) {
- @Override
- public boolean isConnected() {
- return true;
- }
- @Override
- public synchronized void close() throws IOException {
- if (isClosed()) {
- return;
- } else {
- super.close();
- // Workaround for the fact that super.created==false because we
- // created the impl ourselves. As a result, super.close() doesn't
- // call impl.close(). *Sigh*, java.net is horrendous.
- // (Perhaps we should dispense with Socket/SocketImpl altogether?)
- impl.close();
- }
- }
- };
- }
-
- @Override
- public String toString() {
- return "LocalServerSocket(" + address + ")";
- }
-}
diff --git a/src/main/java/com/google/devtools/build/lib/unix/LocalSocket.java b/src/main/java/com/google/devtools/build/lib/unix/LocalSocket.java
deleted file mode 100644
index eea83b610b..0000000000
--- a/src/main/java/com/google/devtools/build/lib/unix/LocalSocket.java
+++ /dev/null
@@ -1,217 +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.unix;
-
-import com.google.devtools.build.lib.UnixJniLoader;
-
-import java.io.Closeable;
-import java.io.FileDescriptor;
-import java.io.IOException;
-import java.io.InterruptedIOException;
-import java.net.SocketException;
-import java.net.SocketTimeoutException;
-
-/**
- * Abstract superclass for client and server local sockets.
- */
-abstract class LocalSocket implements Closeable {
-
- protected enum State {
- NEW,
- BOUND, // server only
- LISTENING, // server only
- CONNECTED, // client only
- CLOSED,
- }
-
- protected LocalSocketAddress address = null;
- protected FileDescriptor fd = new FileDescriptor();
- protected State state;
- protected boolean inputShutdown = false;
- protected boolean outputShutdown = false;
-
- /**
- * Constructs an unconnected local socket.
- */
- protected LocalSocket() throws IOException {
- socket(fd);
- if (!fd.valid()) {
- throw new IOException("Couldn't create socket!");
- }
- this.state = State.NEW;
- }
-
- /**
- * Returns the address of the endpoint this socket is bound to.
- *
- * @return a <code>SocketAddress</code> representing the local endpoint of
- * this socket.
- */
- public LocalSocketAddress getLocalSocketAddress() {
- return address;
- }
-
- /**
- * Closes this socket. This operation is idempotent.
- *
- * To be consistent with Java Socket, the shutdown states of the socket are
- * not changed. This makes it easier to port applications between Socket and
- * LocalSocket.
- *
- * @throws IOException if an I/O error occurred when closing the socket.
- */
- @Override
- public synchronized void close() throws IOException {
- if (state == State.CLOSED) {
- return;
- }
- // Closes the file descriptor if it has not been closed by the
- // input/output streams.
- if (!fd.valid()) {
- throw new IllegalStateException("LocalSocket.close(-1)");
- }
- close(fd);
- if (fd.valid()) {
- throw new IllegalStateException("LocalSocket.close() did not set fd to -1");
- }
- this.state = State.CLOSED;
- }
-
- /**
- * Returns the closed state of the ServerSocket.
- *
- * @return true if the socket has been closed
- */
- public synchronized boolean isClosed() {
- // If the file descriptor has been closed by the input/output
- // streams, marks the socket as closed too.
- return state == State.CLOSED;
- }
-
- /**
- * Returns the connected state of the ClientSocket.
- *
- * @return true if the socket is currently connected.
- */
- public synchronized boolean isConnected() {
- return state == State.CONNECTED;
- }
-
- protected synchronized void checkConnected() throws SocketException {
- if (!isConnected()) {
- throw new SocketException("Transport endpoint is not connected");
- }
- }
-
- protected synchronized void checkNotClosed() throws SocketException {
- if (isClosed()) {
- throw new SocketException("socket is closed");
- }
- }
-
- /**
- * Returns the shutdown state of the input channel.
- *
- * @return true is the input channel of the socket is shutdown.
- */
- public synchronized boolean isInputShutdown() {
- return inputShutdown;
- }
-
- /**
- * Returns the shutdown state of the output channel.
- *
- * @return true is the input channel of the socket is shutdown.
- */
- public synchronized boolean isOutputShutdown() {
- return outputShutdown;
- }
-
- protected synchronized void checkInputNotShutdown() throws SocketException {
- if (isInputShutdown()) {
- throw new SocketException("Socket input is shutdown");
- }
- }
-
- protected synchronized void checkOutputNotShutdown() throws SocketException {
- if (isOutputShutdown()) {
- throw new SocketException("Socket output is shutdown");
- }
- }
-
- static final int SHUT_RD = 0; // Mapped to BSD SHUT_RD in JNI.
- static final int SHUT_WR = 1; // Mapped to BSD SHUT_WR in JNI.
-
- public synchronized void shutdownInput() throws IOException {
- checkNotClosed();
- checkConnected();
- checkInputNotShutdown();
- inputShutdown = true;
- shutdown(fd, SHUT_RD);
- }
-
- public synchronized void shutdownOutput() throws IOException {
- checkNotClosed();
- checkConnected();
- checkOutputNotShutdown();
- outputShutdown = true;
- shutdown(fd, SHUT_WR);
- }
-
- ////////////////////////////////////////////////////////////////////////
- // JNI:
-
- static {
- UnixJniLoader.loadJni();
- }
-
- // The native calls below are thin wrappers around linux system calls. The
- // semantics remains the same except for poll(). See the comments for the
- // method.
- //
- // Note: FileDescriptor is a box for a mutable integer that is visible only
- // to native code.
-
- // Generic operations:
- protected static native void socket(FileDescriptor server)
- throws IOException;
- static native void close(FileDescriptor server)
- throws IOException;
- /**
- * Shut down part of a full-duplex connection
- * @param code Must be either SHUT_RD or SHUT_WR
- */
- static native void shutdown(FileDescriptor fd, int code)
- throws IOException;
-
- /**
- * This method checks waits for the given file descriptor to become available for read.
- * If timeoutMillis passed and there is no activity, a SocketTimeoutException will be thrown.
- */
- protected static native void poll(FileDescriptor read, long timeoutMillis)
- throws IOException, SocketTimeoutException, InterruptedIOException;
-
- // Server operations:
- protected static native void bind(FileDescriptor server, String filename)
- throws IOException;
- protected static native void listen(FileDescriptor server, int backlog)
- throws IOException;
- protected static native void accept(FileDescriptor server,
- FileDescriptor client)
- throws IOException;
-
- // Client operations:
- protected static native void connect(FileDescriptor client, String filename)
- throws IOException;
-}
diff --git a/src/main/java/com/google/devtools/build/lib/unix/LocalSocketAddress.java b/src/main/java/com/google/devtools/build/lib/unix/LocalSocketAddress.java
deleted file mode 100644
index f9b9d43f06..0000000000
--- a/src/main/java/com/google/devtools/build/lib/unix/LocalSocketAddress.java
+++ /dev/null
@@ -1,56 +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.unix;
-
-import java.io.File;
-import java.net.SocketAddress;
-
-/**
- * An implementation of SocketAddress for naming local sockets, i.e. files in
- * the UNIX file system.
- */
-public class LocalSocketAddress extends SocketAddress {
-
- private final File name;
-
- /**
- * Constructs a SocketAddress for the specified file.
- */
- public LocalSocketAddress(File name) {
- this.name = name;
- }
-
- /**
- * Returns the filename of this local socket address.
- */
- public File getName() {
- return name;
- }
-
- @Override
- public String toString() {
- return name.toString();
- }
-
- @Override
- public boolean equals(Object other) {
- return other instanceof LocalSocketAddress &&
- ((LocalSocketAddress) other).name.equals(this.name);
- }
-
- @Override
- public int hashCode() {
- return name.hashCode();
- }
-}
diff --git a/src/main/java/com/google/devtools/build/lib/unix/LocalSocketImpl.java b/src/main/java/com/google/devtools/build/lib/unix/LocalSocketImpl.java
deleted file mode 100644
index a30b450dc1..0000000000
--- a/src/main/java/com/google/devtools/build/lib/unix/LocalSocketImpl.java
+++ /dev/null
@@ -1,171 +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.unix;
-
-import com.google.devtools.build.lib.UnixJniLoader;
-import com.google.devtools.build.lib.util.OS;
-
-import java.io.Closeable;
-import java.io.FileDescriptor;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.InetAddress;
-import java.net.SocketAddress;
-import java.net.SocketImpl;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-/**
- * A simple implementation of SocketImpl for sockets that wrap a UNIX
- * file-descriptor. This SocketImpl assumes that the socket is already
- * created, bound, connected and supports no socket options or out-of-band
- * features. This is used to implement server-side accepted client sockets
- * (i.e. those returned by {@link LocalServerSocket#accept}).
- */
-class LocalSocketImpl extends SocketImpl {
- private static final Logger logger =
- Logger.getLogger(LocalSocketImpl.class.getName());
-
- static {
- UnixJniLoader.loadJni();
- if (OS.getCurrent() != OS.WINDOWS) {
- init();
- }
- }
-
- // The logic here is a little twisted, to support JDK7 and JDK8.
-
- // 1) In JDK7, the FileDescriptor class keeps a reference count of
- // instances using the fd, and closes it when it goes to 0. The
- // reference count is only decremented by the finalizer for a
- // given class. When the call to close() happens, the fd is
- // closed regardless of the current state of the refcount.
- //
- // 2) In JDK8, every instance that uses the fd registers a Closeable
- // with the FileDescriptor. Since the FileDescriptor has a
- // reference to every user, only when all of the users and the
- // FileDescriptor get GC'd does the finalizer run. An explicit
- // call to close() calls FileDescriptor.closeAll(), which
- // force-closes all of the users.
-
- // So, in our case:
-
- // 1) ref() increments the refcount in JDK7, and registers with the
- // FD in JDK8.
-
- // 2) unref() decrements the refcount in JDK7, and does nothing in
- // JDK8.
-
- // 3) The finalizer decrements the refcount in JDK7, and simply
- // calls close() in JDK8 (where we don't have to worry about
- // multiple live users of the FD). The close() method itself is
- // idempotent.
-
- // 4) close() calls fd.closeAll in JDK8, which, in turn, calls
- // closer.close(). In JDK7, close() calls closer.close()
- // explicitly.
- private static native void init();
- private static native void ref(FileDescriptor fd, Closeable closeable);
- private static native boolean unref(FileDescriptor fd);
- private static native boolean close0(FileDescriptor fd, Closeable closeable);
-
- private final boolean isInitialized;
- private final Closeable closer = new Closeable() {
- AtomicBoolean isClosed = new AtomicBoolean(false);
- @Override public void close() throws IOException {
- if (isClosed.compareAndSet(false, true)) {
- LocalSocket.close(fd);
- }
- }
- };
-
- // Note to callers: if you pass a FD into this constructor, this
- // instance is now responsible for closing it (in the sense of
- // LocalSocket.close()). If some other instance tries to close it,
- // then terrible things will happen.
- LocalSocketImpl(FileDescriptor fd) {
- this.fd = fd; // (inherited field)
- ref(fd, closer);
- isInitialized = true;
- }
-
- @Override protected void finalize() {
- try {
- if (isInitialized) {
- if (!unref(fd)) {
- // JDK8 codepath
- close0(fd, closer);
- }
- }
- } catch (Exception e) {
- logger.log(Level.WARNING, "Unable to access FileDescriptor class - " +
- "may cause a file descriptor leak", e);
- }
- }
- @Override protected InputStream getInputStream() {
- return new FileInputStream(getFileDescriptor());
- }
- @Override protected OutputStream getOutputStream() {
- return new FileOutputStream(getFileDescriptor());
- }
- @Override protected void close() throws IOException {
- if (fd.valid()) {
- if (!close0(fd, closer)) {
- // JDK7 codepath
- closer.close();
- }
- }
- }
-
- // Unused:
- @Override
- public void setOption(int optID, Object value) {
- throw new UnsupportedOperationException("setOption");
- }
- @Override
- public Object getOption(int optID) {
- throw new UnsupportedOperationException("getOption");
- }
- @Override protected void create(boolean stream) {
- throw new UnsupportedOperationException("create");
- }
- @Override protected void connect(String host, int port) {
- throw new UnsupportedOperationException("connect");
- }
- @Override protected void connect(InetAddress address, int port) {
- throw new UnsupportedOperationException("connect2");
- }
- @Override protected void connect(SocketAddress address, int timeout) {
- throw new UnsupportedOperationException("connect3");
- }
- @Override protected void bind(InetAddress host, int port) {
- throw new UnsupportedOperationException("bind");
- }
- @Override protected void listen(int backlog) {
- throw new UnsupportedOperationException("listen");
- }
- @Override protected void accept(SocketImpl s) {
- throw new UnsupportedOperationException("accept");
- }
- @Override protected int available() {
- throw new UnsupportedOperationException("available");
- }
- @Override protected void sendUrgentData(int i) {
- throw new UnsupportedOperationException("sendUrgentData");
- }
-}