manu
manu

Reputation: 243

Why does Future.firstCompletedOf not invoke callback on timeout?

I am doing Exercises from Learning Concurrent Programming in Scala.

For an exercise question in code comment.

  1. Program prints proper output of HTML contents for proper URL and timeout sufficiently enough.
  2. Program prints "Error occured" for proper URL and low timeout.

However for invalid URL "Error occured" is not printed. What is the problem with the code below?

/*
 * Implement a command-line program that asks the user to input a URL of some website, 
 * and displays the HTML of that website. Between the time that the user hits ENTER and 
 * the time that the HTML is retrieved, the program should repetitively print a . to the 
 * standard output every 50 milliseconds, with a two seconds timeout. Use only futures 
 * and promises, and avoid the synchronization primitives from the previous chapters. 
 * You may reuse the timeout method defined in this chapter.
 */
object Excersices extends App {

  val timer = new Timer()

  def timeout(t: Long = 1000): Future[Unit] = {
    val p = Promise[Unit]
    val timer = new Timer(true)
    timer.schedule(new TimerTask() {
      override def run() = {
        p success ()
        timer cancel()
      }
    }, t)
    p future
  }

  def printDot = println(".")

  val taskOfPrintingDot = new TimerTask {
    override def run() = printDot
  }

  println("Enter a URL")
  val lines = io.Source.stdin.getLines()
  val url = if (lines hasNext) Some(lines next) else None

  timer.schedule(taskOfPrintingDot, 0L, 50.millisecond.toMillis)
  val timeOut2Sec = timeout(2.second.toMillis)

  val htmlContents = Future {
    url map { x =>
      blocking {
        Source fromURL (x) mkString
      }
    }
  }

  Future.firstCompletedOf(Seq(timeOut2Sec, htmlContents)) map { x =>
    timer cancel ()
    x match {
      case Some(x) =>
        println(x)
      case _ =>
        println("Error occured")
    }

  }

  Thread sleep 5000
}

Upvotes: 2

Views: 519

Answers (2)

dk14
dk14

Reputation: 22374

As @Gábor Bakos said exception produces Failure which doesn't handled by map:

val fut = Future { Some(Source fromURL ("hhhttp://google.com")) }

scala> fut  map { x => println(x) } //nothing printed
res12: scala.concurrent.Future[Unit] = scala.concurrent.impl.Promise$DefaultPromise@5e025724

To process failure - use recover method :

scala> fut recover { case failure => None } map { x => println(x) } 
None
res13: scala.concurrent.Future[Unit] = scala.concurrent.impl.Promise$DefaultPromise@578afc83

In your context it's something like:

Future.firstCompletedOf(Seq(timeOut2Sec, htmlContents)) recover {case x => println("Error:" + x); None} map { x => ...}

Upvotes: 1

manu
manu

Reputation: 243

The Complete Code after using recover as advised by @dk14:

object Exercises extends App {

  val timer = new Timer()
  def timeout(t: Long = 1000): Future[Unit] = {
    val p = Promise[Unit]
    val timer = new Timer(true)
    timer.schedule(new TimerTask() {
      override def run() = {
        p success ()
        timer cancel ()
      }
    }, t)
    p future
  }

  def printDot = println(".")

  val taskOfPrintingDot = new TimerTask {
    override def run() = {
      printDot
    }
  }

  println("Enter a URL")
  val lines = io.Source.stdin.getLines()
  val url = if (lines hasNext) Some(lines next) else None

  timer.schedule(taskOfPrintingDot, 0L, 50.millisecond.toMillis)
  val timeOut2Sec = timeout(2.second.toMillis)

  val htmlContents = Future {
    url map { x =>
      blocking {
        Source fromURL (x) mkString
      }
    }
  }

  Future.firstCompletedOf(Seq(timeOut2Sec, htmlContents))
    .recover { case x => println("Error:" + x); None }
    .map { x =>
      timer cancel ()
      x match {
        case Some(x) =>
          println(x)
        case _ =>
          println("Timeout occurred")
      }
    }

  Thread sleep 5000
}

Upvotes: 0

Related Questions