diff options
author | Paul Phillips <paulp@improving.org> | 2011-03-18 18:23:14 +0000 |
---|---|---|
committer | Paul Phillips <paulp@improving.org> | 2011-03-18 18:23:14 +0000 |
commit | f3b970b28cf640904fe0d340f9eb7de37514cb67 (patch) | |
tree | b5e77b7b78ec8743c8e4e0131cc7af4eb5611f7c /src/compiler/scala/tools/util | |
parent | d5d7953ab4c31b4f58f51b530ca82603f9e59b94 (diff) | |
download | scala-f3b970b28cf640904fe0d340f9eb7de37514cb67.tar.gz scala-f3b970b28cf640904fe0d340f9eb7de37514cb67.tar.bz2 scala-f3b970b28cf640904fe0d340f9eb7de37514cb67.zip |
Accumulated work on fsc.
adds the following new options.
-ipv4 Use IPv4 rather than IPv6 for the server socket absolute-cp Make
-classpath elements absolute paths before sending to server max-idle
-Set idle timeout in minutes for fsc (use 0 for no timeout)
My question marks are what are the right defaults for the first two.
Former behavior is to absolutize the classpath always and never prefer
IPv4 sockets. I changed the default to not absolutize the classpath,
with the option if you need it; I left the system default in place for
the socket creation, but I have a feeling we should default to IPv4. My
only hesitation is that the only way to request an IPv4 socket from java
involves mutating a global system property. (Robustness FTW.) So for
now, you have to give -ipv4.
Closes #3626, #3785, #3788, #3789. Review by community.
Diffstat (limited to 'src/compiler/scala/tools/util')
-rw-r--r-- | src/compiler/scala/tools/util/SocketServer.scala | 133 |
1 files changed, 64 insertions, 69 deletions
diff --git a/src/compiler/scala/tools/util/SocketServer.scala b/src/compiler/scala/tools/util/SocketServer.scala index 0094333402..fba3d4ce48 100644 --- a/src/compiler/scala/tools/util/SocketServer.scala +++ b/src/compiler/scala/tools/util/SocketServer.scala @@ -6,100 +6,95 @@ ** |/ ** \* */ - package scala.tools.util -import java.io.{ PrintWriter, BufferedOutputStream, BufferedReader, InputStreamReader, IOException } -import java.net.{ Socket, ServerSocket, SocketException, SocketTimeoutException } +import java.net.{ ServerSocket, SocketException, SocketTimeoutException } +import java.io.{ PrintWriter, BufferedReader } +import scala.tools.nsc.io.Socket -object SocketServer { - val BufferSize = 10240 +trait CompileOutputCommon { + def verbose: Boolean - def bufferedReader(s: Socket) = new BufferedReader(new InputStreamReader(s.getInputStream())) - def bufferedOutput(s: Socket) = new BufferedOutputStream(s.getOutputStream, BufferSize) + def info(msg: String) = if (verbose) echo(msg) + def echo(msg: String) = Console println msg + def warn(msg: String) = System.err println msg + def fatal(msg: String) = { warn(msg) ; sys.exit(1) } } -import SocketServer._ -/** The abstract class <code>SocketServer</code> implements the server +/** The abstract class SocketServer implements the server * communication for the fast Scala compiler. * * @author Martin Odersky * @version 1.0 */ -abstract class SocketServer { - // After some number of idle minutes, politely exit. - // Should the port file disappear, and the clients - // therefore unable to contact this server instance, - // the process will just eventually terminate by itself. - def fscIdleMinutes = { - sys.props("scala.config.fsc.idle-minutes") match { - case null => 30 - case str => try str.toInt catch { case _: Exception => 30 } - } - } - def fscIdleMillis = fscIdleMinutes * 60 * 1000 - +abstract class SocketServer extends CompileOutputCommon { def shutdown: Boolean def session(): Unit + def timeout(): Unit = () // called after a timeout is detected for subclasses to cleanup + // a hook for subclasses + protected def createServerSocket(): ServerSocket = new ServerSocket(0) - var out: PrintWriter = _ var in: BufferedReader = _ - - def fatal(msg: String): Nothing = { - System.err.println(msg) - sys.exit(1) - } - - private def warn(msg: String) { - System.err.println(msg) + var out: PrintWriter = _ + val BufferSize = 10240 + lazy val serverSocket = createServerSocket() + lazy val port = serverSocket.getLocalPort() + + // Default to 30 minute idle timeout, settable with -max-idle + protected var idleMinutes = 30 + private var savedTimeout = 0 + private val acceptBox = new Socket.Box(() => { + // update the timeout if it has changed + if (savedTimeout != idleMinutes) { + savedTimeout = idleMinutes + setTimeoutOnSocket(savedTimeout) + } + new Socket(serverSocket.accept()) + }) + private def setTimeoutOnSocket(mins: Int) = { + try { + serverSocket setSoTimeout (mins * 60 * 1000) + info("Set socket timeout to " + mins + " minutes.") + true + } + catch { + case ex: SocketException => + warn("Failed to set socket timeout: " + ex) + false + } } - // called after a timeout is detected, - // for SocketServer subclasses to perform - // some cleanup, if any - def timeout() {} - - val serverSocket = - try new ServerSocket(0) - catch { case e: IOException => fatal("Could not listen on any port; exiting.") } - - val port: Int = serverSocket.getLocalPort() - - // @todo: this is going to be a prime candidate for ARM def doSession(clientSocket: Socket) = { - out = new PrintWriter(clientSocket.getOutputStream(), true) - in = bufferedReader(clientSocket) - val bufout = bufferedOutput(clientSocket) + clientSocket.applyReaderAndWriter { (in, out) => + this.in = in + this.out = out + val bufout = clientSocket.bufferedOutput(BufferSize) - try scala.Console.withOut(bufout)(session()) - finally { - bufout.close() - out.close() - in.close() + try scala.Console.withOut(bufout)(session()) + finally bufout.close() } } def run() { - def fail(s: String) = fatal(s format port) - Console.println("Setting timeout to " + fscIdleMillis) - try serverSocket setSoTimeout fscIdleMillis catch { - case e: SocketException => fatal("Could not set timeout on server socket; exiting.") - } - - try { - while (!shutdown) { - val clientSocket = try serverSocket.accept() catch { - case e: IOException => fail("Accept on port %d failed; exiting.") - } - try doSession(clientSocket) - finally clientSocket.close() + info("Starting SocketServer run() loop.") + + def loop() { + acceptBox.either match { + case Right(clientSocket) => + try doSession(clientSocket) + finally clientSocket.close() + case Left(_: SocketTimeoutException) => + warn("Idle timeout exceeded on port %d; exiting" format port) + timeout() + return + case _ => + warn("Accept on port %d failed") } + if (!shutdown) + loop() } - catch { - case e: SocketTimeoutException => - warn("Timeout elapsed with no requests from clients on port %d; exiting" format port) - timeout() - } + try loop() + catch { case ex: SocketException => fatal("Compile server caught fatal exception: " + ex) } finally serverSocket.close() } } |