-
Notifications
You must be signed in to change notification settings - Fork 6
Description
To trigger, we want to evaluate test in ICE, interrupt it with enter then retry evaluating it.
test :: Boolean;
test = isEven (makeNat 100000000) True;
isEven :: Nat -> Boolean -> Boolean;
isEven !x !accum =
case x of
Zero -> accum;
OnePlus x ->
isEven x (not accum);
;
makeNat :: Int -> Nat;
makeNat !x =
if x > 0 then
OnePlus (makeNat (x-1))
else
Zero;
data Nat
= Zero
| OnePlus x :: Nat // Must be lazy to prevent stack overflow in makeNat().
;
The key to having a reproducible test case is to generate code that will call a long running compuation so that RTValue.lastRef() is called somewhere. In this case the call is made in Is_Even.RTAppS.setResult(). I think a similar thing can happen if a supercombinator gets called via f() and not f_S() or f_L().
Here is the stack trace:
java.lang.NullPointerException
at org.openquark.cal_Cal_Test_Core_PlingTest.Is_Even.f2S(Is_Even.java)
at org.openquark.cal_Cal_Test_Core_PlingTest.Is_Even$RTAppS.reduce(Is_Even.java)
at org.openquark.cal.internal.runtime.lecc.RTResultFunction.unsynchronizedEvaluate(RTResultFunction.java:136)
at org.openquark.cal.internal.runtime.lecc.RTResultFunction.evaluate(RTResultFunction.java:103)
at org.openquark.cal_Cal_Test_Core_PlingTest.Io__iceruntarget__0__0.f(Io__iceruntarget__0__0.java)
at org.openquark.cal.internal.runtime.lecc.RTFullApp$General$_0.reduce(RTFullApp.java:225)
at org.openquark.cal.internal.runtime.lecc.RTResultFunction.unsynchronizedEvaluate(RTResultFunction.java:136)
at org.openquark.cal.internal.runtime.lecc.RTResultFunction.evaluate(RTResultFunction.java:103)
at org.openquark.cal_Cal_Core_Prelude.Id.f(Id.java)
at org.openquark.cal.internal.runtime.lecc.RTApplication.reduce(RTApplication.java:156)
at org.openquark.cal.internal.runtime.lecc.RTResultFunction.unsynchronizedEvaluate(RTResultFunction.java:136)
at org.openquark.cal.internal.runtime.lecc.RTResultFunction.evaluate(RTResultFunction.java:103)
at org.openquark.cal.internal.machine.lecc.Executor.exec(Executor.java:173)
at org.openquark.cal.ICE$FunctionRunThread.run(ICE.java:7234)
at java.lang.Thread.run(Thread.java:679)
Here are my notes on what Rich had to say prior to having a reproducible test case:
+ Interaction of how graph is reduced and cancellation function takes a couple args.
+ Suppose we have a two argument recursive function:
+ function is represented as a class with 3 fields : arg1, arg2, result
+ if result != NULL, then we have evaluated the function and that is the result.
+ Function initially not evaluated, when time to evaluate (simple approach):
+ ask function to evaluate
+ apply application
+ replace node w/ full application
+ Problem: the fact that we're holding onto the two arguments casuses extra memory usage
since they are never going to be needed. Everything needed to compute the result is passed to
the recusive function.
+ Basically, the function object is holding onto stale objects. Kindof like holding onto the
beginning of the list.
+ Optimized approach:
+ arg1, arg2 := NULL
+ proceed with evaluation of recursive case (somehow storing the values stored by the two args if
required by recursive call).
+ Problem with optimized approach:
+ notice cancel flag > throw exception > unwind stack > leave object with 2 null arguments.
The simplest solution is to keep track of in progress CAFs for each context and remove the corresponding entry for the context in the CAF's RTCAF.$instancesMap. However, this will need support from RTFullApp.General._0 (or more likely a special implementation for CAFs).