Skip to content

Commit

Permalink
Clean up async exceptions
Browse files Browse the repository at this point in the history
  • Loading branch information
ngallagher committed Jan 26, 2019
1 parent 9741265 commit 0f2df98
Show file tree
Hide file tree
Showing 9 changed files with 93 additions and 62 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@ public StoreContext(Store store){

public StoreContext(Store store, Executor executor){
this.stack = new ThreadStack();
this.scheduler = new ExecutorScheduler(executor);
this.wrapper = new ProxyWrapper(this);
this.verifier = new ExecutableVerifier();
this.interceptor = new TraceInterceptor(verifier, stack);
Expand All @@ -76,6 +75,7 @@ public StoreContext(Store store, Executor executor){
this.evaluator = new OperationEvaluator(this, verifier, executor);
this.provider = new PlatformProvider(extractor, wrapper, stack);
this.binder = new FunctionBinder(resolver, handler);
this.scheduler = new ExecutorScheduler(handler, executor);
}

@Override
Expand Down
10 changes: 10 additions & 0 deletions snap-compile/src/test/java/org/snapscript/compile/AwaitTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,15 @@ public class AwaitTest extends ScriptTestCase {
"\n"+
"assert t != o;\n";

private static final String SUCCESS_22 =
"async func foo(){\n"+
" await blah();\n"+
"}\n"+
"async func blah() {\n"+
" throw new IllegalStateException();\n"+
"}\n"+
"foo().failure(e -> e.printStackTrace()).join();\n";

public void testAwait() throws Exception {
assertScriptExecutes(SUCCESS_1);
assertScriptExecutes(SUCCESS_2);
Expand All @@ -406,6 +415,7 @@ public void testAwait() throws Exception {
assertScriptExecutes(SUCCESS_19);
assertScriptExecutes(SUCCESS_20);
assertScriptExecutes(SUCCESS_21);
assertScriptExecutes(SUCCESS_22);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,75 +10,64 @@
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;

import org.snapscript.core.error.InternalStateException;
import org.snapscript.core.error.ErrorHandler;
import org.snapscript.core.scope.Scope;
import org.snapscript.core.variable.Value;

public class ExecutorScheduler implements TaskScheduler {

private final ErrorHandler handler;
private final Executor executor;

public ExecutorScheduler(Executor executor) {
public ExecutorScheduler(ErrorHandler handler, Executor executor) {
this.executor = executor;
this.handler = handler;
}

@Override
public Promise schedule(Task task) {
PromiseFuture promise = new PromiseFuture(executor, task);
public Promise schedule(Scope scope, Task task) {
PromiseDelegate promise = new PromiseDelegate(handler, executor, scope, task);

if(task != null) {
promise.execute();
}
return promise;
}

private static class PromiseFuture implements Promise {
private static class PromiseDelegate implements Promise {

private final PromiseDispatcher dispatcher;
private final PromiseFuture future;
private final PromiseAnswer answer;
private final PromiseTask task;
private final FutureTask future;
private final Executor executor;

public PromiseFuture(Executor executor, Task task) {
this.dispatcher = new PromiseDispatcher();
this.future = new FutureTask(dispatcher);
this.answer = new PromiseAnswer(dispatcher, future);
public PromiseDelegate(ErrorHandler handler, Executor executor, Scope scope, Task task) {
this.future = new PromiseFuture(handler, scope);
this.answer = new PromiseAnswer(future, handler, scope);
this.task = new PromiseTask(answer, task);
this.executor = executor;
}

@Override
public Object value() {
try {
return future.get();
} catch(Exception e) {
throw new InternalStateException("Could not get value", e);
}
return future.get();
}

@Override
public Object value(long wait) {
try {
return future.get(wait, MILLISECONDS);
} catch(Exception e) {
throw new InternalStateException("Could not get value", e);
}
return future.get(wait, MILLISECONDS);
}

@Override
public Object value(long wait, TimeUnit unit) {
try {
return future.get(wait, unit);
} catch(Exception e) {
throw new InternalStateException("Could not get value", e);
}
return future.get(wait, unit);
}

@Override
public Promise join() {
try {
future.get();
} catch(Exception e) {
} catch(Exception e){
return this;
}
return this;
Expand All @@ -88,7 +77,7 @@ public Promise join() {
public Promise join(long wait) {
try {
future.get(wait, MILLISECONDS);
} catch(Exception e) {
} catch(Exception e){
return this;
}
return this;
Expand All @@ -98,7 +87,7 @@ public Promise join(long wait) {
public Promise join(long wait, TimeUnit unit) {
try {
future.get(wait, unit);
} catch(Exception e) {
} catch(Exception e){
return this;
}
return this;
Expand All @@ -107,7 +96,7 @@ public Promise join(long wait, TimeUnit unit) {
@Override
public Promise failure(Task task) {
if(task != null) {
dispatcher.failure(task);
future.failure(task);
}
return this;
}
Expand All @@ -117,15 +106,15 @@ public Promise failure(Runnable task) {
Task adapter = new RunnableTask(task);

if(task != null) {
dispatcher.failure(adapter);
future.failure(adapter);
}
return this;
}

@Override
public Promise success(Task task) {
if(task != null) {
dispatcher.success(task);
future.success(task);
}
return this;
}
Expand All @@ -135,7 +124,7 @@ public Promise success(Runnable task) {
Task adapter = new RunnableTask(task);

if(task != null) {
dispatcher.success(adapter);
future.success(adapter);
}
return this;
}
Expand All @@ -149,18 +138,24 @@ public void execute() {
}
}

private static class PromiseDispatcher implements Callable {
private static class PromiseFuture implements Callable {

private final AtomicReference<Throwable> error;
private final AtomicReference<Value> success;
private final BlockingQueue<Task> listeners;
private final BlockingQueue<Task> failures;
private final ErrorHandler handler;
private final FutureTask task;
private final Scope scope;

public PromiseDispatcher() {
public PromiseFuture(ErrorHandler handler, Scope scope) {
this.failures = new LinkedBlockingQueue<Task>();
this.listeners = new LinkedBlockingQueue<Task>();
this.error = new AtomicReference<Throwable>();
this.success = new AtomicReference<Value>();
this.task = new FutureTask(this);
this.handler = handler;
this.scope = scope;
}

@Override
Expand All @@ -173,6 +168,22 @@ public Object call() {
return null;
}

public Object get() {
try {
return task.get();
} catch(Exception e) {
return handler.failInternalError(scope, e);
}
}

public Object get(long wait, TimeUnit unit) {
try {
return task.get(wait, unit);
} catch(Exception e) {
return handler.failInternalError(scope, e);
}
}

public void complete() {
Value value = success.get();

Expand Down Expand Up @@ -216,48 +227,49 @@ public void failure(Task task) {
}

public void success(Value value) {
success.compareAndSet(null, value);
if(success.compareAndSet(null, value)) {
task.run();
}
}

public void failure(Throwable cause) {
error.compareAndSet(null, cause);
if(error.compareAndSet(null, cause)) {
task.run();
}
}
}

private static class PromiseAnswer implements Answer {

private final PromiseDispatcher dispatcher;
private final FutureTask task;
private final PromiseFuture future;
private final ErrorHandler handler;
private final Scope scope;

public PromiseAnswer(PromiseDispatcher dispatcher, FutureTask task) {
this.dispatcher = dispatcher;
this.task = task;
public PromiseAnswer(PromiseFuture future, ErrorHandler handler, Scope scope) {
this.handler = handler;
this.future = future;
this.scope = scope;
}

@Override
public void success(Object result) {
try {
Value value = Value.getTransient(result);

dispatcher.success(value);
task.run();
dispatcher.complete();
} catch(Exception cause) {
dispatcher.failure(cause);
dispatcher.error();

throw new InternalStateException("Could not complete task", cause);
future.success(value);
future.complete();
} catch(Exception e) {
handler.failInternalError(scope, e);
}
}

@Override
public void failure(Throwable cause) {
try {
dispatcher.failure(cause);
task.run();
dispatcher.error();
future.failure(cause);
future.error();
} catch(Exception e) {
throw new InternalStateException("Could not complete task", cause);
handler.failInternalError(scope, e);
}
}
}
Expand All @@ -276,7 +288,7 @@ public PromiseTask(PromiseAnswer answer, Task task) {
public void run() {
try {
task.execute(answer);
}catch(Throwable cause){
}catch(Exception cause){
answer.failure(cause);
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package org.snapscript.core.resume;

import org.snapscript.core.scope.Scope;

public interface TaskScheduler {
Promise schedule(Task<Answer> task);
Promise schedule(Scope scope, Task<Answer> task);
}
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,6 @@ private static class TestContext implements Context {
private final Store store;

public TestContext(){
this.scheduler = new ExecutorScheduler(null);
this.linker = new TestLinker();
this.store = new ClassPathStore();
this.stack = new ThreadStack();
Expand All @@ -167,6 +166,7 @@ public TestContext(){
this.matcher = new ConstraintMatcher(loader, wrapper);
this.handler = new ErrorHandler(extractor, stack);
this.table = new FunctionBinder(resolver, handler);
this.scheduler = new ExecutorScheduler(handler,null);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ public MockContext(){
this.linker = new TestLinker();
this.store = new ClassPathStore();
this.stack = new ThreadStack();
this.scheduler = new ExecutorScheduler(null);
this.wrapper = new ProxyWrapper(this);
this.manager = new StoreManager(store);
this.registry = new ModuleRegistry(this, null);
Expand All @@ -57,6 +56,7 @@ public MockContext(){
this.matcher = new ConstraintMatcher(loader, wrapper);
this.handler = new ErrorHandler(extractor, stack);
this.table = new FunctionBinder(resolver, handler);
this.scheduler = new ExecutorScheduler(handler,null);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ private Result execute(Scope scope, Result result) throws Exception {
Yield yield = result.getValue();
Iterator<Object> iterator = yield.iterator();
Task<Answer> task = new AnswerTask(iterator);
Promise promise = scheduler.schedule(task);
Promise promise = scheduler.schedule(scope, task);

return Result.getNormal(promise);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package org.snapscript.tree.resume;

import static org.snapscript.core.result.Result.NORMAL;

import org.snapscript.core.Compilation;
import org.snapscript.core.Context;
import org.snapscript.core.Evaluation;
Expand Down Expand Up @@ -129,7 +131,12 @@ private Result execute(final Scope scope, Object state) throws Exception {
Value value = operation.operate(scope, assign, result);

if (value != null) {
return Result.getNormal(value.getValue());
Object object = value.getValue();

if(object != null) {
return Result.getNormal(object);
}
return NORMAL;
}
}
return Result.getNormal(state);
Expand Down
Loading

0 comments on commit 0f2df98

Please sign in to comment.