2

In this question it is said, that Scala future is represented as daemonic thread, so it shouldn't prevent the application to stop if the main thread is done.

However, if I run the following code:

object main extends App {
  println("before future\n*************")
  val responseFuture = WS.url("http://www.google.com").withRequestTimeout(5 * 1000).get
  responseFuture.onComplete[Unit](
    _ match {
      case Success(resp) => println(resp.body)
      case Failure(ex) => println(ex.getMessage)
    }
  )
  println(Await.result(responseFuture, Duration.Inf))
  println("after future\n*************")
}

I got following output, which is correct.

before future
*************
Response(com.ning.http.client.providers.netty.NettyResponse@9913ac)
after future
*************
<!doctype html><html> ... </html>

But the program keeps running and I have to send a SIGINT to to terminate it. Why is the program not terminating? Do I have to close some connection explicitly? I guess, when using it directly from a Play project there is some automagic under the covers, but not in the normal Scala project.

EDIT: jconsole says:

Name: main
State: WAITING on java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject@14d0af4

Stack trace: 
sun.misc.Unsafe.park(Native Method)
java.util.concurrent.locks.LockSupport.park(LockSupport.java:186)
java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2043)
java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442)
java.util.concurrent.ExecutorCompletionService.take(ExecutorCompletionService.java:193)
sbt.ConcurrentRestrictions$$anon$4.take(ConcurrentRestrictions.scala:196)
...

Env

Scala version: 2.10.2

Java version: 1.7.0_09-b05 (HotSpot)

Play version: "play_2.10" % "2.2.1"

sbt version: 0.12.4

Community
  • 1
  • 1
Jiri Kremser
  • 12,471
  • 7
  • 45
  • 72

2 Answers2

3

The first time you use the WS API, exactly one Ning AsyncHttpClient is created to actually do all the work for you.

You can see it all in the WS source code here.

This client stays "alive" for the lifetime of the WS object, and I suspect it is that object that is grabbing the lock that prevents the main thread from finishing.

If you simply add:

WS.client.close()

at the end of your code snippet, the AsyncHttpClient terminates gracefully and the main thread exits.

millhouse
  • 9,817
  • 4
  • 32
  • 40
  • Thanks, that worked. Actually, I had to do this: `Await.result(responseFuture, 6 seconds)` and then `WS.client.close`. If I don't wait for the future and call it immediately, it doesn't work. It's pretty bad that I had to block the control flow. I was trying also this: `responseFuture onComplete {case _ => WS.client.close}` to my knowledge, it should work, but it doesn't :/ – Jiri Kremser Dec 18 '13 at 17:52
  • Because you're calling WS outside of Play, WS doesn't know when to shut itself down. The underlying cause is the internal Netty thread pool used by AsyncHttpClient. – Will Sargent May 26 '14 at 23:53
1

You could also close the client by adding an onComplete callback. This way you won't have to Await for the future. FYI, Awaiting is bad!

responseFuture.onComplete {
 _ match {
  case _ => WS.client.close
 }
}