1

I'm using sPdf's run method to render HTML as a PDF file.

run uses scala.sys.process.ProcessBuilder and it's ! method:

/** Starts the process represented by this builder, blocks until it exits, and
    * returns the exit code.  Standard output and error are sent to the console.
    */
  def ! : Int

My controller is using a Future to asynchronously execute the conversion but won't spdf block all other interim executions?

 Future { pdf.run(sourceUrl, outputStream) } map { exitCode =>
     outputSteam.toByteArray
 }

UPDATE

Thanks for your answer Paul did a little test and yeah looks to be that way :)

If I update sPdf's run like so:

  def run[A, B](sourceDocument: A, destinationDocument: B)(implicit sourceDocumentLike: SourceDocumentLike[A], destinationDocumentLike: DestinationDocumentLike[B]): Int = {
      println("start/ " + System.currentTimeMillis)
      < ... code removed ... >
      val result = (sink compose source)(process).!
      println("finish/ " + System.currentTimeMillis)
      result
  }

and I execute three consecutive requests the stdout prints

start 1461288779013
start 1461288779014
start 1461288779014
finish 1461288781018
finish 1461288781020
finish 1461288781020

Which looks like asynchronous execution.

Community
  • 1
  • 1
tgk
  • 3,857
  • 2
  • 27
  • 42
  • 1
    `ProcessBuilder` blocks the execution thread, not any other system thread (there's no locking happening) - but the real answer depends on how `spdf` works. If it lets multiple processes start up at once, then no blocking ought to happen. – jkinkead Apr 21 '16 at 22:40

1 Answers1

1

This is Pdf#run:

  def run[A, B](sourceDocument: A, destinationDocument: B)(implicit sourceDocumentLike: SourceDocumentLike[A], destinationDocumentLike: DestinationDocumentLike[B]): Int = {
    val commandLine = toCommandLine(sourceDocument, destinationDocument)
    val process = Process(commandLine)
    def source = sourceDocumentLike.sourceFrom(sourceDocument) _
    def sink = destinationDocumentLike.sinkTo(destinationDocument) _

    (sink compose source)(process).!
  }

There's no synchronization that prevents parallel execution. Assuming that the ExecutionContext has enough available threads,

Future { pdf.run(sourceUrl, outputStream) } map { exitCode =>
    outputSteam.toByteArray
}

Future { pdf.run(sourceUrl, outputStream) } map { exitCode =>
    outputSteam.toByteArray
}

will execute in parallel.


If instead, the run method had been, say, surrounded by a synchronized block, only one invocation would execute per Pdf instance. But there's no reason to prevent concurrency here, so the author didn't.

Paul Draper
  • 78,542
  • 46
  • 206
  • 285