use of com.disney.groovity.util.DeadlockFreeExecutor in project groovity by disney.
the class Async method tag.
@SuppressWarnings({ "rawtypes", "unchecked" })
@Override
public Object tag(Map attributes, final Closure body) throws Exception {
final Integer timeoutSeconds = resolve(attributes, TIMEOUT, Integer.class);
final ScriptHelper scriptHelper = getScriptHelper(body);
final Binding binding = scriptHelper.getBinding();
final Map variables = binding.getVariables();
final AwaitContext asyncContext = AwaitContext.get(variables);
DeadlockFreeExecutor createdThreadPool = null;
// make a copy of current binding for async
final Map asyncVariables = asyncCopy(variables);
if (asyncContext == null || !asyncVariables.containsKey(Async.EXECUTOR_BINDING)) {
Integer numThreads = resolve(attributes, POOL, Integer.class);
if (numThreads != null) {
createdThreadPool = new DeadlockFreeExecutor(interruptFactory, numThreads);
asyncVariables.put(EXECUTOR_BINDING, createdThreadPool);
}
}
final DeadlockFreeExecutor asyncPool = createdThreadPool != null ? createdThreadPool : ((asyncContext != null) ? getExecutor(asyncVariables) : sharedThreadPool);
final boolean shutdownPool = createdThreadPool != null;
final CharArrayWriter out = new CharArrayWriter();
asyncVariables.put(OUT, out);
if (asyncContext != null) {
// signal a boundary for sequencing async output
asyncContext.signalAsync(variables, out);
}
final Execution parentStack = asyncContext != null ? asyncContext.getWaitingExecution() : null;
String scriptPath = scriptHelper.getClassLoader().getScriptName();
final long timeoutTime = timeoutSeconds == null ? -1 : System.currentTimeMillis() + (timeoutSeconds * 1000);
final Callable<Object> bodyRunner = new Callable<Object>() {
@Override
public Object call() throws Exception {
Binding asyncBinding = new Binding(asyncVariables);
Binding oldThreadBinding = ScriptHelper.THREAD_BINDING.get();
ScriptHelper.THREAD_BINDING.set(asyncBinding);
final Execution restoreStack = parentStack != null ? GroovityStatistics.registerStack(parentStack) : null;
try {
Closure asyncBody;
if (body.getThisObject() instanceof Class) {
// if the parent script is not available it is a special case (static) and we may share the context
asyncBody = body;
asyncBody.setDelegate(asyncBinding);
asyncBody.setResolveStrategy(Closure.DELEGATE_FIRST);
} else {
Script asyncInstance = scriptHelper.load(scriptPath);
asyncBody = body.rehydrate(asyncInstance, asyncInstance, asyncInstance);
}
if (timeoutTime > 0) {
// use an interrupt to enforce the timeout
long startTime = System.currentTimeMillis();
if (startTime > timeoutTime) {
throw new InterruptedException();
}
final ScheduledFuture<?> interrupt = interruptFactory.scheduleInterrupt(timeoutTime - startTime);
try {
return asyncBody.call();
} finally {
interrupt.cancel(true);
}
}
return asyncBody.call();
} catch (Throwable e) {
if (asyncContext == null) {
// with no known waiters this exception could get lost, let's log it
log.log(Level.SEVERE, "Error in async", e);
}
throw e;
} finally {
if (oldThreadBinding == null) {
ScriptHelper.THREAD_BINDING.remove();
} else {
ScriptHelper.THREAD_BINDING.set(oldThreadBinding);
}
if (parentStack != null) {
GroovityStatistics.registerStack(restoreStack);
}
if (shutdownPool) {
sharedThreadPool.submit(new Runnable() {
@Override
public void run() {
asyncPool.shutdown();
try {
if (!asyncPool.awaitTermination(60, TimeUnit.SECONDS)) {
asyncPool.shutdownNow();
asyncPool.awaitTermination(60, TimeUnit.SECONDS);
}
} catch (InterruptedException e) {
asyncPool.shutdownNow();
}
}
});
}
}
}
};
Future<Object> future = asyncPool.submit(bodyRunner);
if (asyncContext != null) {
asyncContext.add(future);
}
String var = resolve(attributes, VAR, String.class);
if (var != null && var.length() > 0) {
variables.put(var, future);
}
return future;
}
use of com.disney.groovity.util.DeadlockFreeExecutor in project groovity by disney.
the class Await method tag.
@SuppressWarnings({ "rawtypes", "unchecked" })
@Override
public Object tag(final Map attributes, final Closure body) throws Exception {
final Integer timeoutSeconds = resolve(attributes, TIMEOUT, Integer.class);
final ScriptHelper scriptHelper = getScriptHelper(body);
final Binding binding = scriptHelper.getBinding();
final Map variables = binding.getVariables();
final AwaitContext awaitContext = AwaitContext.create(variables);
DeadlockFreeExecutor createdThreadPool = null;
DeadlockFreeExecutor oldThreadPool = null;
if (!variables.containsKey(Async.EXECUTOR_BINDING)) {
Integer numThreads = resolve(attributes, POOL, Integer.class);
if (numThreads != null) {
createdThreadPool = new DeadlockFreeExecutor(interruptFactory, numThreads);
oldThreadPool = (DeadlockFreeExecutor) variables.put(Async.EXECUTOR_BINDING, createdThreadPool);
}
}
final Writer origOut = (Writer) variables.get(OUT);
try {
Collection<Object> resultsList;
final long timeoutTime = timeoutSeconds == null ? -1 : System.currentTimeMillis() + (timeoutSeconds * 1000);
Throwable error = null;
try {
body.call();
} catch (Throwable e) {
error = e;
}
ArrayDeque<Future> futuresList = awaitContext.getFutures();
resultsList = new ArrayList<>(futuresList.size());
for (Future f : futuresList) {
if (error != null) {
f.cancel(true);
continue;
}
// flush any pending writers before waiting
Optional<CharArrayWriter> ocw;
while ((ocw = awaitContext.nextFragmentWriter()).isPresent()) {
CharArrayWriter cw = ocw.get();
if (cw.size() > 0 && origOut != null) {
cw.writeTo(origOut);
}
}
if (timeoutTime == -1 || f.isDone()) {
try {
resultsList.add(f.get());
} catch (Throwable e) {
error = e;
}
} else {
long timeoutDelta = timeoutTime - System.currentTimeMillis();
if (timeoutDelta <= 0) {
error = new InterruptedException("Await reached timeout of " + timeoutSeconds);
f.cancel(true);
} else {
try {
resultsList.add(f.get(timeoutDelta, TimeUnit.MILLISECONDS));
} catch (Throwable e) {
error = e;
}
}
}
if (error == null) {
// flush async buffer
CharArrayWriter cw = awaitContext.nextFragmentWriter().get();
if (cw.size() > 0 && origOut != null) {
cw.writeTo(origOut);
}
}
}
if (error != null) {
if (error instanceof Exception) {
throw (Exception) error;
}
throw new ExecutionException(error);
}
if (origOut != null) {
final Object finalOut = variables.get(OUT);
if (finalOut != origOut) {
final CharArrayWriter cfo = ((CharArrayWriter) finalOut);
try {
if (cfo.size() > 0) {
cfo.writeTo(origOut);
}
} finally {
cfo.close();
}
}
}
final String var = resolve(attributes, VAR, String.class);
if (var != null && var.length() > 0) {
variables.put(var, resultsList);
}
return resultsList;
} finally {
awaitContext.close(variables);
if (origOut != null) {
variables.put(OUT, origOut);
} else {
variables.remove(OUT);
}
if (createdThreadPool != null) {
createdThreadPool.shutdown();
try {
if (!createdThreadPool.awaitTermination(60, TimeUnit.SECONDS)) {
createdThreadPool.shutdownNow();
createdThreadPool.awaitTermination(60, TimeUnit.SECONDS);
}
} catch (InterruptedException e) {
createdThreadPool.shutdownNow();
}
if (oldThreadPool != null) {
variables.put(Async.EXECUTOR_BINDING, oldThreadPool);
} else {
variables.remove(Async.EXECUTOR_BINDING);
}
}
}
}
Aggregations