summaryrefslogtreecommitdiff
path: root/src/compiler/scala/tools/util
diff options
context:
space:
mode:
authorPaul Phillips <paulp@improving.org>2011-03-18 18:23:14 +0000
committerPaul Phillips <paulp@improving.org>2011-03-18 18:23:14 +0000
commitf3b970b28cf640904fe0d340f9eb7de37514cb67 (patch)
treeb5e77b7b78ec8743c8e4e0131cc7af4eb5611f7c /src/compiler/scala/tools/util
parentd5d7953ab4c31b4f58f51b530ca82603f9e59b94 (diff)
downloadscala-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.scala133
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()
}
}