diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/launch/LaunchUtils.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/launch/LaunchUtils.java index 151b80e71162..a0d8e5b07df7 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/launch/LaunchUtils.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/launch/LaunchUtils.java @@ -29,10 +29,13 @@ import io.ballerina.runtime.internal.configurable.providers.toml.TomlDetails; import io.ballerina.runtime.internal.configurable.providers.toml.TomlFileProvider; import io.ballerina.runtime.internal.diagnostics.RuntimeDiagnosticLog; +import io.ballerina.runtime.internal.troubleshoot.StrandDump; import io.ballerina.runtime.internal.util.RuntimeUtils; import org.apache.commons.lang3.ArrayUtils; +import sun.misc.Signal; import java.io.File; +import java.io.PrintStream; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; @@ -54,9 +57,24 @@ */ public class LaunchUtils { + private static final PrintStream outStream = System.out; + private LaunchUtils() { } + public static void startListenersAndSignalHandler(boolean isService) { + // starts all listeners + startListeners(isService); + + // start TRAP signal handler which produces the strand dump + try { + Signal.handle(new Signal("TRAP"), signal -> outStream.println(StrandDump.getStrandDump())); + } catch (IllegalArgumentException ignored) { + // In some Operating Systems like Windows, "TRAP" POSIX signal is not supported. + // There getting the strand dump using kill signals is not expected, hence this exception is ignored. + } + } + public static void startListeners(boolean isService) { ServiceLoader listeners = ServiceLoader.load(LaunchListener.class); listeners.forEach(listener -> listener.beforeRunProgram(isService)); diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/scheduling/FunctionFrame.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/scheduling/FunctionFrame.java new file mode 100644 index 000000000000..bef520f39ac3 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/scheduling/FunctionFrame.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.internal.scheduling; + +/** + * This interface represents the function frame which saves the existing + * state when a function yields. + * + * @since 2201.2.0 + */ +public interface FunctionFrame { + + String getYieldLocation(); + + String getYieldStatus(); + +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/scheduling/Scheduler.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/scheduling/Scheduler.java index e008a2ad83c1..43670d4a8d78 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/scheduling/Scheduler.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/scheduling/Scheduler.java @@ -32,11 +32,13 @@ import io.ballerina.runtime.internal.values.FutureValue; import java.io.PrintStream; +import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.Stack; import java.util.concurrent.BlockingQueue; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.LinkedBlockingDeque; import java.util.concurrent.Semaphore; import java.util.concurrent.atomic.AtomicBoolean; @@ -55,7 +57,7 @@ */ public class Scheduler { - private static PrintStream err = System.err; + private static final PrintStream err = System.err; /** * Scheduler does not get killed if the immortal value is true. Specific to services. @@ -65,12 +67,13 @@ public class Scheduler { /** * Strands that are ready for execution. */ - private BlockingQueue runnableList = new LinkedBlockingDeque<>(); + private final BlockingQueue runnableList = new LinkedBlockingDeque<>(); private static final ThreadLocal strandHolder = ThreadLocal.withInitial(StrandHolder::new); + private static final ConcurrentHashMap currentStrands = new ConcurrentHashMap<>(); private final Strand previousStrand; - private AtomicInteger totalStrands = new AtomicInteger(); + private final AtomicInteger totalStrands = new AtomicInteger(); private static String poolSizeConf = System.getenv(RuntimeConstants.BALLERINA_MAX_POOL_SIZE_ENV_VAR); @@ -112,6 +115,10 @@ public static Strand getStrandNoException() { return strandHolder.get().strand; } + public static Map getCurrentStrands() { + return new HashMap<>(currentStrands); + } + /** * Schedules given function by creating a new strand group. * @@ -456,6 +463,8 @@ private void cleanUp(Strand justCompleted) { justCompleted.scheduler = null; justCompleted.frames = null; justCompleted.waitingContexts = null; + + currentStrands.remove(justCompleted.getId()); //TODO: more cleanup , eg channels } @@ -503,6 +512,7 @@ private void addToRunnableList(SchedulerItem item, ItemGroup group) { public FutureValue createFuture(Strand parent, Callback callback, Map properties, Type constraint, String name, StrandMetadata metadata) { Strand newStrand = new Strand(name, metadata, this, parent, properties); + currentStrands.put(newStrand.getId(), newStrand); return createFuture(parent, callback, constraint, newStrand); } @@ -510,6 +520,7 @@ public FutureValue createTransactionalFuture(Strand parent, Callback callback, M Type constraint, String name, StrandMetadata metadata) { Strand newStrand = new Strand(name, metadata, this, parent, properties, parent != null ? parent.currentTrxContext : null); + currentStrands.put(newStrand.getId(), newStrand); return createFuture(parent, callback, constraint, newStrand); } @@ -633,6 +644,10 @@ public String toString() { */ class ItemGroup { + private static final AtomicInteger nextItemGroupId = new AtomicInteger(0); + + private final int id; + /** * Keep the list of items that should run on same thread. * Using a stack to get advantage of the locality. @@ -649,10 +664,12 @@ class ItemGroup { public static final ItemGroup POISON_PILL = new ItemGroup(); public ItemGroup(SchedulerItem item) { + this(); items.push(item); } public ItemGroup() { + this.id = nextItemGroupId.incrementAndGet(); } public void add(SchedulerItem item) { @@ -670,4 +687,12 @@ public void lock() { public void unlock() { this.groupLock.unlock(); } + + public int getId() { + return id; + } + + public boolean isScheduled() { + return scheduled.get(); + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/scheduling/Strand.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/scheduling/Strand.java index 057cfc5a2959..f6005eb10812 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/scheduling/Strand.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/scheduling/Strand.java @@ -32,6 +32,7 @@ import java.util.ArrayList; import java.util.Collections; +import java.util.ConcurrentModificationException; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -46,6 +47,7 @@ import static io.ballerina.runtime.api.constants.RuntimeConstants.CURRENT_TRANSACTION_CONTEXT_PROPERTY; import static io.ballerina.runtime.internal.scheduling.State.BLOCK_AND_YIELD; import static io.ballerina.runtime.internal.scheduling.State.BLOCK_ON_AND_YIELD; +import static io.ballerina.runtime.internal.scheduling.State.DONE; import static io.ballerina.runtime.internal.scheduling.State.RUNNABLE; import static io.ballerina.runtime.internal.scheduling.State.YIELD; @@ -57,13 +59,13 @@ public class Strand { - private static AtomicInteger nextStrandId = new AtomicInteger(0); + private static final AtomicInteger nextStrandId = new AtomicInteger(0); - private int id; - private String name; - private StrandMetadata metadata; + private final int id; + private final String name; + private final StrandMetadata metadata; - public Stack frames; + public Stack frames; public int resumeIndex; public Object returnValue; public BError panic; @@ -397,6 +399,14 @@ public int getId() { return id; } + public int getStrandGroupId() { + return strandGroup.getId(); + } + + public boolean isStrandGroupScheduled() { + return strandGroup.isScheduled(); + } + /** * Gets the strand name. This will be optional. Strand name can be either name given in strand annotation or async * call or function pointer variable name. @@ -416,6 +426,70 @@ public StrandMetadata getMetadata() { return metadata; } + public String dumpState() { + StringBuilder strandInfo = new StringBuilder("\tstrand " + this.id); + if (this.name != null && this.getName().isPresent()) { + strandInfo.append(" \"").append(this.getName().get()).append("\""); + } + + strandInfo.append(" ["); + strandInfo.append(this.metadata.getModuleOrg()).append(".").append(this.metadata.getModuleName()).append(".") + .append(this.metadata.getModuleVersion()).append(":").append(this.metadata.getParentFunctionName()); + if (this.parent != null) { + strandInfo.append("][").append(this.parent.getId()); + } + strandInfo.append("] ["); + + String closingBracketWithNewLines = "]\n\n"; + if (this.isYielded()) { + getInfoFromYieldedState(strandInfo, closingBracketWithNewLines); + } else if (this.getState().equals(DONE)) { + strandInfo.append(DONE).append(closingBracketWithNewLines); + } else { + strandInfo.append(RUNNABLE).append(closingBracketWithNewLines); + } + + return strandInfo.toString(); + } + + private void getInfoFromYieldedState(StringBuilder strandInfo, String closingBracketWithNewLines) { + Stack strandFrames = this.frames; + if ((strandFrames == null) || (strandFrames.isEmpty())) { + // this means the strand frames is changed, hence the state is runnable + strandInfo.append(RUNNABLE).append(closingBracketWithNewLines); + return; + } + + StringBuilder frameStackTrace = new StringBuilder(); + String stringPrefix = "\t\tat\t"; + String yieldStatus = "BLOCKED"; + boolean noPickedYieldStatus = true; + try { + for (FunctionFrame frame : strandFrames) { + if (noPickedYieldStatus) { + yieldStatus = frame.getYieldStatus(); + noPickedYieldStatus = false; + } + String yieldLocation = frame.getYieldLocation(); + frameStackTrace.append(stringPrefix).append(yieldLocation); + frameStackTrace.append("\n"); + stringPrefix = "\t\t \t"; + } + } catch (ConcurrentModificationException ce) { + // this exception can be thrown when frames get added or removed while it is being iterated + // that means now the strand state is changed from yielded state to runnable state + strandInfo.append(RUNNABLE).append(closingBracketWithNewLines); + return; + } + if (!this.isYielded() || noPickedYieldStatus) { + // if frames have got empty, noPickedYieldStatus is true, then the state has changed to runnable + strandInfo.append(RUNNABLE).append(closingBracketWithNewLines); + return; + } + strandInfo.append(yieldStatus).append("]:\n"); + strandInfo.append(frameStackTrace).append("\n"); + } + /** * Class to hold flush action related details. * diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/troubleshoot/StrandDump.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/troubleshoot/StrandDump.java new file mode 100644 index 000000000000..7fcdb1041750 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/troubleshoot/StrandDump.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.internal.troubleshoot; + +import io.ballerina.runtime.internal.scheduling.Scheduler; +import io.ballerina.runtime.internal.scheduling.Strand; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Used to get the status of current Ballerina strands. + * + * @since 2201.2.0 + */ +public class StrandDump { + + public static String getStrandDump() { + Map availableStrands = Scheduler.getCurrentStrands(); + int availableStrandCount = availableStrands.size(); + Map> availableStrandGroups = new HashMap<>(); + populateAvailableStrandGroups(availableStrands, availableStrandGroups); + + StringBuilder infoStr = new StringBuilder("Ballerina Strand Dump ["); + DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss"); + LocalDateTime localDateTime = LocalDateTime.now(); + infoStr.append(dateTimeFormatter.format(localDateTime)); + infoStr.append("]\n===========================================\n\n"); + infoStr.append("Current no. of strand groups\t:\t").append(availableStrandGroups.size()).append("\n"); + infoStr.append("Current no. of strands \t:\t").append(availableStrandCount).append("\n\n"); + availableStrandGroups.forEach((strandGroupId, strandList) -> { + infoStr.append("group ").append(strandGroupId).append(" [").append(strandList.get(0)).append("]: [") + .append(strandList.size() - 1).append("]\n"); + strandList.subList(1, strandList.size()).forEach(infoStr::append); + }); + infoStr.append("===========================================\n"); + cleanUp(availableStrands, availableStrandGroups); + return infoStr.toString(); + } + + private static void populateAvailableStrandGroups(Map availableStrands, + Map> availableStrandGroups) { + for (Strand strand : availableStrands.values()) { + int strandGroupId = strand.getStrandGroupId(); + String strandState = strand.dumpState(); + availableStrandGroups.computeIfAbsent(strandGroupId, k -> { + ArrayList strandDataList = new ArrayList<>(); + strandDataList.add(getStrandGroupStatus(strand.isStrandGroupScheduled())); + return strandDataList; + }).add(strandState); + } + } + + private static void cleanUp(Map availableStrands, + Map> availableStrandGroups) { + availableStrands.clear(); + availableStrandGroups.clear(); + } + + private static String getStrandGroupStatus(boolean isStrandGroupScheduled) { + if (isStrandGroupScheduled) { + return "RUNNABLE"; + } else { + return "QUEUED"; + } + } + + private StrandDump() {} + } diff --git a/bvm/ballerina-runtime/src/main/java/module-info.java b/bvm/ballerina-runtime/src/main/java/module-info.java index 29650f0b24e0..0a4c158df500 100644 --- a/bvm/ballerina-runtime/src/main/java/module-info.java +++ b/bvm/ballerina-runtime/src/main/java/module-info.java @@ -18,6 +18,7 @@ requires java.naming; requires org.apache.commons.lang3; requires io.ballerina.identifier; + requires jdk.unsupported; // API exports exports io.ballerina.runtime.api; diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmCodeGenUtil.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmCodeGenUtil.java index 858a12cdb442..7acb00ea8f59 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmCodeGenUtil.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmCodeGenUtil.java @@ -70,7 +70,7 @@ import static org.objectweb.asm.Opcodes.GOTO; import static org.objectweb.asm.Opcodes.ICONST_0; import static org.objectweb.asm.Opcodes.ICONST_1; -import static org.objectweb.asm.Opcodes.IFNE; +import static org.objectweb.asm.Opcodes.IFEQ; import static org.objectweb.asm.Opcodes.INVOKEINTERFACE; import static org.objectweb.asm.Opcodes.INVOKESPECIAL; import static org.objectweb.asm.Opcodes.INVOKESTATIC; @@ -617,17 +617,43 @@ public static BirScope getLastScopeFromTerminator(MethodVisitor mv, BIRNode.BIRB } public static void genYieldCheck(MethodVisitor mv, LabelGenerator labelGen, BIRNode.BIRBasicBlock thenBB, - String funcName, int localVarOffset) { + String funcName, int localVarOffset, int yieldLocationVarIndex, + Location terminatorPos, String fullyQualifiedFuncName, + String yieldStatus, int yieldStatusVarIndex) { mv.visitVarInsn(ALOAD, localVarOffset); mv.visitMethodInsn(INVOKEVIRTUAL, STRAND_CLASS, "isYielded", "()Z", false); - Label yieldLabel = labelGen.getLabel(funcName + "yield"); - mv.visitJumpInsn(IFNE, yieldLabel); + generateSetYieldedStatus(mv, labelGen, funcName, yieldLocationVarIndex, terminatorPos, + fullyQualifiedFuncName, yieldStatus, yieldStatusVarIndex); // goto thenBB Label gotoLabel = labelGen.getLabel(funcName + thenBB.id.value); mv.visitJumpInsn(GOTO, gotoLabel); } + protected static void generateSetYieldedStatus(MethodVisitor mv, LabelGenerator labelGen, String funcName, + int yieldLocationVarIndex, Location terminatorPos, + String fullyQualifiedFuncName, String yieldStatus, + int yieldStatusVarIndex) { + Label yieldLocationLabel = new Label(); + mv.visitJumpInsn(IFEQ, yieldLocationLabel); + + if (yieldLocationVarIndex != -1) { + StringBuilder yieldLocationData = new StringBuilder(fullyQualifiedFuncName); + if (terminatorPos != null) { + yieldLocationData.append("(").append(terminatorPos.lineRange().filePath()).append(":") + .append(terminatorPos.lineRange().startLine().line() + 1).append(")"); + } + mv.visitLdcInsn(yieldLocationData.toString()); + mv.visitVarInsn(ASTORE, yieldLocationVarIndex); + mv.visitLdcInsn(yieldStatus); + mv.visitVarInsn(ASTORE, yieldStatusVarIndex); + } + + Label yieldLabel = labelGen.getLabel(funcName + "yield"); + mv.visitJumpInsn(GOTO, yieldLabel); + mv.visitLabel(yieldLocationLabel); + } + public static PackageID cleanupPackageID(PackageID pkgID) { Name org = new Name(Utils.encodeNonFunctionIdentifier(pkgID.orgName.value)); Name module = new Name(Utils.encodeNonFunctionIdentifier(pkgID.name.value)); diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmConstants.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmConstants.java index 6da410769f7a..e8a5a6b462e4 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmConstants.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmConstants.java @@ -183,6 +183,7 @@ public class JvmConstants { public static final String BAL_FUTURE = "io/ballerina/runtime/api/Future"; public static final String TYPE_CONVERTER = "io/ballerina/runtime/internal/TypeConverter"; public static final String STRAND_STATE = "io/ballerina/runtime/internal/scheduling/State"; + public static final String FUNCTION_FRAME = "io/ballerina/runtime/internal/scheduling/FunctionFrame"; public static final String VALUE_CREATOR = "io/ballerina/runtime/internal/values/ValueCreator"; public static final String XML_FACTORY = "io/ballerina/runtime/internal/XmlFactory"; public static final String XML_SEQUENCE = "io/ballerina/runtime/internal/values/XmlSequence"; @@ -367,6 +368,8 @@ public class JvmConstants { public static final String STRAND_VALUE_ANY = "any"; public static final String STRAND_METADATA_VAR_PREFIX = "$strand_metadata$"; public static final String DEFAULT_STRAND_DISPATCHER = "DEFAULT"; + public static final String YIELD_LOCATION = "yieldLocation"; + public static final String YIELD_STATUS = "yieldStatus"; // observability related constants public static final String OBSERVE_UTILS = "io/ballerina/runtime/observability/ObserveUtils"; @@ -410,8 +413,6 @@ public class JvmConstants { */ public static final int MAX_STRINGS_PER_METHOD = 5000; - public static final int BALLERINA_MAX_YIELD_DEPTH = 256; - private JvmConstants() { } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmTerminatorGen.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmTerminatorGen.java index 30aac774e033..8eb531caa396 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmTerminatorGen.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmTerminatorGen.java @@ -18,6 +18,7 @@ package org.wso2.ballerinalang.compiler.bir.codegen; import io.ballerina.identifier.Utils; +import io.ballerina.tools.diagnostics.Location; import org.ballerinalang.compiler.BLangCompilerException; import org.ballerinalang.model.elements.PackageID; import org.objectweb.asm.Label; @@ -76,7 +77,6 @@ import static org.objectweb.asm.Opcodes.ICONST_1; import static org.objectweb.asm.Opcodes.IFEQ; import static org.objectweb.asm.Opcodes.IFGT; -import static org.objectweb.asm.Opcodes.IFNE; import static org.objectweb.asm.Opcodes.IFNONNULL; import static org.objectweb.asm.Opcodes.IFNULL; import static org.objectweb.asm.Opcodes.ILOAD; @@ -222,12 +222,13 @@ public JvmTerminatorGen(MethodVisitor mv, BIRVarToJVMIndexMap indexMap, LabelGen } private static void genYieldCheckForLock(MethodVisitor mv, LabelGenerator labelGen, String funcName, - int localVarOffset) { + int localVarOffset, int yieldLocationVarIndex, int yieldStatusVarIndex, + String fullyQualifiedFuncName, Location terminatorPos) { mv.visitVarInsn(ALOAD, localVarOffset); mv.visitMethodInsn(INVOKEVIRTUAL, STRAND_CLASS, "isYielded", "()Z", false); - Label yieldLabel = labelGen.getLabel(funcName + "yield"); - mv.visitJumpInsn(IFNE, yieldLabel); + JvmCodeGenUtil.generateSetYieldedStatus(mv, labelGen, funcName, yieldLocationVarIndex, + terminatorPos, fullyQualifiedFuncName, "WAITING FOR LOCK", yieldStatusVarIndex); } private void loadDefaultValue(MethodVisitor mv, BType type) { @@ -317,11 +318,13 @@ private void loadDefaultJValue(MethodVisitor mv, JType jType) { } public void genTerminator(BIRTerminator terminator, String moduleClassName, BIRNode.BIRFunction func, - String funcName, int localVarOffset, int returnVarRefIndex, BType attachedType) { + String funcName, int localVarOffset, int returnVarRefIndex, BType attachedType, + int yieldLocationVarIndex, int yieldStatusVarIndex, String fullyQualifiedFuncName) { switch (terminator.kind) { case LOCK: - this.genLockTerm((BIRTerminator.Lock) terminator, funcName, localVarOffset); + this.genLockTerm((BIRTerminator.Lock) terminator, funcName, localVarOffset, yieldLocationVarIndex, + terminator.pos, fullyQualifiedFuncName, yieldStatusVarIndex); return; case UNLOCK: this.genUnlockTerm((BIRTerminator.Unlock) terminator, funcName); @@ -388,7 +391,8 @@ private void genGoToTerm(BIRTerminator.GOTO gotoIns, String funcName) { this.mv.visitJumpInsn(GOTO, gotoLabel); } - private void genLockTerm(BIRTerminator.Lock lockIns, String funcName, int localVarOffset) { + private void genLockTerm(BIRTerminator.Lock lockIns, String funcName, int localVarOffset, int yieldLocationVarIndex, + Location terminatorPos, String fullyQualifiedFuncName, int yieldStatusVarIndex) { Label gotoLabel = this.labelGen.getLabel(funcName + lockIns.lockedBB.id.value); String lockStore = "L" + LOCK_STORE + ";"; @@ -400,7 +404,8 @@ private void genLockTerm(BIRTerminator.Lock lockIns, String funcName, int localV this.mv.visitVarInsn(ALOAD, localVarOffset); this.mv.visitMethodInsn(INVOKEVIRTUAL, LOCK_VALUE, "lock", LOCK, false); this.mv.visitInsn(POP); - genYieldCheckForLock(this.mv, this.labelGen, funcName, localVarOffset); + genYieldCheckForLock(this.mv, this.labelGen, funcName, localVarOffset, yieldLocationVarIndex, + yieldStatusVarIndex, fullyQualifiedFuncName, terminatorPos); this.mv.visitJumpInsn(GOTO, gotoLabel); } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/interop/InteropMethodGen.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/interop/InteropMethodGen.java index 96984da66fee..ccda4bd722e1 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/interop/InteropMethodGen.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/interop/InteropMethodGen.java @@ -301,7 +301,7 @@ private static void generateBasicBlocks(MethodVisitor mv, List ba // process terminator if (!(terminator instanceof BIRTerminator.Return)) { JvmCodeGenUtil.generateDiagnosticPos(terminator.pos, mv); - termGen.genTerminator(terminator, moduleClassName, func, funcName, -1, -1, null); + termGen.genTerminator(terminator, moduleClassName, func, funcName, -1, -1, null, -1, -1, null); lastScope = JvmCodeGenUtil.getLastScopeFromTerminator(mv, basicBlock, funcName, labelGen, lastScope, visitedScopesSet); } @@ -309,7 +309,8 @@ private static void generateBasicBlocks(MethodVisitor mv, List ba BIRBasicBlock thenBB = terminator.thenBB; if (thenBB != null) { - JvmCodeGenUtil.genYieldCheck(mv, termGen.getLabelGenerator(), thenBB, funcName, -1); + JvmCodeGenUtil.genYieldCheck(mv, termGen.getLabelGenerator(), thenBB, funcName, -1, -1, + terminator.pos, null, null, -1); } } } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/methodgen/FrameClassGen.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/methodgen/FrameClassGen.java index 7e2469652146..8a4487304835 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/methodgen/FrameClassGen.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/methodgen/FrameClassGen.java @@ -21,6 +21,7 @@ import org.ballerinalang.model.elements.PackageID; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.FieldVisitor; +import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; import org.wso2.ballerinalang.compiler.bir.codegen.BallerinaClassWriter; import org.wso2.ballerinalang.compiler.bir.codegen.JvmCodeGenUtil; @@ -34,7 +35,12 @@ import static org.objectweb.asm.ClassWriter.COMPUTE_FRAMES; import static org.objectweb.asm.Opcodes.ACC_SUPER; import static org.objectweb.asm.Opcodes.V1_8; +import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.FUNCTION_FRAME; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.OBJECT; +import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.YIELD_LOCATION; +import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.YIELD_STATUS; +import static org.wso2.ballerinalang.compiler.bir.codegen.JvmSignatures.GET_JSTRING; +import static org.wso2.ballerinalang.compiler.bir.codegen.JvmSignatures.GET_STRING; /** * Generates Jvm byte code for the generateFrame classes. @@ -75,7 +81,8 @@ private void generateFrameClassForFunction(PackageID packageID, BIRNode.BIRFunct if (func.pos != null && func.pos.lineRange().filePath() != null) { cw.visitSource(func.pos.lineRange().filePath(), null); } - cw.visit(V1_8, Opcodes.ACC_PUBLIC + ACC_SUPER, frameClassName, null, OBJECT, null); + cw.visit(V1_8, Opcodes.ACC_PUBLIC + ACC_SUPER, frameClassName, null, OBJECT, + new String[]{FUNCTION_FRAME}); JvmCodeGenUtil.generateDefaultConstructor(cw, OBJECT); int k = 0; @@ -95,6 +102,13 @@ private void generateFrameClassForFunction(PackageID packageID, BIRNode.BIRFunct FieldVisitor fv = cw.visitField(Opcodes.ACC_PUBLIC, "state", "I", null, null); fv.visitEnd(); + fv = cw.visitField(Opcodes.ACC_PUBLIC, YIELD_LOCATION, GET_STRING, null, null); + fv.visitEnd(); + fv = cw.visitField(Opcodes.ACC_PUBLIC, YIELD_STATUS, GET_STRING, null, null); + fv.visitEnd(); + + generateGetStringFieldMethod(cw, frameClassName, "getYieldLocation", YIELD_LOCATION); + generateGetStringFieldMethod(cw, frameClassName, "getYieldStatus", YIELD_STATUS); cw.visitEnd(); @@ -103,4 +117,15 @@ private void generateFrameClassForFunction(PackageID packageID, BIRNode.BIRFunct pkgEntries.put(frameClassName + ".class", cw.toByteArray()); } + private void generateGetStringFieldMethod(ClassWriter cw, String frameClassName, String methodName, + String fieldName) { + MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, methodName, GET_JSTRING, null, null); + mv.visitCode(); + mv.visitVarInsn(Opcodes.ALOAD, 0); + mv.visitFieldInsn(Opcodes.GETFIELD, frameClassName, fieldName, GET_STRING); + mv.visitInsn(Opcodes.ARETURN); + mv.visitMaxs(1, 1); + mv.visitEnd(); + } + } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/methodgen/MainMethodGen.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/methodgen/MainMethodGen.java index 2f3f7ba26fb9..7dbdb17edc4d 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/methodgen/MainMethodGen.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/methodgen/MainMethodGen.java @@ -149,8 +149,8 @@ public void generateMainMethod(BIRNode.BIRFunction userMainFunc, ClassWriter cw, generateJavaCompatibilityCheck(mv); invokeConfigInit(mv, pkg.packageID); - // start all listeners - startListeners(mv, serviceEPAvailable); + // start all listeners and TRAP signal handler + startListenersAndSignalHandler(mv, serviceEPAvailable); genInitScheduler(mv); // register a shutdown hook to call package stop() method. @@ -228,9 +228,9 @@ private String getJavaVersion() { return Objects.requireNonNullElse(javaVersion, ""); } - private void startListeners(MethodVisitor mv, boolean isServiceEPAvailable) { + private void startListenersAndSignalHandler(MethodVisitor mv, boolean isServiceEPAvailable) { mv.visitLdcInsn(isServiceEPAvailable); - mv.visitMethodInsn(INVOKESTATIC, LAUNCH_UTILS, "startListeners", "(Z)V", false); + mv.visitMethodInsn(INVOKESTATIC, LAUNCH_UTILS, "startListenersAndSignalHandler", "(Z)V", false); } private void genShutdownHook(MethodVisitor mv, String initClass) { diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/methodgen/MethodGen.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/methodgen/MethodGen.java index e25236440e31..c415e664427c 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/methodgen/MethodGen.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/methodgen/MethodGen.java @@ -52,6 +52,7 @@ import org.wso2.ballerinalang.compiler.bir.model.VarKind; import org.wso2.ballerinalang.compiler.semantics.analyzer.Types; import org.wso2.ballerinalang.compiler.semantics.model.SymbolTable; +import org.wso2.ballerinalang.compiler.semantics.model.symbols.BTypeSymbol; import org.wso2.ballerinalang.compiler.semantics.model.symbols.Symbols; import org.wso2.ballerinalang.compiler.semantics.model.types.BType; import org.wso2.ballerinalang.compiler.semantics.model.types.BTypeReferenceType; @@ -111,6 +112,8 @@ import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.STRAND; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.STRAND_CLASS; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.TYPEDESC_VALUE; +import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.YIELD_LOCATION; +import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.YIELD_STATUS; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmSignatures.CREATE_CANCELLED_FUTURE_ERROR; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmSignatures.GET_ARRAY_VALUE; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmSignatures.GET_BDECIMAL; @@ -206,6 +209,8 @@ public void genJMethodForBFunc(BIRFunction func, ClassWriter cw, BIRPackage modu int returnVarRefIndex = getReturnVarRefIndex(func, indexMap, retType, mv); int stateVarIndex = getStateVarIndex(indexMap, mv); + int yieldLocationVarIndex = getFrameStringVarIndex(indexMap, mv, YIELD_LOCATION); + int yieldStatusVarIndex = getFrameStringVarIndex(indexMap, mv, YIELD_STATUS); mv.visitVarInsn(ALOAD, localVarOffset); mv.visitFieldInsn(GETFIELD, STRAND_CLASS, RESUME_INDEX, "I"); @@ -234,8 +239,8 @@ public void genJMethodForBFunc(BIRFunction func, ClassWriter cw, BIRPackage modu Label yieldLable = labelGen.getLabel(funcName + "yield"); mv.visitLookupSwitchInsn(yieldLable, toIntArray(states), labels.toArray(new Label[0])); - generateBasicBlocks(mv, labelGen, errorGen, instGen, termGen, func, returnVarRefIndex, - stateVarIndex, localVarOffset, module, attachedType, moduleClassName); + generateBasicBlocks(mv, labelGen, errorGen, instGen, termGen, func, returnVarRefIndex, stateVarIndex, + yieldLocationVarIndex, yieldStatusVarIndex, localVarOffset, module, attachedType, moduleClassName); mv.visitLabel(resumeLabel); String frameName = MethodGenUtils.getFrameClassName(JvmCodeGenUtil.getPackageName(module.packageID), funcName, attachedType); @@ -256,6 +261,8 @@ public void genJMethodForBFunc(BIRFunction func, ClassWriter cw, BIRPackage modu mv.visitInsn(DUP); mv.visitVarInsn(ILOAD, stateVarIndex); mv.visitFieldInsn(PUTFIELD, frameName, STATE, "I"); + generateFrameStringFieldSet(mv, frameName, yieldLocationVarIndex, YIELD_LOCATION); + generateFrameStringFieldSet(mv, frameName, yieldStatusVarIndex, YIELD_STATUS); generateGetFrame(indexMap, localVarOffset, mv); @@ -270,6 +277,12 @@ public void genJMethodForBFunc(BIRFunction func, ClassWriter cw, BIRPackage modu mv.visitEnd(); } + private void generateFrameStringFieldSet(MethodVisitor mv, String frameName, int rhsVarIndex, String fieldName) { + mv.visitInsn(DUP); + mv.visitVarInsn(ALOAD, rhsVarIndex); + mv.visitFieldInsn(PUTFIELD, frameName, fieldName, GET_STRING); + } + private BType getReturnType(BIRFunction func) { BType retType = func.type.retType; if (JvmCodeGenUtil.isExternFunc(func) && Symbols.isFlagOn(retType.flags, Flags.PARAMETERIZED)) { @@ -428,6 +441,13 @@ private int getStateVarIndex(BIRVarToJVMIndexMap indexMap, MethodVisitor mv) { return stateVarIndex; } + private int getFrameStringVarIndex(BIRVarToJVMIndexMap indexMap, MethodVisitor mv, String frameStringFieldName) { + int stateVarIndex = indexMap.addIfNotExists(frameStringFieldName, symbolTable.stringType); + mv.visitInsn(ACONST_NULL); + mv.visitVarInsn(ASTORE, stateVarIndex); + return stateVarIndex; + } + private void addCasesForBasicBlocks(BIRFunction func, String funcName, LabelGenerator labelGen, List