blob: 40ccdeb91539eeb350740cd7ccf684d7a9446064 [file] [log] [blame]
// 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.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.
*/
public abstract class RPCServer {
private static final Logger LOG = Logger.getLogger(RPCServer.class.getName());
private static AtomicBoolean runShutdownHooks = new AtomicBoolean(true);
/**
* 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 {
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;
/**
* Called when the server receives a SIGINT.
*/
public abstract void interrupt();
}