NonLocalReturnControl without stacktrace in play application

345 views
Skip to first unread message

Matan Safriel

unread,
Mar 14, 2015, 3:43:12 PM3/14/15
to scala...@googlegroups.com
Hi,

I have caused a NonLocalReturnControl runtime error by explicitly, superfluously, and unintentionally returning a value within this function

  private def foo(data: DataObject): Future[ReadyData] = {
   
implicit val context = play.api.libs.concurrent.Execution.Implicits.defaultContext
    data
.Get flatMap { _ match {
       
case Ready(dataID) => {  
         
return Future { ExecutedData(data, data.access) } // culprit line
       
}
       
case NotReady(_) => {
          create
(data)
       
}
     
}
   
}
 
}

I have read all over about this type of runtime error, but am still in need of an authoritative explanation about it; is it specific to futures mapping constructs, or is it something more general concerning the nesting of closures? my guess would be it's nonsensical to return from within a future's map block, but something about the type of the error tells me it's more general than that.

In addition it is mentioned in http://docs.scala-lang.org/sips/completed/futures-promises.html in a vague way as receiving some special treatment, the specialty of which I could not infer there, which connects nicely to the following greater awkwardness I encountered over it: there was no stack trace to be found in the play log file (nor on the console). Only:

2015-03-14 16:48:51,484 - [ERROR] - from akka.actor.ActorSystemImpl in play-akka.actor.default-dispatcher-4
Uncaught error from thread [play-akka.actor.default-dispatcher-3]
scala
.runtime.NonLocalReturnControl: null

Usually, play isn't shy in spilling out the full stack traces....

I attached a debugger to home in on the location of the error. There was definitely a stack at the time/location where the debugger hit a NonLocalReturnControl, but this didn't make it to the log/console. I found that a bit odd, so I am wondering what have I been missing for getting to the stack trace without a joyful remote debugger session.

Thanks in advance!
Matan



Viktor Klang

unread,
Mar 14, 2015, 5:32:42 PM3/14/15
to Matan Safriel, scala-user
Hi Matan,

See http://www.scala-lang.org/files/archive/api/2.11.5/#scala.util.control.ControlThrowable@inheritance-diagram
NonLocalReturnControl extends NoStackTrace,
disable the suppression of the stack trace using this system property: https://github.com/scala/scala/blob/v2.11.5/src/library/scala/sys/SystemProperties.scala#L82

That aside, my recommendation is to never use "return", and the code can be rewritten as:

  private def foo(data: DataObject)(implicit ec: ExecutionContext): Future[ReadyData] =
    data
.Get flatMap {
     
case Ready(dataID) => Future.successful(ExecutedData(data, data.access))
     
case NotReady(_)   => create(data)
   
}

--
You received this message because you are subscribed to the Google Groups "scala-user" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-user+...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.



--
Cheers,

Matan Safriel

unread,
Mar 14, 2015, 5:41:31 PM3/14/15
to Viktor Klang, scala-user
Thanks! missed that nuance in the source....
I guess there's a good reason for having the stack trace disabled by default, it is probably meant to avoid worse happenings of a sort. What might those be?

Viktor Klang

unread,
Mar 14, 2015, 6:07:29 PM3/14/15
to Matan Safriel, scala-user
On Sat, Mar 14, 2015 at 10:41 PM, Matan Safriel <dev....@gmail.com> wrote:
Thanks! missed that nuance in the source....
I guess there's a good reason for having the stack trace disabled by default, it is probably meant to avoid worse happenings of a sort. What might those be?

The construction of stack traces is resource heavy and since NLRC is used to transport return values across the stack, the overhead needs to be minimal.



--
Cheers,

Matan Safriel

unread,
Mar 14, 2015, 6:24:44 PM3/14/15
to Viktor Klang, scala-user
Thanks Viktor,

What might be the rationale then, of coupling NLRC to both return values transport and the case of using a return statement inside a future map? maybe this would call to explain what non-local means here...

I would dread having to attach a debugger in production, to find out what the stack may be, which is why I started this discussion (imagine one may not even know where to start looking in a debug session). Of course good and tested code wouldn't likely give way to such a surprise in production, but I would like to think the platform would tell you where an exception has been generated at all times.

Thanks in advance for future comment,
Matan



Viktor Klang

unread,
Mar 14, 2015, 6:36:55 PM3/14/15
to Matan Safriel, scala-user
On Sat, Mar 14, 2015 at 11:24 PM, Matan Safriel <dev....@gmail.com> wrote:
Thanks Viktor,

What might be the rationale then, of coupling NLRC to both return values transport and the case of using a return statement inside a future map? maybe this would call to explain what non-local means here...

 

I would dread having to attach a debugger in production, to find out what the stack may be, which is why I started this discussion (imagine one may not even know where to start looking in a debug session).

It's simple: Don't use return :)
 
Of course good and tested code wouldn't likely give way to such a surprise in production, but I would like to think the platform would tell you where an exception has been generated at all times.

But it's not an exception in the traditional sense, it's just using exceptions to transport values across stack frames.
 

Thanks in advance for future comment,
Matan






--
Cheers,
Reply all
Reply to author
Forward
0 new messages