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 1a82952d48f8..112832b002bf 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 @@ -455,7 +455,6 @@ public void unblockStrand(Strand strand) { } private void cleanUp(Strand justCompleted) { - justCompleted.scheduler = null; justCompleted.frames = null; justCompleted.waitingContexts = null; diff --git a/tests/ballerina-test-utils/src/main/java/org/ballerinalang/test/BRunUtil.java b/tests/ballerina-test-utils/src/main/java/org/ballerinalang/test/BRunUtil.java index 3be624a79cdb..dfc7680ac7e7 100644 --- a/tests/ballerina-test-utils/src/main/java/org/ballerinalang/test/BRunUtil.java +++ b/tests/ballerina-test-utils/src/main/java/org/ballerinalang/test/BRunUtil.java @@ -373,8 +373,8 @@ public static void runInit(CompileResult compileResult) throws ClassNotFoundExce directRun(compileResult.getClassLoader().loadClass(configClassName), "$configureInit", new Class[]{String[].class, Path[].class, String.class}, new Object[]{new String[]{}, configurationDetails.paths, configurationDetails.configContent}); - runOnSchedule(initClazz, ASTBuilderUtil.createIdentifier(null, "$moduleInit"), scheduler); - runOnSchedule(initClazz, ASTBuilderUtil.createIdentifier(null, "$moduleStart"), scheduler); + runOnSchedule(initClazz, "$moduleInit", scheduler); + runOnSchedule(initClazz, "$moduleStart", scheduler); // if (temp) { // scheduler.immortal = true; // new Thread(scheduler::start).start(); @@ -397,6 +397,26 @@ private static void directRun(Class initClazz, String functionName, Class[] p } } + public static void runOnSchedule(CompileResult compileResult, String functionName, Scheduler scheduler) { + BIRNode.BIRFunction function = getInvokedFunction(compileResult, functionName); + PackageManifest packageManifest = compileResult.packageManifest(); + String funcClassName = JarResolver.getQualifiedClassName(packageManifest.org().toString(), + packageManifest.name().toString(), + packageManifest.version().toString(), + getClassName(function.pos.lineRange().fileName())); + Class funcClass = null; + try { + funcClass = compileResult.getClassLoader().loadClass(funcClassName); + } catch (ClassNotFoundException e) { + throw new RuntimeException("Error while invoking function '" + functionName + "'", e); + } + runOnSchedule(funcClass, functionName, scheduler); + } + + private static void runOnSchedule(Class initClazz, String name, Scheduler scheduler) { + runOnSchedule(initClazz, ASTBuilderUtil.createIdentifier(null, name), scheduler); + } + private static void runOnSchedule(Class initClazz, BLangIdentifier name, Scheduler scheduler) { String funcName = JvmCodeGenUtil.cleanupFunctionName(name.value); try { @@ -418,6 +438,7 @@ private static void runOnSchedule(Class initClazz, BLangIdentifier name, Sche }; final FutureValue out = scheduler .schedule(new Object[1], func, null, null, null, PredefinedTypes.TYPE_ANY, null, null); + Scheduler.setDaemonStrand(out.strand); scheduler.start(); final Throwable t = out.panic; if (t != null) { diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/runtime/api/RuntimeAPITest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/runtime/api/RuntimeAPITest.java index 7ca9edaca1d8..83e50996f2a2 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/runtime/api/RuntimeAPITest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/runtime/api/RuntimeAPITest.java @@ -17,12 +17,21 @@ */ package org.ballerinalang.test.runtime.api; +import io.ballerina.runtime.api.Module; +import io.ballerina.runtime.api.creators.ValueCreator; +import io.ballerina.runtime.api.utils.StringUtils; +import io.ballerina.runtime.api.values.BMap; +import io.ballerina.runtime.api.values.BString; +import io.ballerina.runtime.internal.scheduling.Scheduler; import org.ballerinalang.test.BCompileUtil; import org.ballerinalang.test.BRunUtil; import org.ballerinalang.test.CompileResult; +import org.testng.Assert; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; +import java.util.concurrent.atomic.AtomicReference; + /** * Test cases for runtime api. * @@ -49,4 +58,41 @@ public Object[] packageNameProvider() { "environment" }; } + + @Test + public void testRecordNoStrandDefaultValue() { + CompileResult strandResult = BCompileUtil.compile("test-src/runtime/api/no_strand"); + final Scheduler scheduler = new Scheduler(false); + AtomicReference exceptionRef = new AtomicReference<>(); + Thread thread1 = new Thread(() -> { + BRunUtil.runOnSchedule(strandResult, "main", scheduler); + }); + Thread thread2 = new Thread(() -> { + try { + Thread.sleep(1000); + BMap recordValue = ValueCreator.createRecordValue(new Module("testorg", + "no_strand", "1"), "MutualSslHandshake"); + Assert.assertEquals(recordValue.getType().getName(), "MutualSslHandshake"); + Assert.assertEquals(recordValue.get(StringUtils.fromString("status")), + StringUtils.fromString("passed")); + Assert.assertNull(recordValue.get(StringUtils.fromString("base64EncodedCert"))); + } catch (Throwable e) { + exceptionRef.set(e); + } finally { + scheduler.poison(); + } + }); + try { + thread1.start(); + thread2.start(); + thread1.join(); + thread2.join(); + Throwable storedException = exceptionRef.get(); + if (storedException != null) { + throw new AssertionError("Test failed due to an exception in a thread", storedException); + } + } catch (InterruptedException e) { + throw new RuntimeException("Error while invoking function 'main'", e); + } + } } diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/runtime/api/no_strand/Ballerina.toml b/tests/jballerina-unit-test/src/test/resources/test-src/runtime/api/no_strand/Ballerina.toml new file mode 100644 index 000000000000..7c01db43e064 --- /dev/null +++ b/tests/jballerina-unit-test/src/test/resources/test-src/runtime/api/no_strand/Ballerina.toml @@ -0,0 +1,4 @@ +[package] +org= "testorg" +name="no_strand" +version= "1.0.0" diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/runtime/api/no_strand/main.bal b/tests/jballerina-unit-test/src/test/resources/test-src/runtime/api/no_strand/main.bal new file mode 100644 index 000000000000..96d3a1c5e4e8 --- /dev/null +++ b/tests/jballerina-unit-test/src/test/resources/test-src/runtime/api/no_strand/main.bal @@ -0,0 +1,55 @@ +// Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). +// +// 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. + +import ballerina/lang.runtime; + +public class Listener { + + private string name = ""; + + public function init(string name){ + self.name = name; + } + + public function 'start() returns error? { + } + + public function gracefulStop() returns error? { + } + + public function immediateStop() returns error? { + } + + public function attach(service object {} s, string[]|string? name = ()) returns error? { + } + + public function detach(service object {} s) returns error? { + } +} + +public function main() { + Listener l = new("TestListener"); + runtime:registerListener(l); +} + +public type MutualSslHandshake record {| + MutualSslStatus status = PASSED; + string? base64EncodedCert = (); +|}; + +public type MutualSslStatus PASSED|FAILED|(); +public const PASSED = "passed"; +public const FAILED = "failed";