From 91e4d2b4a1123c5ed7ccee85b18df3706c9b40fd Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Sat, 14 Dec 2013 20:06:51 -0700 Subject: [PATCH 1/9] quick sketch of composable continuation implementation I've been told by knowledgeable people that it is impossible to implement composable continuations (AKA delimited continuations AKA shift/reset) in terms of call-with-current-continuation. Since I don't yet understand why that is, I figured it would help my understanding to attempt it and see how it fails. --- classpath/avian/Continuations.java | 81 +++++++++++++++++++++++++ classpath/avian/Function.java | 15 +++++ classpath/avian/FunctionReceiver.java | 15 +++++ makefile | 1 + test/extra/ComposableContinuations.java | 43 +++++++++++++ 5 files changed, 155 insertions(+) create mode 100644 classpath/avian/Function.java create mode 100644 classpath/avian/FunctionReceiver.java create mode 100644 test/extra/ComposableContinuations.java diff --git a/classpath/avian/Continuations.java b/classpath/avian/Continuations.java index 631bd48a72..476be807f7 100644 --- a/classpath/avian/Continuations.java +++ b/classpath/avian/Continuations.java @@ -120,6 +120,8 @@ import java.util.concurrent.Callable; public class Continuations { private Continuations() { } + private static final ThreadLocal latestReset = new ThreadLocal(); + /** * Captures the current continuation, passing a reference to the * specified receiver. @@ -181,6 +183,76 @@ public class Continuations { } } + public static T reset(final Callable thunk) throws Exception { + final Reset reset = new Reset(latestReset.get()); + latestReset.set(reset); + try { + T result = callWithCurrentContinuation + (new CallbackReceiver() { + public T receive(Callback continuation) throws Exception { + reset.continuation = continuation; + return thunk.call(); + } + }); + + if (reset.continuation != null) { + reset.continuation.handleResult(result); + } + + return result; + } finally { + latestReset.set(reset.next); + } + } + + public static Result shift + (final FunctionReceiver receiver) + throws Exception + { + return (Result) callWithCurrentContinuation(new CallbackReceiver() { + public Object receive(final Callback continuation) { + final Reset reset = latestReset.get(); + final Callback resetContinuation = reset.continuation; + reset.continuation = new Callback() { + public void handleResult(Object ignored) { + try { + resetContinuation.handleResult + (receiver.receive + (new Function() { + public Object call(final Object argument) + throws Exception { + Object result = callWithCurrentContinuation + (new CallbackReceiver() { + public Object receive + (Callback shiftContinuation) + throws Exception + { + reset.continuation = shiftContinuation; + continuation.handleResult(argument); + throw new AssertionError(); + } + }); + + reset.continuation = null; + return result; + } + })); + } catch (Exception e) { + resetContinuation.handleException(e); + } + } + + public void handleException(Throwable exception) { + throw new AssertionError(); + } + }; + + resetContinuation.handleResult(null); + throw new AssertionError(); + } + }); + } + private static native UnwindResult dynamicWind2(Runnable before, Callable thunk, Runnable after) @@ -235,4 +307,13 @@ public class Continuations { this.exception = exception; } } + + private static class Reset { + public Callback continuation; + public final Reset next; + + public Reset(Reset next) { + this.next = next; + } + } } diff --git a/classpath/avian/Function.java b/classpath/avian/Function.java new file mode 100644 index 0000000000..61e77fdcc5 --- /dev/null +++ b/classpath/avian/Function.java @@ -0,0 +1,15 @@ +/* Copyright (c) 2008-2013, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package avian; + +public interface Function { + public Result call(Argument argument) throws Exception; +} diff --git a/classpath/avian/FunctionReceiver.java b/classpath/avian/FunctionReceiver.java new file mode 100644 index 0000000000..d58c36e7a4 --- /dev/null +++ b/classpath/avian/FunctionReceiver.java @@ -0,0 +1,15 @@ +/* Copyright (c) 2008-2013, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package avian; + +public interface FunctionReceiver { + public Result receive(Function function) throws Exception; +} diff --git a/makefile b/makefile index fce13ac081..66f6baab72 100755 --- a/makefile +++ b/makefile @@ -1397,6 +1397,7 @@ unittest-depends = \ ifeq ($(continuations),true) continuation-tests = \ + extra.ComposableContinuations \ extra.Continuations \ extra.Coroutines \ extra.DynamicWind diff --git a/test/extra/ComposableContinuations.java b/test/extra/ComposableContinuations.java new file mode 100644 index 0000000000..ad964bcfdf --- /dev/null +++ b/test/extra/ComposableContinuations.java @@ -0,0 +1,43 @@ +package extra; + +import static avian.Continuations.shift; +import static avian.Continuations.reset; + +import avian.FunctionReceiver; +import avian.Function; + +import java.util.concurrent.Callable; + +public class ComposableContinuations { + private static void expect(boolean v) { + if (! v) throw new RuntimeException(); + } + + public static void main(String[] args) throws Exception { + expect(2 * reset(new Callable() { + public Integer call() throws Exception { + return 1 + shift(new FunctionReceiver() { + public Integer receive + (Function continuation) + throws Exception + { + return continuation.call(5); + } + }); + } + }) == 12); + + expect(1 + reset(new Callable() { + public Integer call() throws Exception { + return 2 * shift(new FunctionReceiver() { + public Integer receive + (Function continuation) + throws Exception + { + return continuation.call(continuation.call(4)); + } + }); + } + }) == 17); + } +} From aa3fa1aff4201f4338aac6ccd718c4a7cff1b0ea Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Sun, 15 Dec 2013 09:52:38 -0700 Subject: [PATCH 2/9] simplify shift/reset API and add test (currently failing) --- classpath/avian/Continuations.java | 6 +- classpath/avian/Function.java | 4 +- classpath/avian/FunctionReceiver.java | 4 +- test/extra/ComposableContinuations.java | 79 +++++++++++++++++++++++-- 4 files changed, 80 insertions(+), 13 deletions(-) diff --git a/classpath/avian/Continuations.java b/classpath/avian/Continuations.java index 476be807f7..814a69b012 100644 --- a/classpath/avian/Continuations.java +++ b/classpath/avian/Continuations.java @@ -205,11 +205,11 @@ public class Continuations { } } - public static Result shift - (final FunctionReceiver receiver) + public static T shift + (final FunctionReceiver receiver) throws Exception { - return (Result) callWithCurrentContinuation(new CallbackReceiver() { + return (T) callWithCurrentContinuation(new CallbackReceiver() { public Object receive(final Callback continuation) { final Reset reset = latestReset.get(); final Callback resetContinuation = reset.continuation; diff --git a/classpath/avian/Function.java b/classpath/avian/Function.java index 61e77fdcc5..633a51dfdb 100644 --- a/classpath/avian/Function.java +++ b/classpath/avian/Function.java @@ -10,6 +10,6 @@ package avian; -public interface Function { - public Result call(Argument argument) throws Exception; +public interface Function { + public T call(T argument) throws Exception; } diff --git a/classpath/avian/FunctionReceiver.java b/classpath/avian/FunctionReceiver.java index d58c36e7a4..d526b39240 100644 --- a/classpath/avian/FunctionReceiver.java +++ b/classpath/avian/FunctionReceiver.java @@ -10,6 +10,6 @@ package avian; -public interface FunctionReceiver { - public Result receive(Function function) throws Exception; +public interface FunctionReceiver { + public T receive(Function function) throws Exception; } diff --git a/test/extra/ComposableContinuations.java b/test/extra/ComposableContinuations.java index ad964bcfdf..b652533960 100644 --- a/test/extra/ComposableContinuations.java +++ b/test/extra/ComposableContinuations.java @@ -16,9 +16,8 @@ public class ComposableContinuations { public static void main(String[] args) throws Exception { expect(2 * reset(new Callable() { public Integer call() throws Exception { - return 1 + shift(new FunctionReceiver() { - public Integer receive - (Function continuation) + return 1 + shift(new FunctionReceiver() { + public Integer receive(Function continuation) throws Exception { return continuation.call(5); @@ -29,9 +28,8 @@ public class ComposableContinuations { expect(1 + reset(new Callable() { public Integer call() throws Exception { - return 2 * shift(new FunctionReceiver() { - public Integer receive - (Function continuation) + return 2 * shift(new FunctionReceiver() { + public Integer receive(Function continuation) throws Exception { return continuation.call(continuation.call(4)); @@ -39,5 +37,74 @@ public class ComposableContinuations { }); } }) == 17); + + expect(equal(reset(new Callable>() { + public Cell call() throws Exception { + shift(new FunctionReceiver>() { + public Cell receive + (Function> continuation) + throws Exception + { + return cons(1, continuation.call(null)); + } + }); + + shift(new FunctionReceiver>() { + public Cell receive + (Function> continuation) + throws Exception + { + return cons(2, continuation.call(null)); + } + }); + + return null; + } + }), cons(1, cons(2, null)))); + } + + private static boolean equal(T a, T b) { + return (a == null && b == null) || (a != null && a.equals(b)); + } + + private static boolean equal(Cell a, Cell b) { + System.out.println(a + " vs " + b); + + while (a != null) { + if (b == null || (! equal(a.car, b.car))) { + return false; + } + a = a.cdr; + b = b.cdr; + } + + return b == null; + } + + private static Cell cons(Car car, Cell cdr) { + return new Cell(car, cdr); + } + + private static class Cell { + public final Car car; + public final Cell cdr; + + public Cell(Car car, Cell cdr) { + this.car = car; + this.cdr = cdr; + } + + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("("); + for (Cell c = this; c != null; c = c.cdr) { + sb.append(c.car); + if (c.cdr != null) { + sb.append(", "); + } + } + sb.append(")"); + return sb.toString(); + } } } From ff5744750754f8d501edfb804fdfce7684e287be Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Sun, 15 Dec 2013 16:32:02 -0700 Subject: [PATCH 3/9] fix handling of multiple shifts delimited by a single reset --- classpath/avian/Continuations.java | 40 ++++++++++++++++--------- src/jnienv.cpp | 2 +- test/extra/ComposableContinuations.java | 2 -- 3 files changed, 27 insertions(+), 17 deletions(-) diff --git a/classpath/avian/Continuations.java b/classpath/avian/Continuations.java index 814a69b012..2ffb7c7417 100644 --- a/classpath/avian/Continuations.java +++ b/classpath/avian/Continuations.java @@ -195,8 +195,10 @@ public class Continuations { } }); - if (reset.continuation != null) { - reset.continuation.handleResult(result); + Cell shift = reset.shifts; + if (shift != null) { + reset.shifts = shift.next; + shift.value.handleResult(result); } return result; @@ -212,42 +214,41 @@ public class Continuations { return (T) callWithCurrentContinuation(new CallbackReceiver() { public Object receive(final Callback continuation) { final Reset reset = latestReset.get(); - final Callback resetContinuation = reset.continuation; - reset.continuation = new Callback() { + reset.shifts = new Cell(new Callback() { public void handleResult(Object ignored) { try { - resetContinuation.handleResult + reset.continuation.handleResult (receiver.receive (new Function() { public Object call(final Object argument) - throws Exception { - Object result = callWithCurrentContinuation + throws Exception + { + return callWithCurrentContinuation (new CallbackReceiver() { public Object receive (Callback shiftContinuation) throws Exception { - reset.continuation = shiftContinuation; + reset.shifts = new Cell + (shiftContinuation, reset.shifts); + continuation.handleResult(argument); throw new AssertionError(); } }); - - reset.continuation = null; - return result; } })); } catch (Exception e) { - resetContinuation.handleException(e); + reset.continuation.handleException(e); } } public void handleException(Throwable exception) { throw new AssertionError(); } - }; + }, reset.shifts); - resetContinuation.handleResult(null); + reset.continuation.handleResult(null); throw new AssertionError(); } }); @@ -311,9 +312,20 @@ public class Continuations { private static class Reset { public Callback continuation; public final Reset next; + public Cell shifts; public Reset(Reset next) { this.next = next; } } + + private static class Cell { + public final T value; + public final Cell next; + + public Cell(T value, Cell next) { + this.value = value; + this.next = next; + } + } } diff --git a/src/jnienv.cpp b/src/jnienv.cpp index 1dcd9e6769..7e7fd63beb 100644 --- a/src/jnienv.cpp +++ b/src/jnienv.cpp @@ -3550,7 +3550,7 @@ boot(Thread* t, uintptr_t*) const char* port = findProperty(t, "avian.trace.port"); if (port) { - object host = makeString(t, "0.0.0.0"); + object host = makeString(t, "localhost"); PROTECT(t, host); object method = resolveMethod diff --git a/test/extra/ComposableContinuations.java b/test/extra/ComposableContinuations.java index b652533960..4af6bd8b8c 100644 --- a/test/extra/ComposableContinuations.java +++ b/test/extra/ComposableContinuations.java @@ -68,8 +68,6 @@ public class ComposableContinuations { } private static boolean equal(Cell a, Cell b) { - System.out.println(a + " vs " + b); - while (a != null) { if (b == null || (! equal(a.car, b.car))) { return false; From fcb7059573ab10bff0dd2b69ff6d4a162c332a3b Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Sun, 15 Dec 2013 17:11:51 -0700 Subject: [PATCH 4/9] revert unintended change to jnienv.cpp --- src/jnienv.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/jnienv.cpp b/src/jnienv.cpp index 7e7fd63beb..1dcd9e6769 100644 --- a/src/jnienv.cpp +++ b/src/jnienv.cpp @@ -3550,7 +3550,7 @@ boot(Thread* t, uintptr_t*) const char* port = findProperty(t, "avian.trace.port"); if (port) { - object host = makeString(t, "localhost"); + object host = makeString(t, "0.0.0.0"); PROTECT(t, host); object method = resolveMethod From 570b5447bf46faa6493a2d759214161b37a502fd Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Tue, 18 Mar 2014 09:49:00 -0600 Subject: [PATCH 5/9] fix openjdk and android builds when continuations=true Also, update the whitespace padding for printing test results to accommodate long names like extra.ComposableContinuations. --- makefile | 4 +++- test/test.sh | 6 +++--- unittest/test-harness.cpp | 4 ++-- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/makefile b/makefile index 66f6baab72..49df1d16b0 100755 --- a/makefile +++ b/makefile @@ -192,7 +192,7 @@ ifneq ($(android),) crypto-native := $(android)/libcore/crypto/src/main/native ifeq ($(platform),windows) - crypto-cpps := $(crypto-native)/org_conscrypt_NativeCrypto.cpp + crypto-cpps := $(crypto-native)/org_conscrypt_NativeCrypto.cpp android-cflags += -D__STDC_CONSTANT_MACROS blacklist = $(luni-native)/java_io_Console.cpp \ $(luni-native)/java_lang_ProcessManager.cpp \ @@ -1337,6 +1337,8 @@ ifneq ($(classpath),avian) $(classpath-src)/avian/ConstantPool.java \ $(classpath-src)/avian/Continuations.java \ $(classpath-src)/avian/FieldAddendum.java \ + $(classpath-src)/avian/Function.java \ + $(classpath-src)/avian/FunctionReceiver.java \ $(classpath-src)/avian/IncompatibleContinuationException.java \ $(classpath-src)/avian/Machine.java \ $(classpath-src)/avian/MethodAddendum.java \ diff --git a/test/test.sh b/test/test.sh index b56dcbb436..0bd3db7d68 100644 --- a/test/test.sh +++ b/test/test.sh @@ -18,7 +18,7 @@ fi echo -n "" >${log} -printf "%12s------- Unit tests -------\n" "" +printf "%20s------- Unit tests -------\n" "" ${unit_tester} 2>>${log} if [ "${?}" != "0" ]; then trouble=1 @@ -27,9 +27,9 @@ fi echo -printf "%12s------- Java tests -------\n" "" +printf "%20s------- Java tests -------\n" "" for test in ${tests}; do - printf "%24s: " "${test}" + printf "%32s: " "${test}" case ${mode} in debug|debug-fast|fast|small ) diff --git a/unittest/test-harness.cpp b/unittest/test-harness.cpp index 1c11c480a3..5092501861 100644 --- a/unittest/test-harness.cpp +++ b/unittest/test-harness.cpp @@ -32,7 +32,7 @@ Test::Test(const char* name): bool Test::runAll() { int failures = 0; for(Test* t = Test::first; t; t = t->next) { - printf("%24s: ", t->name); + printf("%32s: ", t->name); t->run(); failures += t->failures; if(t->failures > 0) { @@ -49,4 +49,4 @@ int main(int argc UNUSED, char** argv UNUSED) { return 0; } return 1; -} \ No newline at end of file +} From fd778c2c7608290d16459b05a73e279c4718e09e Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Fri, 21 Mar 2014 07:33:50 -0600 Subject: [PATCH 6/9] remove redundant interfaces and generalize shift/reset generics Turns out Function can do the jobs of both CallbackReceiver and FunctionReceiver, so I've removed the latter two. Also, shift and reset should work with a combination of types, not just a single type, so I've expanded their generic signatures. --- classpath/avian/CallbackReceiver.java | 15 ----- classpath/avian/Continuations.java | 28 ++++----- classpath/avian/Function.java | 4 +- classpath/avian/FunctionReceiver.java | 15 ----- src/compile.cpp | 4 +- src/types.def | 2 - test/extra/ComposableContinuations.java | 81 ++++++++++++++++--------- test/extra/Continuations.java | 36 ++++++----- test/extra/Coroutines.java | 12 ++-- test/extra/DynamicWind.java | 43 +++++++------ 10 files changed, 120 insertions(+), 120 deletions(-) delete mode 100644 classpath/avian/CallbackReceiver.java delete mode 100644 classpath/avian/FunctionReceiver.java diff --git a/classpath/avian/CallbackReceiver.java b/classpath/avian/CallbackReceiver.java deleted file mode 100644 index 82c54fba20..0000000000 --- a/classpath/avian/CallbackReceiver.java +++ /dev/null @@ -1,15 +0,0 @@ -/* Copyright (c) 2008-2013, Avian Contributors - - Permission to use, copy, modify, and/or distribute this software - for any purpose with or without fee is hereby granted, provided - that the above copyright notice and this permission notice appear - in all copies. - - There is NO WARRANTY for this software. See license.txt for - details. */ - -package avian; - -public interface CallbackReceiver { - public T receive(Callback callback) throws Exception; -} diff --git a/classpath/avian/Continuations.java b/classpath/avian/Continuations.java index 2ffb7c7417..e751516515 100644 --- a/classpath/avian/Continuations.java +++ b/classpath/avian/Continuations.java @@ -127,14 +127,14 @@ public class Continuations { * specified receiver. * *

This method will either return the result returned by - * receiver.receive(Callback), propagate the exception + * receiver.call(Callback), propagate the exception * thrown by that method, return the result passed to the * handleResult(T) method of the continuation, or throw the * exception passed to the handleException(Throwable) method of the * continuation. */ public static native T callWithCurrentContinuation - (CallbackReceiver receiver) throws Exception; + (Function,T> receiver) throws Exception; /** * Calls the specified "before" and "after" tasks each time a @@ -183,13 +183,13 @@ public class Continuations { } } - public static T reset(final Callable thunk) throws Exception { + public static C reset(final Callable thunk) throws Exception { final Reset reset = new Reset(latestReset.get()); latestReset.set(reset); try { - T result = callWithCurrentContinuation - (new CallbackReceiver() { - public T receive(Callback continuation) throws Exception { + C result = (C) callWithCurrentContinuation + (new Function,Object>() { + public Object call(Callback continuation) throws Exception { reset.continuation = continuation; return thunk.call(); } @@ -207,26 +207,26 @@ public class Continuations { } } - public static T shift - (final FunctionReceiver receiver) + public static A shift + (final Function,C> receiver) throws Exception { - return (T) callWithCurrentContinuation(new CallbackReceiver() { - public Object receive(final Callback continuation) { + return (A) callWithCurrentContinuation + (new Function,Object>() { + public Object call(final Callback continuation) { final Reset reset = latestReset.get(); reset.shifts = new Cell(new Callback() { public void handleResult(Object ignored) { try { reset.continuation.handleResult - (receiver.receive + (receiver.call (new Function() { public Object call(final Object argument) throws Exception { return callWithCurrentContinuation - (new CallbackReceiver() { - public Object receive - (Callback shiftContinuation) + (new Function,Object>() { + public Object call(Callback shiftContinuation) throws Exception { reset.shifts = new Cell diff --git a/classpath/avian/Function.java b/classpath/avian/Function.java index 633a51dfdb..8d42f7b193 100644 --- a/classpath/avian/Function.java +++ b/classpath/avian/Function.java @@ -10,6 +10,6 @@ package avian; -public interface Function { - public T call(T argument) throws Exception; +public interface Function { + public B call(A argument) throws Exception; } diff --git a/classpath/avian/FunctionReceiver.java b/classpath/avian/FunctionReceiver.java deleted file mode 100644 index d526b39240..0000000000 --- a/classpath/avian/FunctionReceiver.java +++ /dev/null @@ -1,15 +0,0 @@ -/* Copyright (c) 2008-2013, Avian Contributors - - Permission to use, copy, modify, and/or distribute this software - for any purpose with or without fee is hereby granted, provided - that the above copyright notice and this permission notice appear - in all copies. - - There is NO WARRANTY for this software. See license.txt for - details. */ - -package avian; - -public interface FunctionReceiver { - public T receive(Function function) throws Exception; -} diff --git a/src/compile.cpp b/src/compile.cpp index 41e63823cd..b974567f4b 100644 --- a/src/compile.cpp +++ b/src/compile.cpp @@ -8044,8 +8044,8 @@ callWithCurrentContinuation(MyThread* t, object receiver) if (root(t, ReceiveMethod) == 0) { object m = resolveMethod - (t, root(t, Machine::BootLoader), "avian/CallbackReceiver", "receive", - "(Lavian/Callback;)Ljava/lang/Object;"); + (t, root(t, Machine::BootLoader), "avian/Function", "call", + "(Ljava/lang/Object;)Ljava/lang/Object;"); if (m) { setRoot(t, ReceiveMethod, m); diff --git a/src/types.def b/src/types.def index 7bb8bc1fb0..82b0e5a5aa 100644 --- a/src/types.def +++ b/src/types.def @@ -176,8 +176,6 @@ (type unwindResult avian/Continuations$UnwindResult) -(type callbackReceiver avian/CallbackReceiver) - (type string java/lang/String (alias data object value) (alias length uint32_t count) diff --git a/test/extra/ComposableContinuations.java b/test/extra/ComposableContinuations.java index 4af6bd8b8c..0a517bb2f6 100644 --- a/test/extra/ComposableContinuations.java +++ b/test/extra/ComposableContinuations.java @@ -1,10 +1,9 @@ package extra; import static avian.Continuations.shift; -import static avian.Continuations.reset; -import avian.FunctionReceiver; import avian.Function; +import avian.Continuations; import java.util.concurrent.Callable; @@ -14,10 +13,11 @@ public class ComposableContinuations { } public static void main(String[] args) throws Exception { - expect(2 * reset(new Callable() { + expect(2 * Continuations.reset(new Callable() { public Integer call() throws Exception { - return 1 + shift(new FunctionReceiver() { - public Integer receive(Function continuation) + return 1 + shift + (new Function,Integer>() { + public Integer call(Function continuation) throws Exception { return continuation.call(5); @@ -26,10 +26,11 @@ public class ComposableContinuations { } }) == 12); - expect(1 + reset(new Callable() { + expect(1 + Continuations.reset(new Callable() { public Integer call() throws Exception { - return 2 * shift(new FunctionReceiver() { - public Integer receive(Function continuation) + return 2 * shift + (new Function,Integer>() { + public Integer call(Function continuation) throws Exception { return continuation.call(continuation.call(4)); @@ -38,29 +39,53 @@ public class ComposableContinuations { } }) == 17); - expect(equal(reset(new Callable>() { - public Cell call() throws Exception { - shift(new FunctionReceiver>() { - public Cell receive - (Function> continuation) + expect + (equal + (Continuations.,Cell>reset + (new Callable>() { + public Cell call() throws Exception { + shift(new Function,Cell>, + Cell>() + { + public Cell call + (Function,Cell> continuation) + throws Exception + { + return cons(1, continuation.call(null)); + } + }); + + shift(new Function,Cell>, + Cell>() + { + public Cell call + (Function,Cell> continuation) + throws Exception + { + return cons(2, continuation.call(null)); + } + }); + + return null; + } + }), cons(1, cons(2, null)))); + + expect + (equal + (Continuations.reset + (new Callable() { + public String call() throws Exception { + return new String + (shift(new Function,Integer>() { + public Integer call(Function continuation) throws Exception { - return cons(1, continuation.call(null)); + return Integer.parseInt + (continuation.call(new byte[] { 0x34, 0x32 })); } - }); - - shift(new FunctionReceiver>() { - public Cell receive - (Function> continuation) - throws Exception - { - return cons(2, continuation.call(null)); - } - }); - - return null; - } - }), cons(1, cons(2, null)))); + }), "UTF-8"); + } + }), 42)); } private static boolean equal(T a, T b) { diff --git a/test/extra/Continuations.java b/test/extra/Continuations.java index a439d8a58d..1186c43a4a 100644 --- a/test/extra/Continuations.java +++ b/test/extra/Continuations.java @@ -2,7 +2,7 @@ package extra; import static avian.Continuations.callWithCurrentContinuation; -import avian.CallbackReceiver; +import avian.Function; import avian.Callback; public class Continuations { @@ -11,22 +11,26 @@ public class Continuations { } public static void main(String[] args) throws Exception { - expect(callWithCurrentContinuation(new CallbackReceiver() { - public Integer receive(Callback continuation) { - continuation.handleResult(42); - throw new AssertionError(); - } - }) == 42); + expect + (callWithCurrentContinuation + (new Function,Integer>() { + public Integer call(Callback continuation) { + continuation.handleResult(42); + throw new AssertionError(); + } + }) == 42); - expect(callWithCurrentContinuation(new CallbackReceiver() { - public Integer receive(Callback continuation) { - return 43; - } - }) == 43); + expect + (callWithCurrentContinuation + (new Function,Integer>() { + public Integer call(Callback continuation) { + return 43; + } + }) == 43); try { - callWithCurrentContinuation(new CallbackReceiver() { - public Integer receive(Callback continuation) { + callWithCurrentContinuation(new Function,Integer>() { + public Integer call(Callback continuation) { continuation.handleException(new MyException()); throw new AssertionError(); } @@ -37,8 +41,8 @@ public class Continuations { } try { - callWithCurrentContinuation(new CallbackReceiver() { - public Integer receive(Callback continuation) + callWithCurrentContinuation(new Function,Integer>() { + public Integer call(Callback continuation) throws MyException { throw new MyException(); diff --git a/test/extra/Coroutines.java b/test/extra/Coroutines.java index 7d4ee9c5a5..3afd2dce73 100644 --- a/test/extra/Coroutines.java +++ b/test/extra/Coroutines.java @@ -2,7 +2,7 @@ package extra; import static avian.Continuations.callWithCurrentContinuation; -import avian.CallbackReceiver; +import avian.Function; import avian.Callback; public class Coroutines { @@ -40,8 +40,8 @@ public class Coroutines { final Consumer consumer = new Consumer() { public void consume(final Character c) throws Exception { - callWithCurrentContinuation(new CallbackReceiver() { - public Object receive(Callback continuation) { + callWithCurrentContinuation(new Function,Object>() { + public Object call(Callback continuation) { state.produceNext = continuation; state.consumeNext.handleResult(c); @@ -53,9 +53,9 @@ public class Coroutines { }; final Producer producer = new Producer() { - final CallbackReceiver receiver - = new CallbackReceiver() { - public Character receive(Callback continuation) + final Function,Character> receiver + = new Function,Character>() { + public Character call(Callback continuation) throws Exception { state.consumeNext = continuation; diff --git a/test/extra/DynamicWind.java b/test/extra/DynamicWind.java index aa617ddc9c..bbc305842c 100644 --- a/test/extra/DynamicWind.java +++ b/test/extra/DynamicWind.java @@ -3,7 +3,7 @@ package extra; import static avian.Continuations.callWithCurrentContinuation; import static avian.Continuations.dynamicWind; -import avian.CallbackReceiver; +import avian.Function; import avian.Callback; import java.util.concurrent.Callable; @@ -86,24 +86,27 @@ public class DynamicWind { }); } - private void continuationUnwindTest(final CallbackReceiver receiver) + private void continuationUnwindTest + (final Function,Integer> receiver) throws Exception { System.out.println("continuationUnwindTest enter"); try { - expect(callWithCurrentContinuation(new CallbackReceiver() { - public Integer receive(final Callback continuation) - throws Exception - { - unwindTest(new Callable() { - public Integer call() throws Exception { - return receiver.receive(continuation); - } - }); - throw new AssertionError(); - } - }) == 42); + expect + (callWithCurrentContinuation + (new Function,Integer>() { + public Integer call(final Callback continuation) + throws Exception + { + unwindTest(new Callable() { + public Integer call() throws Exception { + return receiver.call(continuation); + } + }); + throw new AssertionError(); + } + }) == 42); } catch (MyException e) { e.printStackTrace(); } @@ -118,8 +121,8 @@ public class DynamicWind { } private void continuationResultUnwind() throws Exception { - continuationUnwindTest(new CallbackReceiver() { - public Integer receive(final Callback continuation) { + continuationUnwindTest(new Function,Integer>() { + public Integer call(final Callback continuation) { continuation.handleResult(42); throw new AssertionError(); } @@ -127,8 +130,8 @@ public class DynamicWind { } private void continuationExceptionUnwind() throws Exception { - continuationUnwindTest(new CallbackReceiver() { - public Integer receive(final Callback continuation) { + continuationUnwindTest(new Function,Integer>() { + public Integer call(final Callback continuation) { continuation.handleException(new MyException()); throw new AssertionError(); } @@ -163,8 +166,8 @@ public class DynamicWind { task = 1; return callWithCurrentContinuation - (new CallbackReceiver() { - public Integer receive(final Callback continuation) + (new Function,Integer>() { + public Integer call(final Callback continuation) throws Exception { continuationReference = continuation; From 959172a11222871023dad6abf6c0134861f157ab Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Mon, 24 Mar 2014 09:50:09 -0600 Subject: [PATCH 7/9] return naturally from function in Continuations.shift rather than via the continuation Since the function in question is the only one on the call stack above the reset method, there's no need to invoke the captured continuation -- we get the same effect by just returning normally, and it's more efficient that way. --- classpath/avian/Continuations.java | 72 ++++++++++++++++-------------- 1 file changed, 39 insertions(+), 33 deletions(-) diff --git a/classpath/avian/Continuations.java b/classpath/avian/Continuations.java index e751516515..09c42a2211 100644 --- a/classpath/avian/Continuations.java +++ b/classpath/avian/Continuations.java @@ -187,7 +187,7 @@ public class Continuations { final Reset reset = new Reset(latestReset.get()); latestReset.set(reset); try { - C result = (C) callWithCurrentContinuation + Object result = callWithCurrentContinuation (new Function,Object>() { public Object call(Callback continuation) throws Exception { reset.continuation = continuation; @@ -195,13 +195,15 @@ public class Continuations { } }); - Cell shift = reset.shifts; - if (shift != null) { - reset.shifts = shift.next; - shift.value.handleResult(result); + while (true) { + Cell shift = reset.shifts; + if (shift != null) { + reset.shifts = shift.next; + result = shift.value.call(result); + } else { + return (C) result; + } } - - return result; } finally { latestReset.set(reset.next); } @@ -215,32 +217,36 @@ public class Continuations { (new Function,Object>() { public Object call(final Callback continuation) { final Reset reset = latestReset.get(); - reset.shifts = new Cell(new Callback() { - public void handleResult(Object ignored) { - try { - reset.continuation.handleResult - (receiver.call - (new Function() { - public Object call(final Object argument) - throws Exception - { - return callWithCurrentContinuation - (new Function,Object>() { - public Object call(Callback shiftContinuation) - throws Exception - { - reset.shifts = new Cell - (shiftContinuation, reset.shifts); + reset.shifts = new Cell(new Function() { + public Object call(Object ignored) throws Exception { + return receiver.call + (new Function() { + public Object call(final Object argument) + throws Exception + { + return callWithCurrentContinuation + (new Function,Object>() { + public Object call + (final Callback shiftContinuation) + throws Exception + { + reset.shifts = new Cell + (new Function() { + public Object call(Object result) + throws Exception + { + shiftContinuation.handleResult(result); + throw new AssertionError(); + } + }, + reset.shifts); - continuation.handleResult(argument); - throw new AssertionError(); - } - }); - } - })); - } catch (Exception e) { - reset.continuation.handleException(e); - } + continuation.handleResult(argument); + throw new AssertionError(); + } + }); + } + }); } public void handleException(Throwable exception) { @@ -312,7 +318,7 @@ public class Continuations { private static class Reset { public Callback continuation; public final Reset next; - public Cell shifts; + public Cell shifts; public Reset(Reset next) { this.next = next; From 164717056b86a8e1232bd6ae21e2896356a3a925 Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Mon, 24 Mar 2014 10:07:53 -0600 Subject: [PATCH 8/9] remove makefile references to deleted classes --- makefile | 2 -- 1 file changed, 2 deletions(-) diff --git a/makefile b/makefile index 49df1d16b0..f4be56d570 100755 --- a/makefile +++ b/makefile @@ -1330,7 +1330,6 @@ ifneq ($(classpath),avian) $(classpath-src)/avian/AnnotationInvocationHandler.java \ $(classpath-src)/avian/Assembler.java \ $(classpath-src)/avian/Callback.java \ - $(classpath-src)/avian/CallbackReceiver.java \ $(classpath-src)/avian/ClassAddendum.java \ $(classpath-src)/avian/InnerClassReference.java \ $(classpath-src)/avian/Classes.java \ @@ -1338,7 +1337,6 @@ ifneq ($(classpath),avian) $(classpath-src)/avian/Continuations.java \ $(classpath-src)/avian/FieldAddendum.java \ $(classpath-src)/avian/Function.java \ - $(classpath-src)/avian/FunctionReceiver.java \ $(classpath-src)/avian/IncompatibleContinuationException.java \ $(classpath-src)/avian/Machine.java \ $(classpath-src)/avian/MethodAddendum.java \ From c2bfba92f0c2ce32b577f6b83604372b44841015 Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Mon, 24 Mar 2014 10:47:37 -0600 Subject: [PATCH 9/9] consolidate duplicate Cell classes --- classpath/avian/Cell.java | 20 +++++++++++ classpath/avian/Continuations.java | 10 ------ test/extra/ComposableContinuations.java | 46 ++----------------------- 3 files changed, 23 insertions(+), 53 deletions(-) diff --git a/classpath/avian/Cell.java b/classpath/avian/Cell.java index 448c515c19..afe9c9213b 100644 --- a/classpath/avian/Cell.java +++ b/classpath/avian/Cell.java @@ -31,4 +31,24 @@ public class Cell { sb.append(")"); return sb.toString(); } + + public static Cell cons(Car car, Cell cdr) { + return new Cell(car, cdr); + } + + public static boolean equal(T a, T b) { + return (a == null && b == null) || (a != null && a.equals(b)); + } + + public static boolean equal(Cell a, Cell b) { + while (a != null) { + if (b == null || (! equal(a.value, b.value))) { + return false; + } + a = a.next; + b = b.next; + } + + return b == null; + } } diff --git a/classpath/avian/Continuations.java b/classpath/avian/Continuations.java index 09c42a2211..3401cbcd45 100644 --- a/classpath/avian/Continuations.java +++ b/classpath/avian/Continuations.java @@ -324,14 +324,4 @@ public class Continuations { this.next = next; } } - - private static class Cell { - public final T value; - public final Cell next; - - public Cell(T value, Cell next) { - this.value = value; - this.next = next; - } - } } diff --git a/test/extra/ComposableContinuations.java b/test/extra/ComposableContinuations.java index 0a517bb2f6..d33d39693b 100644 --- a/test/extra/ComposableContinuations.java +++ b/test/extra/ComposableContinuations.java @@ -1,7 +1,10 @@ package extra; import static avian.Continuations.shift; +import static avian.Cell.cons; +import static avian.Cell.equal; +import avian.Cell; import avian.Function; import avian.Continuations; @@ -87,47 +90,4 @@ public class ComposableContinuations { } }), 42)); } - - private static boolean equal(T a, T b) { - return (a == null && b == null) || (a != null && a.equals(b)); - } - - private static boolean equal(Cell a, Cell b) { - while (a != null) { - if (b == null || (! equal(a.car, b.car))) { - return false; - } - a = a.cdr; - b = b.cdr; - } - - return b == null; - } - - private static Cell cons(Car car, Cell cdr) { - return new Cell(car, cdr); - } - - private static class Cell { - public final Car car; - public final Cell cdr; - - public Cell(Car car, Cell cdr) { - this.car = car; - this.cdr = cdr; - } - - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append("("); - for (Cell c = this; c != null; c = c.cdr) { - sb.append(c.car); - if (c.cdr != null) { - sb.append(", "); - } - } - sb.append(")"); - return sb.toString(); - } - } }