use of com.disney.groovity.stats.GroovityStatistics.Execution 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;
}
Aggregations