// 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 SocketAddress 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; }