Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
First (compiles but untested) draft of nqp::continuation* op implemen…
…tations
  • Loading branch information
sorear committed Jun 15, 2013
1 parent 9aeeadd commit bfee017
Show file tree
Hide file tree
Showing 4 changed files with 134 additions and 14 deletions.
10 changes: 9 additions & 1 deletion src/vm/jvm/runtime/org/perl6/nqp/runtime/CallFrame.java
Expand Up @@ -11,7 +11,7 @@
* relating to it. Call frames are created by the callee after arguments are
* passed in but before argument checking.
*/
public class CallFrame {
public class CallFrame implements Cloneable {
/**
* The thread context that created this call frame.
*/
Expand Down Expand Up @@ -192,4 +192,12 @@ public void leave() {
this.codeRef.staticInfo.priorInvocation = this;
this.tc.curFrame = this.caller;
}

CallFrame cloneContinuation() {
try {
return (CallFrame)clone();
} catch (CloneNotSupportedException e) {
return null;
}
}
}
112 changes: 104 additions & 8 deletions src/vm/jvm/runtime/org/perl6/nqp/runtime/Ops.java
@@ -1,5 +1,8 @@
package org.perl6.nqp.runtime;

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
Expand Down Expand Up @@ -1431,6 +1434,9 @@ public static void invokeMain(ThreadContext tc, SixModelObject invokee, String p
invokeDirect(tc, invokee, new CallSiteDescriptor(callsite, null), args);
}
public static void invokeDirect(ThreadContext tc, SixModelObject invokee, CallSiteDescriptor csd, Object[] args) {
invokeDirect(tc, invokee, csd, true, args);
}
public static void invokeDirect(ThreadContext tc, SixModelObject invokee, CallSiteDescriptor csd, boolean barrier, Object[] args) {
// Otherwise, get the code ref.
CodeRef cr;
if (invokee instanceof CodeRef) {
Expand All @@ -1451,6 +1457,8 @@ public static void invokeDirect(ThreadContext tc, SixModelObject invokee, CallSi
cr.staticInfo.mh.invokeExact(tc, cr, csd, args);
}
catch (ControlException e) {
if (barrier && (e instanceof SaveStackException))
ExceptionHandling.dieInternal(tc, "control operator crossed continuation barrier");
throw e;
}
catch (Throwable e) {
Expand Down Expand Up @@ -4142,16 +4150,104 @@ public static long usecompilerhllconfig(ThreadContext tc) {
return 1;
}

public static SixModelObject continuationclone(SixModelObject in, ThreadContext tc) {
throw new UnsupportedOperationException();
private static MethodHandle reset_reenter;
static {
try {
reset_reenter = MethodHandles.insertArguments(
MethodHandles.publicLookup().findStatic(Ops.class, "continuationreset",
MethodType.methodType(Void.TYPE, SixModelObject.class, SixModelObject.class, ThreadContext.class, ResumeStatus.Frame.class)),
0, null, null, null);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
// this is the most complicated one because it's not doing a tailcall, so we need to actually use the resumeframe
public static void continuationreset(SixModelObject key, SixModelObject run, ThreadContext tc, ResumeStatus.Frame resume) throws Throwable {
SixModelObject cont = null;

if (resume != null) {
// reload stuff here, then don't goto because java source doesn't have that
// XXX: compiler should be modified to take ThreadContext from continuation, NOT RELOAD RESUME..., wrap resumeNext in an appropriate handler
Object[] bits = resume.saveSpace;
key = (SixModelObject) bits[0];
tc = resume.tc;
}

while (true) {
try {
if (resume != null) {
resume.resumeNext();
} else if (cont != null) {
invokeDirect(tc, run, invocantCallSite, false, new Object[] { cont });
} else {
invokeDirect(tc, run, emptyCallSite, false, emptyArgList);
}
// If we get here, the reset argument or something placed using control returned normally
// so we should just return.
return;
} catch (SaveStackException sse) {
if (sse.key != null && sse.key != key) {
// This is intended for an outer scope, so just append ourself
throw sse.pushFrame(0, reset_reenter, new Object[] { key }, null);
}
// Ooo! This is ours!
resume = null;
STable contType = tc.gc.Continuation.st;
cont = contType.REPR.allocate(tc, contType);
((ResumeStatus)cont).top = sse.top;
run = sse.handler;
if (!sse.protect) break;
}
}
// now, if we get HERE, it means we saw an unprotected control operator
// so run it without protection

invokeDirect(tc, run, invocantCallSite, false, new Object[] { cont });
}
public static SixModelObject continuationreset(SixModelObject key, SixModelObject run, ThreadContext tc, ResumeStatus.Frame resume) {
throw new UnsupportedOperationException();

public static SixModelObject continuationclone(SixModelObject in, ThreadContext tc) {
if (!(in instanceof ResumeStatus))
ExceptionHandling.dieInternal(tc, "applied continuationinvoke to non-continuation");

ResumeStatus.Frame read = ((ResumeStatus)in).top;
ResumeStatus.Frame nroot = null, ntail = null, nnew;

while (read != null) {
CallFrame cf = read.callFrame == null ? null : read.callFrame.cloneContinuation();
nnew = new ResumeStatus.Frame(read.method, read.resumePoint, read.saveSpace, cf, null);
if (ntail != null) {
ntail.next = nnew;
} else {
nroot = nnew;
}
ntail = nnew;
}

STable contType = tc.gc.Continuation.st;
SixModelObject cont = contType.REPR.allocate(tc, contType);
((ResumeStatus)cont).top = nroot;
return cont;
}
public static SixModelObject continuationcontrol(long protect, SixModelObject key, SixModelObject run, ThreadContext tc, ResumeStatus.Frame resume) {
throw new UnsupportedOperationException();

public static void continuationcontrol(long protect, SixModelObject key, SixModelObject run, ThreadContext tc, ResumeStatus.Frame resume) {
throw new SaveStackException(key, protect != 0, run);
}
public static SixModelObject continuationinvoke(SixModelObject cont, SixModelObject arg, ThreadContext tc, ResumeStatus.Frame resume) {
throw new UnsupportedOperationException();

public static void continuationinvoke(SixModelObject cont, SixModelObject arg, ThreadContext tc, ResumeStatus.Frame resume) throws Throwable {
if (!(cont instanceof ResumeStatus))
ExceptionHandling.dieInternal(tc, "applied continuationinvoke to non-continuation");

ResumeStatus.Frame root = ((ResumeStatus)cont).top;

// fixups: safe to do more than once, but not concurrently
// these are why continuationclone is needed...
ResumeStatus.Frame csr = root;
while (csr != null) {
csr.tc = tc; // csr.callFrame.{csr,tc} will be set on resume
if (csr.next == null) csr.thunk = arg;
csr = csr.next;
}

root.resume();
}
}
13 changes: 11 additions & 2 deletions src/vm/jvm/runtime/org/perl6/nqp/runtime/ResumeStatus.java
Expand Up @@ -21,6 +21,11 @@ public static final class Frame {
public final CallFrame callFrame;
/** The next deeper frame. Don't modify after exposing to the world. */
public Frame next;
/** Thread context which the frame is being restored on. Only valid during restore. */
public transient ThreadContext tc;
/** Set during resume to a thunk which is called once the stack is restored. */
public transient SixModelObject thunk;


/** Constructor which sets all fields. */
public Frame(MethodHandle method, int resumePoint, Object[] saveSpace, CallFrame callFrame, Frame next) {
Expand All @@ -34,7 +39,7 @@ public Frame(MethodHandle method, int resumePoint, Object[] saveSpace, CallFrame
/** Restores a frame to the VM stack. */
public void resume() throws Throwable {
if (callFrame != null) {
ThreadContext tc = callFrame.tc;
callFrame.tc = tc;
callFrame.caller = tc.curFrame;
tc.curFrame = callFrame;
}
Expand All @@ -43,7 +48,11 @@ public void resume() throws Throwable {

/** Restores the next frame, if any. */
public void resumeNext() throws Throwable {
if (next != null) next.resume();
if (next != null) {
next.resume();
} else {
Ops.invokeDirect(tc, thunk, Ops.emptyCallSite, false, Ops.emptyArgList);
}
}
}

Expand Down
13 changes: 10 additions & 3 deletions src/vm/jvm/runtime/org/perl6/nqp/runtime/SaveStackException.java
@@ -1,6 +1,7 @@
package org.perl6.nqp.runtime;

import java.lang.invoke.MethodHandle;
import org.perl6.nqp.sixmodel.SixModelObject;

/** Thrown by dcshift operations to cause all currently executing frames to save their state. */

Expand All @@ -9,10 +10,16 @@ public class SaveStackException extends ControlException {
/** Topmost frame saved so far. */
public ResumeStatus.Frame top;
/** Tag identifying a specific instance of reset. */
public int tag; /* make a sixmodelobject? */
public SixModelObject key;
/** If true, the reset should reinstate itself while running the handler. */
public boolean protect;
/** Handler function passed to control. */
public SixModelObject handler;

public SaveStackException(int tag) {
this.tag = tag;
public SaveStackException(SixModelObject key, boolean protect, SixModelObject handler) {
this.key = key;
this.protect = protect;
this.handler = handler;
}

public SaveStackException pushFrame(int resumePoint, MethodHandle method, Object[] saveSpace, CallFrame callFrame) {
Expand Down

0 comments on commit bfee017

Please sign in to comment.