Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

LDEV-4696 - Feat/allow Lucee threads to cooperatively stop avoiding threadDeath in tomcat #2425

Open
wants to merge 5 commits into
base: 6.2
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions core/src/main/java/lucee/commons/io/SystemUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -1406,12 +1406,11 @@ public static void stop(PageContext pc, Thread thread) {
}

private static boolean _stop(Thread thread, Log log, boolean force) {
// we try to interrupt/stop the suspended thrad
// we try to interrupt/stop the suspended thread
suspendEL(thread);
try {
if (isInLucee(thread)) {
if (!force) thread.interrupt();
else thread.stop();
thread.interrupt();
}
else {
if (log != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import lucee.transformer.TransformerException;
import lucee.transformer.bytecode.Body;
import lucee.transformer.bytecode.BytecodeContext;
import lucee.transformer.bytecode.util.InterruptHandlerInjector;
import lucee.transformer.expression.ExprBoolean;
import lucee.transformer.expression.Expression;

Expand Down Expand Up @@ -57,14 +58,18 @@ public DoWhile(Expression expr, Body body, Position start, Position end, String
@Override
public void _writeOut(BytecodeContext bc) throws TransformerException {
GeneratorAdapter adapter = bc.getAdapter();
final int loopCounter = InterruptHandlerInjector.writeLoopInit(adapter);

adapter.visitLabel(begin);
body.writeOut(bc);

InterruptHandlerInjector.writeLoopBodyEnd(adapter, loopCounter, beforeEnd, "during do while");
adapter.visitLabel(beforeEnd);

expr.writeOut(bc, Expression.MODE_VALUE);
adapter.ifZCmp(Opcodes.IFNE, begin);

InterruptHandlerInjector.writePreempt(adapter, end, "after do while");
adapter.visitLabel(end);

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import lucee.transformer.bytecode.Body;
import lucee.transformer.bytecode.BytecodeContext;
import lucee.transformer.bytecode.util.ASMUtil;
import lucee.transformer.bytecode.util.InterruptHandlerInjector;
import lucee.transformer.expression.Expression;

public final class For extends StatementBaseNoFinal implements FlowControlBreak, FlowControlContinue, HasBody {
Expand Down Expand Up @@ -69,6 +70,7 @@ public void _writeOut(BytecodeContext bc) throws TransformerException {
Label beforeInit = new Label();
Label afterInit = new Label();
Label afterUpdate = new Label();
final int loopCounter = InterruptHandlerInjector.writeLoopInit(adapter);

bc.visitLine(getStart());
adapter.visitLabel(beforeInit);
Expand All @@ -87,13 +89,16 @@ public void _writeOut(BytecodeContext bc) throws TransformerException {
update.writeOut(bc, Expression.MODE_VALUE);
ASMUtil.pop(adapter, update, Expression.MODE_VALUE);
}

InterruptHandlerInjector.writeLoopBodyEnd(adapter, loopCounter, afterUpdate, "during for loop");
// ExpressionUtil.visitLine(bc, getStartLine());
adapter.visitLabel(afterUpdate);

if (condition != null) condition.writeOut(bc, Expression.MODE_VALUE);
else bc.getFactory().TRUE().writeOut(bc, Expression.MODE_VALUE);
adapter.visitJumpInsn(Opcodes.IFNE, afterInit);
// ExpressionUtil.visitLine(bc, getEndLine());
InterruptHandlerInjector.writePreempt(adapter, end, "after for loop");
adapter.visitLabel(end);

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import lucee.transformer.bytecode.BytecodeContext;
import lucee.transformer.bytecode.expression.var.VariableRef;
import lucee.transformer.bytecode.util.Types;
import lucee.transformer.bytecode.util.InterruptHandlerInjector;
import lucee.transformer.bytecode.visitor.OnFinally;
import lucee.transformer.bytecode.visitor.TryFinallyVisitor;
import lucee.transformer.expression.Expression;
Expand Down Expand Up @@ -81,6 +82,7 @@ public void _writeOut(BytecodeContext bc) throws TransformerException {
GeneratorAdapter adapter = bc.getAdapter();
final int it = adapter.newLocal(Types.ITERATOR);
final int item = adapter.newLocal(Types.REFERENCE);
final int loopCounter = InterruptHandlerInjector.writeLoopInit(adapter);

// Value
// ForEachUtil.toIterator(value)
Expand Down Expand Up @@ -130,7 +132,7 @@ public void _writeOut(BytecodeContext bc) throws TransformerException {

// Body
body.writeOut(bc);
adapter.visitJumpInsn(Opcodes.GOTO, begin);
InterruptHandlerInjector.writeLoopBodyEnd(adapter, loopCounter, begin, "during foreach loop");
adapter.visitLabel(end);
tfv.visitTryEnd(bc);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import lucee.transformer.TransformerException;
import lucee.transformer.bytecode.Body;
import lucee.transformer.bytecode.BytecodeContext;
import lucee.transformer.bytecode.util.InterruptHandlerInjector;
import lucee.transformer.expression.ExprBoolean;
import lucee.transformer.expression.Expression;

Expand Down Expand Up @@ -67,13 +68,14 @@ public While(boolean b, Body body, Position start, Position end, String label) {
@Override
public void _writeOut(BytecodeContext bc) throws TransformerException {
GeneratorAdapter adapter = bc.getAdapter();
final int loopCounter = InterruptHandlerInjector.writeLoopInit(adapter);
adapter.visitLabel(begin);

expr.writeOut(bc, Expression.MODE_VALUE);
adapter.ifZCmp(Opcodes.IFEQ, end);

body.writeOut(bc);
adapter.visitJumpInsn(Opcodes.GOTO, begin);
InterruptHandlerInjector.writeLoopBodyEnd(adapter, loopCounter, begin, "during while loop");

adapter.visitLabel(end);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/**
* The goal of this helper is mostly to add a hooked bytecode to allow safe interruption of threads avoiding
* Tomcat ThreadDeath and the thread.stop() codepath
*/
package lucee.transformer.bytecode.util;

import org.objectweb.asm.Label;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;

import org.objectweb.asm.commons.GeneratorAdapter;
import org.objectweb.asm.commons.Method;
import lucee.transformer.bytecode.util.Types;

public final class InterruptHandlerInjector {
private static final Type TYPE_THREAD = Type.getType(Thread.class);
private static final Type TYPE_EXCEPTION = Type.getType(InterruptedException.class);
private static final Method METHOD_INTERRUPTED = new Method("interrupted", Type.BOOLEAN_TYPE, new Type[] {});

public static int writeLoopInit(GeneratorAdapter adapter) {
final int toIt = adapter.newLocal(Types.ITERATOR);
adapter.push(0);
adapter.storeLocal(toIt, Type.INT_TYPE);
return toIt;
}

public static void writeLoopBodyEnd(GeneratorAdapter adapter, int iteratorRef, Label jumpLabel, String timeoutDescription) {
// count the loop, only check for interruptions once every 10K iterations
adapter.iinc(iteratorRef, 1);
adapter.loadLocal(iteratorRef);
adapter.push(10000);
adapter.ifICmp(Opcodes.IFLT, jumpLabel);
// reset counter
adapter.push(0);
adapter.storeLocal(iteratorRef);
// Check if the thread is interrupted
writePreempt(adapter, jumpLabel, timeoutDescription);
}

public static void writeEndPreempt(GeneratorAdapter adapter, String timeoutDescription) {
Label endPreempt = new Label();
writePreempt(adapter, endPreempt, timeoutDescription);
adapter.visitLabel(endPreempt);
}

public static void writePreempt(GeneratorAdapter adapter, Label label, String timeoutDescription) {
// Check if the thread is interrupted
adapter.invokeStatic(TYPE_THREAD, METHOD_INTERRUPTED);
// Thread hasn't been interrupted, go to endPreempt
adapter.ifZCmp(Opcodes.IFEQ, label);
// Thread interrupted, throw Interrupted Exception
adapter.throwException(TYPE_EXCEPTION, "Timeout Exception ".concat(timeoutDescription));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import org.objectweb.asm.commons.GeneratorAdapter;

import lucee.transformer.bytecode.BytecodeContext;
import lucee.transformer.bytecode.util.InterruptHandlerInjector;

// TODO testen wurde noch nicht getestet

Expand All @@ -31,21 +32,25 @@ public final class DoWhileVisitor implements LoopVisitor {
private Label begin;
private Label end;
private Label beforeEnd;
private int loopCounter;

public void visitBeginBody(GeneratorAdapter mv) {
end = new Label();
beforeEnd = new Label();

begin = new Label();
loopCounter = InterruptHandlerInjector.writeLoopInit(mv);
mv.visitLabel(begin);
}

public void visitEndBodyBeginExpr(GeneratorAdapter mv) {
InterruptHandlerInjector.writeLoopBodyEnd(mv, loopCounter, beforeEnd, "during do while");
mv.visitLabel(beforeEnd);
}

public void visitEndExpr(GeneratorAdapter mv) {
mv.ifZCmp(Opcodes.IFNE, begin);
InterruptHandlerInjector.writePreempt(mv, end, "after do while");
mv.visitLabel(end);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import lucee.transformer.Position;
import lucee.transformer.bytecode.BytecodeContext;
import lucee.transformer.bytecode.util.Types;
import lucee.transformer.bytecode.util.InterruptHandlerInjector;

public final class ForDoubleVisitor implements Opcodes, LoopVisitor {

Expand All @@ -33,9 +34,11 @@ public final class ForDoubleVisitor implements Opcodes, LoopVisitor {
public Label beforeBody = new Label(), afterBody = new Label();
public Label beforeUpdate = new Label(), afterUpdate = new Label();
public int i;
private int loopCounter;

public int visitBeforeExpression(GeneratorAdapter adapter, int start, int step, boolean isLocal) {
// init
loopCounter = InterruptHandlerInjector.writeLoopInit(adapter);
adapter.visitLabel(beforeInit);
forInit(adapter, start, isLocal);
adapter.goTo(beforeExpr);
Expand Down Expand Up @@ -75,6 +78,8 @@ public void forUpdate(GeneratorAdapter adapter, int step, boolean isLocal) {
adapter.visitVarInsn(DSTORE, i);
}
else adapter.visitIincInsn(i, step);

InterruptHandlerInjector.writeLoopBodyEnd(adapter, loopCounter, beforeExpr, "during for loop");
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import lucee.transformer.Position;
import lucee.transformer.bytecode.BytecodeContext;
import lucee.transformer.bytecode.util.Types;
import lucee.transformer.bytecode.util.InterruptHandlerInjector;

public final class ForIntVisitor implements Opcodes, LoopVisitor {

Expand All @@ -33,9 +34,11 @@ public final class ForIntVisitor implements Opcodes, LoopVisitor {
private Label beforeBody = new Label(), afterBody = new Label();
private Label beforeUpdate = new Label(), afterUpdate = new Label();
private int i;
private int loopCounter;

public int visitBeforeExpression(GeneratorAdapter adapter, int start, int step, boolean isLocal) {
// init
loopCounter = InterruptHandlerInjector.writeLoopInit(adapter);
adapter.visitLabel(beforeInit);
forInit(adapter, start, isLocal);
adapter.goTo(beforeExpr);
Expand Down Expand Up @@ -75,6 +78,8 @@ private void forUpdate(GeneratorAdapter adapter, int step, boolean isLocal) {
adapter.visitVarInsn(ISTORE, i);
}
else adapter.visitIincInsn(i, step);

InterruptHandlerInjector.writeLoopBodyEnd(adapter, loopCounter, beforeExpr, "during for loop");
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import lucee.transformer.Position;
import lucee.transformer.bytecode.BytecodeContext;
import lucee.transformer.bytecode.util.Types;
import lucee.transformer.bytecode.util.InterruptHandlerInjector;

public final class ForVisitor implements Opcodes, LoopVisitor {

Expand All @@ -35,8 +36,11 @@ public final class ForVisitor implements Opcodes, LoopVisitor {
private int i;
private Label lend = new Label();
private Label lbegin = new Label();
private int loopCounter;

public int visitBegin(GeneratorAdapter adapter, int start, boolean isLocal) {
loopCounter = InterruptHandlerInjector.writeLoopInit(adapter);

adapter.visitLabel(l0);

forInit(adapter, start, isLocal);
Expand Down Expand Up @@ -77,6 +81,8 @@ private void forInit(GeneratorAdapter adapter, int start, boolean isLocal) {
if (isLocal) adapter.loadLocal(start);
else adapter.push(start);
adapter.visitVarInsn(ISTORE, i);

InterruptHandlerInjector.writeLoopBodyEnd(adapter, loopCounter, l1, "during for loop");
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,19 @@

import lucee.transformer.Position;
import lucee.transformer.bytecode.BytecodeContext;
import lucee.transformer.bytecode.util.InterruptHandlerInjector;

public final class WhileVisitor implements LoopVisitor {

private Label begin;
private Label end;
private int loopCounter;

public void visitBeforeExpression(BytecodeContext bc) {
begin = new Label();
end = new Label();
bc.getAdapter().visitLabel(begin);
loopCounter = InterruptHandlerInjector.writeLoopInit(bc.getAdapter());
}

public void visitAfterExpressionBeforeBody(BytecodeContext bc) {
Expand All @@ -41,6 +44,8 @@ public void visitAfterExpressionBeforeBody(BytecodeContext bc) {

public void visitAfterBody(BytecodeContext bc, Position endline) {
bc.getAdapter().visitJumpInsn(Opcodes.GOTO, begin);

InterruptHandlerInjector.writeLoopBodyEnd(bc.getAdapter(), loopCounter, end, "during while loop");
bc.getAdapter().visitLabel(end);
bc.visitLine(endline);
}
Expand Down
Loading