diff --git a/make/CompileJavaClasses.gmk b/make/CompileJavaClasses.gmk index 10f5b533a585b328bfa068c0496acc425a344f98..188ccc1742fbf3cebac72cf2470bbeacf6535ba9 100644 --- a/make/CompileJavaClasses.gmk +++ b/make/CompileJavaClasses.gmk @@ -264,6 +264,11 @@ ifndef OPENJDK $(wildcard $(JDK_TOPDIR)/src/closed/$(OPENJDK_TARGET_OS_API_DIR)/classes) endif +LINUX_SRC_DIRS := +ifeq ($(OPENJDK_TARGET_OS), linux) + LINUX_SRC_DIRS += $(JDK_TOPDIR)/src/linux/classes +endif + MACOSX_SRC_DIRS := ifeq ($(OPENJDK_TARGET_OS), macosx) MACOSX_SRC_DIRS += $(JDK_TOPDIR)/src/macosx/classes @@ -328,6 +333,7 @@ $(eval $(call SetupJavaCompilation,BUILD_JDK,\ SRC:=$(JDK_TOPDIR)/src/share/classes \ $(JDK_TOPDIR)/src/$(OPENJDK_TARGET_OS_API_DIR)/classes \ $(MACOSX_SRC_DIRS) \ + $(LINUX_SRC_DIRS) \ $(AIX_SRC_DIRS) \ $(JDK_OUTPUTDIR)/gensrc \ $(JDK_OUTPUTDIR)/gensrc_no_srczip \ diff --git a/make/CreateJars.gmk b/make/CreateJars.gmk index 4fe038f64e036ede1b1b4339e9d53c44aa5656ee..12be408d8b42efc02654d15f8347b832637eda58 100644 --- a/make/CreateJars.gmk +++ b/make/CreateJars.gmk @@ -562,7 +562,8 @@ EXPORTED_PRIVATE_PKGS = com.oracle.net \ com.alibaba.management \ com.alibaba.jvm.gc \ com.alibaba.rcm \ - com.alibaba.tenant + com.alibaba.tenant \ + com.alibaba.wisp.engine \ $(IMAGES_OUTPUTDIR)/symbols/_the.symbols: $(IMAGES_OUTPUTDIR)/lib/rt.jar $(RM) -r $(IMAGES_OUTPUTDIR)/symbols/META-INF/sym diff --git a/make/data/classlist/classlist.linux b/make/data/classlist/classlist.linux index 86a2a150bec267ee48271596b14c2c8336156d62..9ab2398b60c69e31d64fe24f10b4526434de3548 100644 --- a/make/data/classlist/classlist.linux +++ b/make/data/classlist/classlist.linux @@ -2565,4 +2565,7 @@ com/alibaba/tenant/TenantException com/alibaba/tenant/TenantState com/alibaba/tenant/TenantGlobals com/alibaba/tenant/TenantContainerFactory +com/dyn/Coroutine +com/alibaba/wisp/engine/WispEngine +com/alibaba/wisp/engine/WispSysmon # eea35d9d56e0006e \ No newline at end of file diff --git a/make/lib/CoreLibraries.gmk b/make/lib/CoreLibraries.gmk index ffdeb712b3112b5e2d80796c400eeda0667c1a51..019106e9e8d2a52bf075c80a58fd7923d21ad9d6 100644 --- a/make/lib/CoreLibraries.gmk +++ b/make/lib/CoreLibraries.gmk @@ -144,6 +144,7 @@ LIBJAVA_SRC_DIRS += $(JDK_TOPDIR)/src/$(OPENJDK_TARGET_OS_API_DIR)/native/java/l $(JDK_TOPDIR)/src/share/native/com/alibaba/jwarmup \ $(JDK_TOPDIR)/src/share/native/com/alibaba/jvm/gc \ $(JDK_TOPDIR)/src/share/native/com/alibaba/tenant \ + $(JDK_TOPDIR)/src/share/native/java/dyn \ $(JDK_TOPDIR)/src/share/native/sun/misc \ $(JDK_TOPDIR)/src/share/native/sun/reflect \ $(JDK_TOPDIR)/src/share/native/java/util \ @@ -151,6 +152,11 @@ LIBJAVA_SRC_DIRS += $(JDK_TOPDIR)/src/$(OPENJDK_TARGET_OS_API_DIR)/native/java/l $(JDK_TOPDIR)/src/$(OPENJDK_TARGET_OS_API_DIR)/native/common \ $(JDK_TOPDIR)/src/$(OPENJDK_TARGET_OS_API_DIR)/native/java/util +# Wisp only supports Linux +ifeq ($(OPENJDK_TARGET_OS), linux) + LIBJAVA_SRC_DIRS += $(JDK_TOPDIR)/src/linux/native/com/alibaba/wisp/engine +endif + ifeq ($(OPENJDK_TARGET_OS), windows) LIBJAVA_SRC_DIRS += $(JDK_TOPDIR)/src/$(OPENJDK_TARGET_OS_API_DIR)/native/sun/util/locale/provider else ifeq ($(OPENJDK_TARGET_OS), macosx) diff --git a/make/mapfiles/libjava/mapfile-vers b/make/mapfiles/libjava/mapfile-vers index 85f287d3d365ae798eca149d7f73fdcd9ec0c82a..cf92f270221be7a48a1258065cacee3ac873b7e4 100644 --- a/make/mapfiles/libjava/mapfile-vers +++ b/make/mapfiles/libjava/mapfile-vers @@ -221,6 +221,10 @@ SUNWprivate_1.1 { Java_com_alibaba_jwarmup_JWarmUp_registerNatives; Java_com_alibaba_tenant_TenantGlobals_getTenantFlags; Java_com_alibaba_jvm_gc_ElasticHeapMXBeanImpl_registerNatives; + Java_com_alibaba_wisp_engine_WispEngine_registerNatives; + Java_com_alibaba_wisp_engine_WispSysmon_registerNatives; + Java_com_alibaba_wisp_engine_WispTask_registerNatives; + Java_java_dyn_Coroutine_registerNatives; Java_java_lang_Throwable_fillInStackTrace; Java_java_lang_Throwable_getStackTraceDepth; Java_java_lang_Throwable_getStackTraceElement; diff --git a/make/mapfiles/libjava/reorder-x86 b/make/mapfiles/libjava/reorder-x86 index 05792d0e304f82f458aa709da124b7f45c0519f7..cf32a6eec0bc64413e01c726ce43e6772a59cee6 100644 --- a/make/mapfiles/libjava/reorder-x86 +++ b/make/mapfiles/libjava/reorder-x86 @@ -13,6 +13,10 @@ text: .text%Java_java_lang_Thread_registerNatives; text: .text%Java_com_alibaba_tenant_TenantGlobals_getTenantFlags; text: .text%Java_com_alibaba_jwarmup_JWarmUp_registerNatives; text: .text%Java_com_alibaba_jvm_gc_ElasticHeapMXBeanImpl_registerNatives; +text: .text%Java_com_alibaba_wisp_engine_WispEngine_registerNatives; +text: .text%Java_com_alibaba_wisp_engine_WispSysmon_registerNatives; +text: .text%Java_com_alibaba_wisp_engine_WispTask_registerNatives; +text: .text%Java_java_dyn_Coroutine_registerNatives; text: .text%Java_java_security_AccessController_getStackAccessControlContext; text: .text%Java_java_security_AccessController_getInheritedAccessControlContext; text: .text%Java_java_lang_ClassLoader_registerNatives; diff --git a/make/mapfiles/libnio/mapfile-linux b/make/mapfiles/libnio/mapfile-linux index bdfaa45f13f9a789716cd12a21e45fda87348d08..55afed516212177133ce57b135e35d6eccc714a6 100644 --- a/make/mapfiles/libnio/mapfile-linux +++ b/make/mapfiles/libnio/mapfile-linux @@ -49,6 +49,7 @@ SUNWprivate_1.1 { Java_sun_nio_ch_EPoll_epollCreate; Java_sun_nio_ch_EPoll_epollCtl; Java_sun_nio_ch_EPoll_epollWait; + Java_sun_nio_ch_EPoll_errnoENOENT; Java_sun_nio_ch_EPollPort_close0; Java_sun_nio_ch_EPollPort_drain1; Java_sun_nio_ch_EPollPort_interrupt; diff --git a/src/linux/classes/com/alibaba/wisp/engine/StealAwareRunnable.java b/src/linux/classes/com/alibaba/wisp/engine/StealAwareRunnable.java new file mode 100644 index 0000000000000000000000000000000000000000..7a1336927384b8b8038f425c68ae48613bb20b09 --- /dev/null +++ b/src/linux/classes/com/alibaba/wisp/engine/StealAwareRunnable.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.alibaba.wisp.engine; + + +/** + * An runnable that is aware of work stealing. + */ +public interface StealAwareRunnable extends Runnable { + + /** + * @return if that runnable could be stolen + */ + default boolean isStealEnable() { + return true; + } + + /** + * Set this runnable's {@link #isStealEnable} to given value + */ + default void setStealEnable(boolean b) { + } +} diff --git a/src/linux/classes/com/alibaba/wisp/engine/TaskDispatcher.java b/src/linux/classes/com/alibaba/wisp/engine/TaskDispatcher.java new file mode 100644 index 0000000000000000000000000000000000000000..dc5d62d6e3ec8b93c62ddcb8d6ac4a84a36250b7 --- /dev/null +++ b/src/linux/classes/com/alibaba/wisp/engine/TaskDispatcher.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.alibaba.wisp.engine; + +class TaskDispatcher implements StealAwareRunnable { + private final ClassLoader ctxClassLoader; + private final long enqueueTime; + private final Runnable target; + private final String name; + private final Thread thread; + + TaskDispatcher(ClassLoader ctxClassLoader, Runnable target, String name, Thread thread) { + this.ctxClassLoader = ctxClassLoader; + this.enqueueTime = WispEngine.getNanoTime(); + this.target = target; + this.name = name; + this.thread = thread; + } + + @Override + public void run() { + WispCarrier current = WispCarrier.current(); + current.countEnqueueTime(enqueueTime); + current.runTaskInternal(target, name, thread, + ctxClassLoader == null ? current.current.ctxClassLoader : ctxClassLoader); + } +} diff --git a/src/linux/classes/com/alibaba/wisp/engine/ThreadAsWisp.java b/src/linux/classes/com/alibaba/wisp/engine/ThreadAsWisp.java new file mode 100644 index 0000000000000000000000000000000000000000..eedf53ff7b201d6f7efdaa3a0d0d5e35bb70a5a1 --- /dev/null +++ b/src/linux/classes/com/alibaba/wisp/engine/ThreadAsWisp.java @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.alibaba.wisp.engine; + +import java.util.stream.Stream; + +/** + * Convert Thread.start() to Wisp without changing application's code. + *

+ * Note that ThreadAsWisp is not wisp core logic, just a wrapper of wisp + * coroutine create API which is used by jdk threading library, so we can + * use objectMonitor here. + */ +class ThreadAsWisp { + private final static String JAVA_LANG_PKG = "package:java.lang"; + + private static int nonDaemonCount; + private static Thread preventShutdownThread; + + + /** + * Try to "start thread" as wisp if all listed condition is satisfied: + *

+ * 1. not in java.lang or blacklisted package/class + * 2. not a WispEngine internal Thread + * 3. allThreadAsWisp is true and not match the blacklist + * + * @param thread the thread + * @param target thread's target field + * @return if condition is satisfied and thread is started as wisp + */ + static boolean tryStart(Thread thread, Runnable target) { + if (!WispConfiguration.ALL_THREAD_AS_WISP + || WispEngine.isEngineThread(thread) + || matchBlackList(thread, target)) { + return false; + } + + if (!thread.isDaemon()) { + synchronized (ThreadAsWisp.class) { + if (nonDaemonCount++ == 0) { // start a non-daemon thread to prevent jvm exit + assert preventShutdownThread == null; + preventShutdownThread = new PreventShutdownThread(); + preventShutdownThread.start(); + } + } + } + + // pthread_create always return before new thread started, so we should not wait here + WispEngine.JLA.setWispAlive(thread, true); // thread.isAlive() should be true + WispEngine.current().startAsThread(thread, thread.getName(), thread); + return true; + } + + /** + * Refer to hotspot JavaThread::exit(): + *

+ * 1. Call Thread.exit() to clean up threadGroup + * 2. Notify threads wait on Thread.join() + * 3. exit jvm if all non-daemon thread is exited + * + * @param thread exited thread + */ + static void exit(Thread thread) { + WispEngine.JLA.threadExit(thread); + synchronized (thread) { + thread.notifyAll(); + } + if (!thread.isDaemon()) { + synchronized (ThreadAsWisp.class) { + if (--nonDaemonCount == 0) { + assert preventShutdownThread != null && !preventShutdownThread.isInterrupted(); + preventShutdownThread.interrupt(); + preventShutdownThread = null; + } + } + } + } + + private static boolean matchBlackList(Thread thread, Runnable target) { + return Stream.concat(Stream.of(JAVA_LANG_PKG), WispConfiguration.getThreadAsWispBlacklist().stream()) + .anyMatch(s -> { + Class clazz = (target == null ? thread : target).getClass(); + // Java code could start a thread by passing a `target` Runnable argument + // or override Thread.run() method; + // if `target` is null, we're processing the override situation, so we need + // to check the thread object's class. + String[] sp = s.split(":"); + if (sp.length != 2) { + return false; + } + switch (sp[0]) { + case "class": + return sp[1].equals(clazz.getName()); + case "package": + Package pkg = clazz.getPackage(); + String pkgName = pkg == null ? "" : pkg.getName(); + return sp[1].equals(pkgName); + case "name": + return wildCardMatch(thread.getName(), sp[1]); + default: + return false; + } + }); + } + + private static boolean wildCardMatch(String s, String p) { + return s.matches(p.replace("?", ".?").replace("*", ".*")); + } + + + private static class PreventShutdownThread extends Thread { + // help us monitor if PreventShutdownThread start/exit too much times. + // startCount should very close to 1 for most applications + private static int startCount; + + private PreventShutdownThread() { + super(WispEngine.DAEMON_THREAD_GROUP, "Wisp-Prevent-Shutdown-" + startCount++); + setDaemon(false); // the daemon attribute is inherited from parent, set to false explicitly + } + + @Override + public synchronized void run() { + while (true) { + try { + wait(); + } catch (InterruptedException e) { + break; + } + } + } + } // class PreventShutdownThread +} diff --git a/src/linux/classes/com/alibaba/wisp/engine/TimeOut.java b/src/linux/classes/com/alibaba/wisp/engine/TimeOut.java new file mode 100644 index 0000000000000000000000000000000000000000..f9f78b4194d1b33216c3c1ddf202e3f1bc45e3e9 --- /dev/null +++ b/src/linux/classes/com/alibaba/wisp/engine/TimeOut.java @@ -0,0 +1,272 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.alibaba.wisp.engine; + +import java.util.Arrays; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.TimeUnit; + +/** + * Represents {@link WispTask} related time out + */ +public class TimeOut { + final WispTask task; + long deadlineNano; + boolean canceled = false; + /** + * its position in the heapArray, -1 indicates that TimeOut has been deleted + */ + private final boolean fromJvm; + private int queueIdx; + private TimerManager manager; + + /** + * @param task related {@link WispTask} + * @param deadlineNano wake up related {@link WispTask} at {@code deadline} if not canceled + */ + public TimeOut(WispTask task, long deadlineNano, boolean fromJvm) { + this.task = task; + this.deadlineNano = deadlineNano; + this.fromJvm = fromJvm; + } + + /** + * @return {@code true} if and only if associated timer is expired. + */ + public boolean expired() { + return !canceled && System.nanoTime() >= deadlineNano; + } + + /** + * unpark the blocked task + */ + void doUnpark() { + if (fromJvm) { + task.unpark(); + } else { + task.jdkUnpark(); + } + } + + /** + * We use minimum heap algorithm to get the minimum deadline of all timers, + * also keep every TimeOut's queueIdx(its position in heap) so that we can easily + * remove it. + */ + static class TimerManager { + Queue queue = new Queue(); + ConcurrentLinkedQueue rmQ = new ConcurrentLinkedQueue<>(); + + void copyTimer(Queue copiedQueue) { + copiedQueue.size = 0; + for (TimeOut timeOut : copiedQueue.queue) { + if (timeOut != null) { + addTimer(timeOut); + } + } + } + + void addTimer(TimeOut timeOut) { + timeOut.deadlineNano = overflowFree(timeOut.deadlineNano, queue.peek()); + timeOut.manager = this; + queue.offer(timeOut); + } + + void cancelTimer(TimeOut timeOut) { + if (timeOut.queueIdx != -1) { + if (timeOut.manager == this) { + queue.remove(timeOut); + } else { + timeOut.manager.rmQ.add(timeOut); + } + } + } + + /** + * Dispatch timeout events and return the timeout deadline for next + * first timeout task + * + * @return -1: there's no timeout task, > 0 deadline nanos + */ + long processTimeoutEventsAndGetWaitDeadline(final long now) { + TimeOut timeOut; + while ((timeOut = rmQ.poll()) != null) { + assert timeOut.manager == this; + queue.remove(timeOut); + } + + long deadline = -1; + if (queue.size != 0) { + while ((timeOut = queue.peek()) != null) { + if (timeOut.canceled) { + queue.poll(); + } else if (timeOut.deadlineNano <= now) { + queue.poll(); + timeOut.doUnpark(); + } else { + deadline = timeOut.deadlineNano; + break; + } + } + } + return deadline; + } + + static class Queue { + private static final int INITIAL_CAPACITY = 16; + private TimeOut[] queue = new TimeOut[INITIAL_CAPACITY]; + private int size = 0; + + /** + * Inserts TimeOut x at position k, maintaining heap invariant by + * promoting x up the tree until it is greater than or equal to + * its parent, or is the root. + * + * @param k the position to fill + * @param timeOut the TimeOut to insert + */ + private void siftUp(int k, TimeOut timeOut) { + while (k > 0) { + int parent = (k - 1) >>> 1; + TimeOut e = queue[parent]; + if (timeOut.deadlineNano >= e.deadlineNano) { + break; + } + queue[k] = e; + e.queueIdx = k; + k = parent; + } + queue[k] = timeOut; + timeOut.queueIdx = k; + } + + /** + * Inserts item x at position k, maintaining heap invariant by + * demoting x down the tree repeatedly until it is less than or + * equal to its children or is a leaf. + * + * @param k the position to fill + * @param timeOut the item to insert + */ + private void siftDown(int k, TimeOut timeOut) { + int half = size >>> 1; + while (k < half) { + int child = (k << 1) + 1; + TimeOut c = queue[child]; + int right = child + 1; + if (right < size && c.deadlineNano > queue[right].deadlineNano) { + c = queue[child = right]; + } + if (timeOut.deadlineNano <= c.deadlineNano) { + break; + } + queue[k] = c; + c.queueIdx = k; + k = child; + } + queue[k] = timeOut; + timeOut.queueIdx = k; + } + + + public boolean remove(TimeOut timeOut) { + int i = timeOut.queueIdx; + if (i == -1) { + //this timeOut has been deleted. + return false; + } + + queue[i].queueIdx = -1; + int s = --size; + TimeOut replacement = queue[s]; + queue[s] = null; + if (s != i) { + siftDown(i, replacement); + if (queue[i] == replacement) { + siftUp(i, replacement); + } + } + return true; + } + + public int size() { + return size; + } + + public boolean offer(TimeOut timeOut) { + int i = size++; + if (i >= queue.length) { + queue = Arrays.copyOf(queue, queue.length * 2); + } + if (i == 0) { + queue[0] = timeOut; + timeOut.queueIdx = 0; + } else { + siftUp(i, timeOut); + } + return true; + } + + public TimeOut poll() { + if (size == 0) { + return null; + } + TimeOut f = queue[0]; + int s = --size; + TimeOut x = queue[s]; + queue[s] = null; + if (s != 0) { + siftDown(0, x); + } + f.queueIdx = -1; + return f; + } + + public TimeOut peek() { + return queue[0]; + } + } + } + + private static long overflowFree(long deadlineNano, TimeOut head) { + if (deadlineNano < Long.MIN_VALUE / 2) { // deadlineNano regarded as negative overflow + deadlineNano = Long.MAX_VALUE; + } + if (head != null && head.deadlineNano < 0 && deadlineNano > 0 && deadlineNano - head.deadlineNano < 0) { + deadlineNano = Long.MAX_VALUE + head.deadlineNano; + // then deadlineNano - head.deadlineNano = Long.MAX_VALUE > 0 + // i.e. deadlineNano > head.deadlineNano + } + return deadlineNano; + } + + static long nanos2Millis(long nanos) { + long ms = TimeUnit.NANOSECONDS.toMillis(nanos + TimeUnit.MILLISECONDS.toNanos(1) / 2); + if (ms < 0) { + ms = 0; + } + if (WispConfiguration.PARK_ONE_MS_AT_LEAST && ms == 0) { + ms = 1; + } + return ms; + } +} diff --git a/src/linux/classes/com/alibaba/wisp/engine/WispCarrier.java b/src/linux/classes/com/alibaba/wisp/engine/WispCarrier.java new file mode 100644 index 0000000000000000000000000000000000000000..5feecf8bc3aa1e084f5f17edd4089e3116934fb2 --- /dev/null +++ b/src/linux/classes/com/alibaba/wisp/engine/WispCarrier.java @@ -0,0 +1,597 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.alibaba.wisp.engine; + +import java.dyn.Coroutine; +import java.dyn.CoroutineSupport; +import java.io.IOException; +import java.nio.channels.SelectableChannel; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; + +/** + * {@link WispCarrier} schedules all {@link WispTask} on according worker and control their life cycle + * {@link WispCarrier} exposed its scheduling function for wisp inner usage and maintained all thread local + * schedule info, such as current active {@link WispTask} and thread local task cache, etc. + * + *

A {@link WispCarrier} instance is expected to run in a specific worker. Get per-thread instance by calling + * {@link WispCarrier#current()}. + */ +final class WispCarrier implements Comparable { + + private static final AtomicIntegerFieldUpdater TASK_COUNT_UPDATER = + AtomicIntegerFieldUpdater.newUpdater(WispEngine.class, "runningTaskCount"); + + /** + * The user can only can only get thread-specific carrier by calling this method. + *

+ * We can not use ThreadLocal any more, because if transparentAsync, it behaves as a coroutine local. + * + * @return thread-specific carrier + */ + static WispCarrier current() { + Thread thread = WispEngine.JLA.currentThread0(); + WispTask current = WispEngine.JLA.getWispTask(thread); + if (current == null) { + WispCarrier carrier = new WispCarrier(WispEngine.WISP_ROOT_ENGINE); + if (carrier.threadTask.ctx != null) { + WispEngine.JLA.setWispTask(thread, carrier.getCurrentTask()); + carrier.init(); + } // else: fake carrier used in jni attach + return carrier; + } else { + return current.carrier; + } + } + + // thread, threadTask and worker are 1:1:1 related + WispScheduler.Worker worker; + final Thread thread; + private final WispTask threadTask; + WispEngine engine; + // current running task + WispTask current; + private final List taskCache = new ArrayList<>(); + boolean isInCritical; + WispCounter counter; + int schedTick; + int lastSchedTick; // access by Sysmon + boolean terminated; + private long switchTimestamp = 0; + private WispTask yieldingTask; + private TimeOut pendingTimer; + + private WispCarrier(WispEngine engine) { + thread = WispEngine.JLA.currentThread0(); + this.engine = engine; + CoroutineSupport cs = thread.getCoroutineSupport(); + current = threadTask = new WispTask(this, + cs == null ? null : cs.threadCoroutine(), + cs != null, true); + if (cs == null) { // fake carrier used in jni attach + threadTask.setThreadWrapper(thread); + } else { + threadTask.reset(null, "THREAD: " + thread.getName(), thread, thread.getContextClassLoader()); + } + } + + /** + * Use 2nd-phase init after constructor. Because if constructor calls Thread.currentThread(), + * and recursive calls constructor, then stackOverflow. + */ + private void init() { + WispTask.trackTask(threadTask); + counter = WispCounter.create(this); + } + + /** + * @return Currently running WispTask. Ensured by {@link #yieldTo(WispTask)} + * If calling in a non-coroutine environment, return a thread-emulated WispTask. + */ + WispTask getCurrentTask() { + return current; + } + + /** + * Each WispCarrier has a corresponding worker. Thread can't be changed for WispCarrier. + * Use thread id as WispCarrier id. + * + * @return WispCarrier id + */ + long getId() { + assert thread != null; + return thread.getId(); + } + + // ----------------------------------------------- lifecycle + + final WispTask runTaskInternal(Runnable target, String name, Thread thread, ClassLoader ctxLoader) { + if (engine.hasBeenShutdown && !WispTask.SHUTDOWN_TASK_NAME.equals(name)) { + throw new RejectedExecutionException("Wisp carrier has been shutdown"); + } + assert current == threadTask; + boolean isInCritical0 = isInCritical; + isInCritical = true; + WispTask wispTask; + try { + counter.incrementCreateTaskCount(); + if ((wispTask = getTaskFromCache()) == null) { + wispTask = new WispTask(this, null, true, false); + WispTask.trackTask(wispTask); + } + wispTask.reset(target, name, thread, ctxLoader); + TASK_COUNT_UPDATER.incrementAndGet(engine); + } finally { + isInCritical = isInCritical0; + } + yieldTo(wispTask); + runWispTaskEpilog(); + + return wispTask; + } + + /** + * The only exit path of a task. + * WispTask must call {@code taskExit()} to exit safely. + */ + void taskExit() { // and exit + current.status = WispTask.Status.ZOMBIE; + TASK_COUNT_UPDATER.decrementAndGet(engine); + + current.countExecutionTime(switchTimestamp); + switchTimestamp = 0; + + unregisterEvent(); + returnTaskToCache(current); + + // reset threadWrapper after call returnTaskToCache, + // since the threadWrapper will be used in Thread.currentThread() + current.resetThreadWrapper(); + counter.incrementCompleteTaskCount(); + + // In Tenant killing process, we have an pending exception, + // WispTask.Coroutine's loop will be breaked + // invoke an explicit reschedule instead of return + schedule(); + } + + /** + * @return task from global cached theScheduler + */ + private WispTask getTaskFromCache() { + assert WispCarrier.current() == this; + if (!taskCache.isEmpty()) { + return taskCache.remove(taskCache.size() - 1); + } + if (engine.hasBeenShutdown) { + return null; + } + WispTask task = engine.groupTaskCache.poll(); + if (task == null) { + return null; + } + if (task.carrier != this) { + if (steal(task) != Coroutine.StealResult.SUCCESS) { + engine.groupTaskCache.add(task); + return null; + } + } + assert task.carrier == this; + return task; + } + + /** + * return task back to global cache + */ + private void returnTaskToCache(WispTask task) { + // reuse exited wispTasks from shutdown wispEngine is very tricky, so we'd better not return + // these tasks to global cache + if (taskCache.size() > WispConfiguration.WISP_ENGINE_TASK_CACHE_SIZE && !engine.hasBeenShutdown) { + engine.groupTaskCache.add(task); + } else { + taskCache.add(task); + } + } + + /** + * hook for yield wispTask + */ + private void runWispTaskEpilog() { + processPendingTimer(); + processYield(); + } + + void destroy() { + WispTask.cleanExitedTasks(taskCache); + WispTask.cleanExitedTask(threadTask); + terminated = true; + } + + // ------------------------------------------ scheduling + + /** + * Block current coroutine and do scheduling. + * Typically called when resource is not ready. + */ + final void schedule() { + assert WispCarrier.current() == this; + WispTask current = this.current; + current.countExecutionTime(switchTimestamp); + assert current != threadTask; + assert current.resumeEntry != null : "call `schedule()` in scheduler"; + current.resumeEntry.setStealEnable(true); + yieldTo(threadTask); // letting the scheduler choose runnable task + if (engine.hasBeenShutdown && current != threadTask + && !WispTask.SHUTDOWN_TASK_NAME.equals(current.getName())) { + CoroutineSupport.checkAndThrowException(current.ctx); + } + } + + /** + * Wake up a {@link WispTask} that belongs to this carrier + * + * @param task target task + */ + void wakeupTask(WispTask task) { + assert !task.isThreadTask(); + assert task.resumeEntry != null; + assert task.carrier == this; + task.updateEnqueueTime(); + engine.scheduler.executeWithWorkerThread(task.resumeEntry, thread); + } + + /** + * create a Entry runnable for wisp task, + * used for bridge coroutine and Executor interface. + */ + StealAwareRunnable createResumeEntry(WispTask task) { + assert !task.isThreadTask(); + return new StealAwareRunnable() { + boolean stealEnable = true; + + @Override + public void run() { + WispCarrier current = WispCarrier.current(); + /* + * Please be extremely cautious: + * task.carrier can not be changed here by other thread + * is based on our implementation of using park instead of + * direct schedule, so only one thread could receive + * this closure. + */ + WispCarrier source = task.carrier; + if (source != current) { + Coroutine.StealResult res = current.steal(task); + if (res != Coroutine.StealResult.SUCCESS) { + if (res != Coroutine.StealResult.FAIL_BY_CONTENTION) { + stealEnable = false; + } + source.wakeupTask(task); + return; + } + // notify detached empty worker to exit + if (source.worker.hasBeenHandoff && TASK_COUNT_UPDATER.get(source.engine) == 0) { + source.worker.signal(); + } + } + current.countEnqueueTime(task.getEnqueueTime()); + task.resetEnqueueTime(); + if (current.yieldTo(task)) { + current.runWispTaskEpilog(); + } else { // switch failure + // this is unexpected, record in counter to help troubleshooting. + // The actual behavior of switch failure is similar to unpark lost, + // so we re-enqueue the entry for compensation. + resumeFailure++; + current.wakeupTask(task); + } + } + + @Override + public void setStealEnable(boolean b) { + stealEnable = b; + } + + @Override + public boolean isStealEnable() { + return stealEnable; + } + }; + } + + private static int resumeFailure = 0; + + /** + * Steal task from it's current bond carrier to this carrier + * + * @return steal result + */ + private Coroutine.StealResult steal(WispTask task) { + /* shutdown is an async operation in wisp2, SHUTDOWN task relies on runningTaskCount to + determine whether it's okay to exit the worker, hence we need to make sure no more new + wispTasks are created or stolen for hasBeenShutdown engines + for example: + 1. SHUTDOWN task found runningTaskCount equals 0 and exit + 2. worker's task queue may still has some remaining tasks, when tried to steal these tasks + we may encounter jvm crash. + */ + if (engine.hasBeenShutdown) { + return Coroutine.StealResult.FAIL_BY_STATUS; + } + + assert WispCarrier.current() == this; + assert !task.isThreadTask(); + if (task.carrier != this) { + while (task.stealLock != 0) {/* wait until steal enabled */} + Coroutine.StealResult res = task.ctx.steal(true); + if (res != Coroutine.StealResult.SUCCESS) { + task.stealFailureCount++; + return res; + } + task.stealCount++; + task.setCarrier(this); + } + return Coroutine.StealResult.SUCCESS; + } + + /** + * The ONLY entry point to a task, + * {@link #current} will be set correctly + * + * @param task coroutine to run + */ + private boolean yieldTo(WispTask task) { + assert task != null; + assert WispCarrier.current() == this; + assert task.carrier == this; + assert task != current; + + schedTick++; + + if (task.status == WispTask.Status.ZOMBIE) { + unregisterEvent(task); + return false; + } + + WispTask from = current; + current = task; + counter.incrementSwitchCount(); + switchTimestamp = WispEngine.getNanoTime(); + assert !isInCritical; + boolean res = WispTask.switchTo(from, task); + assert res : "coroutine switch failure"; + // Since carrier is changed with stealing, + // we shouldn't directly access carrier's member any more. + assert WispCarrier.current().current == from; + assert !from.carrier.isInCritical; + return res; + } + + /** + * Telling to the scheduler that the current carrier is willing to yield + * its current use of a processor. + *

+ * Called by {@link Thread#yield()} + */ + void yield() { + if (!WispConfiguration.WISP_HIGH_PRECISION_TIMER && worker != null) { + worker.processTimer(); + } + if (WispEngine.runningAsCoroutine(current.getThreadWrapper())) { + if (getTaskQueueLength() > 0) { + assert yieldingTask == null; + yieldingTask = current; + // delay it, make sure wakeupTask is called after yield out + schedule(); + } + } else { + WispEngine.JLA.yield0(); + } + } + + private void processYield() { + assert current.isThreadTask(); + if (yieldingTask != null) { + wakeupTask(yieldingTask); + yieldingTask = null; + } + } + + // ------------------------------------------------ IO + + /** + * Modify current {@link WispTask}'s interest channel and event. + * {@see registerEvent(...)} + *

+ * Used for implementing socket io + *

+     *     while (!ch.read(buf) == 0) { // 0 indicate IO not ready, not EOF..
+     *         registerEvent(ch, OP_READ);
+     *         schedule();
+     *     }
+     *     // read is done here
+     * 
+     */
+    void registerEvent(SelectableChannel ch, int events) throws IOException {
+        registerEvent(current, ch, events);
+    }
+
+
+    /**
+     * register target {@link WispTask}'s interest channel and event.
+     *
+     * @param ch     the channel that is related to the current WispTask
+     * @param events interest event
+     */
+    private void registerEvent(WispTask target, SelectableChannel ch, int events) throws IOException {
+        if (ch != null && ch.isOpen() && events != 0) {
+            WispEventPump.Pool.INSTANCE.registerEvent(target, ch, events);
+        }
+    }
+
+    /**
+     * Clean current task's interest event before an non-IO blocking operation
+     * or task exit to prevent unexpected wake up.
+     */
+    void unregisterEvent() {
+        unregisterEvent(current);
+    }
+
+    private void unregisterEvent(WispTask target) {
+        if (target.ch != null) {
+            target.resetRegisterEventTime();
+            target.ch = null;
+        }
+    }
+
+    // ------------------------------------------------ timer support
+
+    /**
+     * Add a timer for current {@link WispTask},
+     * used for implementing timed IO operation / sleep etc...
+     *
+     * @param deadlineNano deadline of the timer
+     * @param fromJvm      synchronized or obj.wait()
+     */
+    void addTimer(long deadlineNano, boolean fromJvm) {
+        WispTask task = current;
+        TimeOut timeOut = new TimeOut(task, deadlineNano, fromJvm);
+        task.timeOut = timeOut;
+
+        if (WispConfiguration.WISP_HIGH_PRECISION_TIMER) {
+            if (task.isThreadTask()) {
+                scheduleInTimer(timeOut);
+            } else {
+                /*
+                 * timer.schedule may enter park() again
+                 * we delegate this operation to thread coroutine
+                 * (which always use native park)
+                 */
+                pendingTimer = timeOut;
+            }
+        } else {
+            engine.scheduler.addTimer(timeOut, thread);
+        }
+    }
+
+    /**
+     * Cancel the timer added by {@link #addTimer(long, boolean)}.
+     */
+    void cancelTimer() {
+        if (current.timeOut != null) {
+            current.timeOut.canceled = true;
+            if (!WispConfiguration.WISP_HIGH_PRECISION_TIMER) {
+                engine.scheduler.cancelTimer(current.timeOut, thread);
+            }
+            current.timeOut = null;
+        }
+        pendingTimer = null;
+    }
+
+    private void processPendingTimer() {
+        assert current.isThreadTask();
+        if (WispConfiguration.WISP_HIGH_PRECISION_TIMER && pendingTimer != null) {
+            scheduleInTimer(pendingTimer);
+            pendingTimer = null;
+        }
+    }
+
+    private void scheduleInTimer(TimeOut timeOut) {
+        boolean isInCritical0 = isInCritical;
+        final long timeout = timeOut.deadlineNano - System.nanoTime();
+        isInCritical = true;
+        if (timeout > 0) {
+            WispEngine.timer.schedule(new Runnable() {
+                @Override
+                public void run() {
+                    if (!timeOut.canceled) {
+                        timeOut.doUnpark();
+                    }
+                }
+            }, timeout, TimeUnit.NANOSECONDS);
+        } else if (!timeOut.canceled) {
+            timeOut.task.jdkUnpark();
+        }
+        isInCritical = isInCritical0;
+    }
+
+    // ----------------------------------------------- status fetch
+
+    /**
+     * @return if current carrier is busy
+     */
+    boolean isRunning() {
+        return current != threadTask;
+    }
+
+    /**
+     * @return queue length. used for mxBean report
+     */
+    int getTaskQueueLength() {
+        if (worker == null) {
+            return 0;
+        }
+        int ql = worker.queueLength;
+        // use a local copy to avoid queueLength change to negative.
+        return Math.max(ql, 0);
+    }
+
+    /**
+     * @return running task number, used for mxBean report
+     */
+    int getRunningTaskCount() {
+        return engine.runningTaskCount;
+    }
+
+    // -----------------------------------------------  retake
+
+    /**
+     * hand off wispEngine for blocking system calls.
+     */
+    void handOff() {
+        engine.scheduler.handOffWorkerThread(thread);
+    }
+
+    // ----------------------------------------------- Monitoring
+
+    WispCounter getCounter() {
+        return counter;
+    }
+
+    void countEnqueueTime(long enqueueTime) {
+        if (enqueueTime != 0) {
+            counter.incrementTotalEnqueueTime(System.nanoTime() - enqueueTime);
+        }
+    }
+
+    @Override
+    public String toString() {
+        return "WispCarrier on " + thread.getName();
+    }
+
+    @Override
+    public int compareTo(WispCarrier o) {
+        return Long.compare(getId(), o.getId());
+    }
+}
diff --git a/src/linux/classes/com/alibaba/wisp/engine/WispConfiguration.java b/src/linux/classes/com/alibaba/wisp/engine/WispConfiguration.java
new file mode 100644
index 0000000000000000000000000000000000000000..dede8fe3909d7cba6d5fb1962663eea4ae2e8782
--- /dev/null
+++ b/src/linux/classes/com/alibaba/wisp/engine/WispConfiguration.java
@@ -0,0 +1,233 @@
+/*
+ * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Alibaba designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+package com.alibaba.wisp.engine;
+
+import sun.security.action.GetPropertyAction;
+
+import java.io.*;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Properties;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+class WispConfiguration {
+    private static final String DELIMITER = ";";
+
+    static final boolean TRANSPARENT_WISP_SWITCH;
+    static final boolean ENABLE_THREAD_AS_WISP;
+    static final boolean ALL_THREAD_AS_WISP;
+
+    static final int STACK_SIZE;
+    static final boolean PARK_ONE_MS_AT_LEAST;
+    static final int WORKER_COUNT;
+    static final boolean ENABLE_HANDOFF;
+    static final WispSysmon.Policy HANDOFF_POLICY;
+    static final int SYSMON_TICK_US;
+    static final int MIN_PARK_NANOS;
+    static final int POLLER_SHARDING_SIZE;
+
+    static final int SYSMON_CARRIER_GROW_TICK_US;
+    // monitor
+    static final boolean WISP_PROFILE;
+    static final boolean WISP_PROFILE_LOG_ENABLED;
+    static final int WISP_PROFILE_LOG_INTERVAL_MS;
+    static final String WISP_PROFILE_LOG_PATH;
+
+    static final boolean WISP_HIGH_PRECISION_TIMER;
+    static final int WISP_ENGINE_TASK_CACHE_SIZE;
+    static final int WISP_SCHEDULE_STEAL_RETRY;
+    static final int WISP_SCHEDULE_PUSH_RETRY;
+    static final int WISP_SCHEDULE_HELP_STEAL_RETRY;
+    static final WispScheduler.SchedulingPolicy SCHEDULING_POLICY;
+    static final boolean USE_DIRECT_SELECTOR_WAKEUP;
+    static final boolean CARRIER_AS_POLLER;
+    static final boolean MONOLITHIC_POLL;
+    static final boolean CARRIER_GROW;
+
+    // io
+    static final boolean WISP_ENABLE_SOCKET_LOCK;
+
+    private static List THREAD_AS_WISP_BLACKLIST;
+
+
+    static {
+        Properties p = java.security.AccessController.doPrivileged(
+                new java.security.PrivilegedAction() {
+                    public Properties run() {
+                        return System.getProperties();
+                    }
+                }
+        );
+
+        TRANSPARENT_WISP_SWITCH = p.containsKey("com.alibaba.wisp.transparentWispSwitch") ?
+                parseBooleanParameter(p, "com.alibaba.wisp.transparentWispSwitch", false) :
+                parseBooleanParameter(p, "com.alibaba.transparentAsync", false);
+        ENABLE_THREAD_AS_WISP = p.containsKey("com.alibaba.wisp.enableThreadAsWisp") ?
+                parseBooleanParameter(p, "com.alibaba.wisp.enableThreadAsWisp", false) :
+                parseBooleanParameter(p, "com.alibaba.shiftThreadModel", false);
+        ALL_THREAD_AS_WISP = parseBooleanParameter(p, "com.alibaba.wisp.allThreadAsWisp", false);
+        STACK_SIZE = parsePositiveIntegerParameter(p, "com.alibaba.wisp.stacksize", 512 * 1024);
+        PARK_ONE_MS_AT_LEAST = parseBooleanParameter(p, "com.alibaba.wisp.parkOneMs", true);
+        WORKER_COUNT = parsePositiveIntegerParameter(p, "com.alibaba.wisp.carrierEngines",
+                Runtime.getRuntime().availableProcessors());
+        POLLER_SHARDING_SIZE = parsePositiveIntegerParameter(p, "com.alibaba.pollerShardingSize", 8);
+        ENABLE_HANDOFF = parseBooleanParameter(p, "com.alibaba.wisp.enableHandOff",
+                TRANSPARENT_WISP_SWITCH);
+        // handoff worker thread implementation is not stable enough,
+        // use preempt by default, and we'll move to ADAPTIVE in the future
+        HANDOFF_POLICY = WispSysmon.Policy.valueOf(
+                p.getProperty("com.alibaba.wisp.handoffPolicy", WispSysmon.Policy.PREEMPT.name()));
+        SYSMON_TICK_US = parsePositiveIntegerParameter(p, "com.alibaba.wisp.sysmonTickUs",
+                (int) TimeUnit.MILLISECONDS.toMicros(100));
+        MIN_PARK_NANOS = parsePositiveIntegerParameter(p, "com.alibaba.wisp.minParkNanos", 100);
+        WISP_PROFILE_LOG_ENABLED = parseBooleanParameter(p, "com.alibaba.wisp.enableProfileLog", false);
+        WISP_PROFILE_LOG_INTERVAL_MS = parsePositiveIntegerParameter(p, "com.alibaba.wisp.logTimeInternalMillis", 15000);
+        if (WISP_PROFILE_LOG_ENABLED) {
+            WISP_PROFILE = true;
+            WISP_PROFILE_LOG_PATH = p.getProperty("com.alibaba.wisp.logPath");
+        } else {
+            WISP_PROFILE = parseBooleanParameter(p, "com.alibaba.wisp.profile", false);
+            WISP_PROFILE_LOG_PATH = "";
+        }
+
+        CARRIER_AS_POLLER = parseBooleanParameter(p, "com.alibaba.wisp.useCarrierAsPoller", ALL_THREAD_AS_WISP);
+        MONOLITHIC_POLL = parseBooleanParameter(p, "com.alibaba.wisp.monolithicPoll", true);
+        WISP_HIGH_PRECISION_TIMER = parseBooleanParameter(p, "com.alibaba.wisp.highPrecisionTimer", false);
+        WISP_ENGINE_TASK_CACHE_SIZE = parsePositiveIntegerParameter(p, "com.alibaba.wisp.engineTaskCache", 20);
+        WISP_SCHEDULE_STEAL_RETRY = parsePositiveIntegerParameter(p, "com.alibaba.wisp.schedule.stealRetry", Math.max(1, WORKER_COUNT / 2));
+        WISP_SCHEDULE_PUSH_RETRY = parsePositiveIntegerParameter(p, "com.alibaba.wisp.schedule.pushRetry", WORKER_COUNT);
+        WISP_SCHEDULE_HELP_STEAL_RETRY = parsePositiveIntegerParameter(p, "com.alibaba.wisp.schedule.helpStealRetry", Math.max(1, WORKER_COUNT / 4));
+        SCHEDULING_POLICY = WispScheduler.SchedulingPolicy.valueOf(p.getProperty("com.alibaba.wisp.schedule.policy",
+                WORKER_COUNT > 16 ? WispScheduler.SchedulingPolicy.PUSH.name() : WispScheduler.SchedulingPolicy.PULL.name()));
+        USE_DIRECT_SELECTOR_WAKEUP = parseBooleanParameter(p, "com.alibaba.wisp.directSelectorWakeup", true);
+        WISP_ENABLE_SOCKET_LOCK = parseBooleanParameter(p, "com.alibaba.wisp.useSocketLock", true);
+        CARRIER_GROW = parseBooleanParameter(p, "com.alibaba.wisp.growCarrier", false);
+        SYSMON_CARRIER_GROW_TICK_US = parsePositiveIntegerParameter(p, "com.alibaba.wisp.growCarrierTickUs", (int) TimeUnit.SECONDS.toMicros(5));
+        checkCompatibility();
+    }
+
+    private static void checkCompatibility() {
+        checkDependency(ENABLE_THREAD_AS_WISP, "-Dcom.alibaba.wisp.enableThreadAsWisp=true",
+                TRANSPARENT_WISP_SWITCH, "-Dcom.alibaba.wisp.transparentWispSwitch=true");
+        checkDependency(ENABLE_HANDOFF, "-Dcom.alibaba.wisp.enableHandOff=true",
+                TRANSPARENT_WISP_SWITCH, "-Dcom.alibaba.wisp.enableThreadAsWisp=true");
+        checkDependency(ALL_THREAD_AS_WISP, "-Dcom.alibaba.wisp.allThreadAsWisp=true",
+                ENABLE_THREAD_AS_WISP, "-Dcom.alibaba.wisp.enableThreadAsWisp=true");
+        checkDependency(CARRIER_AS_POLLER, "-Dcom.alibaba.wisp.useCarrierAsPoller=true",
+                ALL_THREAD_AS_WISP, "-Dcom.alibaba.wisp.allThreadAsWisp=true");
+        if (ENABLE_THREAD_AS_WISP && !ALL_THREAD_AS_WISP) {
+            throw new IllegalArgumentException("shift thread model by stack configuration is no longer supported," +
+                    " use -XX:+UseWisp2 instead");
+        }
+    }
+
+    private static void checkDependency(boolean cond, String condStr, boolean preRequire, String preRequireStr) {
+        if (cond && !preRequire) {
+            throw new IllegalArgumentException("\"" + condStr + "\" depends on \"" + preRequireStr + "\"");
+        }
+    }
+
+    private static int parsePositiveIntegerParameter(Properties p, String key, int defaultVal) {
+        String value;
+        if (p == null || (value = p.getProperty(key)) == null) {
+            return defaultVal;
+        }
+        int res = defaultVal;
+        try {
+            res = Integer.valueOf(value);
+        } catch (NumberFormatException e) {
+            return defaultVal;
+        }
+        return res <= 0 ? defaultVal : res;
+    }
+
+    private static boolean parseBooleanParameter(Properties p, String key, boolean defaultVal) {
+        String value;
+        if (p == null || (value = p.getProperty(key)) == null) {
+            return defaultVal;
+        }
+        return Boolean.valueOf(value);
+    }
+
+    private static List parseListParameter(Properties p, Properties confProp, String key) {
+        String value = p.getProperty(key);
+        if (value == null) {
+            value = confProp.getProperty(key);
+        }
+        return value == null ? Collections.emptyList() :
+                Arrays.asList(value.trim().split(DELIMITER));
+    }
+
+    /**
+     * Loading config from system property "com.alibaba.wisp.config" specified
+     * file or jre/lib/wisp.properties.
+     */
+    private static void loadBizConfig() {
+        Properties p = java.security.AccessController.doPrivileged(
+                new java.security.PrivilegedAction() {
+                    public Properties run() {
+                        return System.getProperties();
+                    }
+                }
+        );
+        String path = p.getProperty("com.alibaba.wisp.config");
+
+        Properties confProp = new Properties();
+        if (path != null) {
+            File f = new File(path);
+            if (f.exists()) {
+                try (InputStream is = new BufferedInputStream(new FileInputStream(f.getPath()))) {
+                    confProp.load(is);
+                } catch (IOException e) {
+                    // ignore, all STACK_LIST are empty
+                }
+            }
+        }
+        THREAD_AS_WISP_BLACKLIST = parseListParameter(p, confProp, "com.alibaba.wisp.threadAsWisp.black");
+
+    }
+
+    private static final int UNLOADED = 0, LOADING = 1, LOADED = 2;
+    private static final AtomicInteger bizLoadStatus = new AtomicInteger(UNLOADED);
+
+    private static void ensureBizConfigLoaded() {
+        if (bizLoadStatus.get() == LOADED) {
+            return;
+        }
+        if (bizLoadStatus.get() == UNLOADED && bizLoadStatus.compareAndSet(UNLOADED, LOADING)) {
+            try {
+                loadBizConfig();
+            } finally {
+                bizLoadStatus.set(LOADED);
+            }
+        }
+        while (bizLoadStatus.get() != LOADED) {/* wait */}
+    }
+
+    static List getThreadAsWispBlacklist() {
+        ensureBizConfigLoaded();
+        assert THREAD_AS_WISP_BLACKLIST != null;
+        return THREAD_AS_WISP_BLACKLIST;
+    }
+}
diff --git a/src/linux/classes/com/alibaba/wisp/engine/WispCounter.java b/src/linux/classes/com/alibaba/wisp/engine/WispCounter.java
new file mode 100644
index 0000000000000000000000000000000000000000..4d37f92e0e4acf18cab1d3a06edda353b6953536
--- /dev/null
+++ b/src/linux/classes/com/alibaba/wisp/engine/WispCounter.java
@@ -0,0 +1,342 @@
+/*
+ * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Alibaba designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+package com.alibaba.wisp.engine;
+
+import java.beans.ConstructorProperties;
+
+final public class WispCounter {
+
+    private long switchCount = 0;
+
+    private long waitTimeTotal = 0;
+
+    private long runningTimeTotal = 0;
+
+    private long completedTaskCount = 0;
+
+    private long createTaskCount = 0;
+
+    private long parkCount = 0;
+
+    private long unparkCount = 0;
+
+    private long unparkInterruptSelectorCount = 0;
+
+    private long selectableIOCount = 0;
+
+    private long timeOutCount = 0;
+
+    private long eventLoopCount = 0;
+
+    private long totalEnqueueTime = 0;
+
+    private long maxEnqueueTime = 0;
+
+    private long enqueueCount = 0;
+
+    private long totalExecutionTime = 0;
+
+    private long maxExecutionTime = 0;
+
+    private long executionCount = 0;
+
+    private long totalWaitSocketIOTime = 0;
+
+    private long maxWaitSocketIOTime = 0;
+
+    private long waitSocketIOCount = 0;
+
+    private long totalBlockingTime = 0;
+
+    private long maxBlockingTime = 0;
+
+    private long unparkFromJvmCount = 0;
+
+    private long runningTaskCount = 0;
+
+    private long taskQueueLength = 0;
+
+    WispCarrier carrier;
+
+    private WispCounter(WispCarrier carrier) {
+        this.carrier = carrier;
+    }
+
+    boolean getRunningState() {
+        WispCarrier e = carrier;
+        return e != null && e.isRunning();
+    }
+
+    void incrementSwitchCount() {
+        switchCount++;
+    }
+
+    long getSwitchCount() {
+        return switchCount;
+    }
+
+    void incrementCompleteTaskCount() {
+        completedTaskCount++;
+    }
+
+    void incrementRunningTimeTotal(long value) {
+        runningTimeTotal += value;
+    }
+
+    long getRunningTimeTotal() {
+        return runningTimeTotal;
+    }
+
+    void incrementWaitTime(long value) {
+        waitTimeTotal += value;
+    }
+
+    long getWaitTimeTotal() {
+        return waitTimeTotal;
+    }
+
+    public long getCompletedTaskCount() {
+        return completedTaskCount;
+    }
+
+    void incrementCreateTaskCount() {
+        createTaskCount++;
+    }
+
+    long getCreateTaskCount() {
+        return createTaskCount;
+    }
+
+    void incrementParkCount() {
+        parkCount++;
+    }
+
+    long getParkCount() {
+        return parkCount;
+    }
+
+
+    void incrementUnparkInterruptSelectorCount() {
+        unparkInterruptSelectorCount++;
+    }
+
+    long getUnparkInterruptSelectorCount() {
+        return unparkInterruptSelectorCount;
+    }
+
+    void incrementSelectableIOCount() {
+        selectableIOCount++;
+    }
+
+    long getSelectableIOCount() {
+        return selectableIOCount;
+    }
+
+    void incrementTimeOutCount() {
+        timeOutCount++;
+    }
+
+    long getTimeOutCount() {
+        return timeOutCount;
+    }
+
+    void incrementEventLoopCount() {
+        eventLoopCount++;
+    }
+
+    long getEventLoopCount() {
+        return eventLoopCount;
+    }
+
+    void incrementTotalEnqueueTime(long value) {
+        totalEnqueueTime += value;
+        enqueueCount++;
+        if (value > maxEnqueueTime) {
+            maxEnqueueTime = value;
+        }
+    }
+
+    public long getTotalEnqueueTime() {
+        return totalEnqueueTime;
+    }
+
+    public long getEnqueueCount() {
+        return enqueueCount;
+    }
+
+    void incrementTotalExecutionTime(long value) {
+        totalExecutionTime += value;
+        executionCount++;
+        if (value > maxExecutionTime) {
+            maxExecutionTime = value;
+        }
+    }
+
+    public long getTotalExecutionTime() {
+        return totalExecutionTime;
+    }
+
+    public long getExecutionCount() {
+        return executionCount;
+    }
+
+    void incrementTotalWaitSocketIOTime(long value) {
+        totalWaitSocketIOTime += value;
+        waitSocketIOCount++;
+        if (value > maxWaitSocketIOTime) {
+            maxWaitSocketIOTime = value;
+        }
+    }
+
+    public long getTotalWaitSocketIOTime() {
+        return totalWaitSocketIOTime;
+    }
+
+    public long getWaitSocketIOCount() {
+        return waitSocketIOCount;
+    }
+
+    void incrementTotalBlockingTime(long value) {
+        totalBlockingTime += value;
+        unparkCount++;
+        if (value > maxBlockingTime) {
+            maxBlockingTime = value;
+        }
+    }
+
+    public long getTotalBlockingTime() {
+        return totalBlockingTime;
+    }
+
+    public long getUnparkCount() {
+        return unparkCount;
+    }
+
+    long getCurrentTaskQueueLength() {
+        WispCarrier e = carrier;
+        return e != null ? e.getTaskQueueLength() : 0;
+    }
+
+    long getCurrentRunningTaskCount() {
+        WispCarrier e = carrier;
+        return e != null ? e.getRunningTaskCount() : 0;
+    }
+
+    void incrementUnparkFromJvmCount() {
+        unparkFromJvmCount++;
+    }
+
+    long getUnparkFromJvmCount() {
+        return unparkFromJvmCount;
+    }
+
+    public long getMaxEnqueueTime() {
+        return maxEnqueueTime;
+    }
+
+    public long getMaxExecutionTime() {
+        return maxExecutionTime;
+    }
+
+    public long getMaxWaitSocketIOTime() {
+        return maxWaitSocketIOTime;
+    }
+
+    public long getMaxBlockingTime() {
+        return maxBlockingTime;
+    }
+
+    public long getTaskQueueLength() {
+        return taskQueueLength;
+    }
+
+    public long getRunningTaskCount() {
+        return runningTaskCount;
+    }
+
+    WispCounter() {
+    }
+
+    @ConstructorProperties({"completedTaskCount", "totalEnqueueTime", "maxEnqueueTime", "enqueueCount",
+            "totalExecutionTime", "maxExecutionTime", "executionCount",
+            "totalWaitSocketIOTime", "maxWaitSocketIOTime", "waitSocketIOCount",
+            "totalBlockingTime", "maxBlockingTime", "unparkCount",
+            "runningTaskCount", "taskQueueLength"})
+    public WispCounter(long completedTaskCount, long totalEnqueueTime, long maxEnqueueTime, long enqueueCount,
+                       long totalExecutionTime, long maxExecutionTime, long executionCount,
+                       long totalWaitSocketIOTime, long maxWaitSocketIOTime, long waitSocketIOCount,
+                       long totalBlockingTime, long maxBlockingTime, long unparkCount,
+                       long runningTaskCount, long taskQueueLength) {
+        this.completedTaskCount = completedTaskCount;
+        this.totalEnqueueTime = totalEnqueueTime;
+        this.maxEnqueueTime = maxEnqueueTime;
+        this.enqueueCount = enqueueCount;
+        this.totalExecutionTime = totalExecutionTime;
+        this.maxExecutionTime = maxExecutionTime;
+        this.executionCount = executionCount;
+        this.totalWaitSocketIOTime = totalWaitSocketIOTime;
+        this.maxWaitSocketIOTime = maxWaitSocketIOTime;
+        this.waitSocketIOCount = waitSocketIOCount;
+        this.totalBlockingTime = totalBlockingTime;
+        this.maxBlockingTime = maxBlockingTime;
+        this.unparkCount = unparkCount;
+        this.runningTaskCount = runningTaskCount;
+        this.taskQueueLength = taskQueueLength;
+    }
+
+
+    void assign(WispCounter counter) {
+        createTaskCount = counter.createTaskCount;
+        completedTaskCount = counter.completedTaskCount;
+        totalEnqueueTime = counter.totalEnqueueTime;
+        enqueueCount = counter.enqueueCount;
+        maxEnqueueTime = counter.maxEnqueueTime;
+        totalExecutionTime = counter.totalExecutionTime;
+        executionCount = counter.executionCount;
+        maxExecutionTime = counter.maxExecutionTime;
+        totalBlockingTime = counter.totalBlockingTime;
+        unparkCount = counter.unparkCount;
+        maxBlockingTime = counter.maxBlockingTime;
+        totalWaitSocketIOTime = counter.totalWaitSocketIOTime;
+        waitSocketIOCount = counter.waitSocketIOCount;
+        maxWaitSocketIOTime = counter.maxWaitSocketIOTime;
+        switchCount = counter.switchCount;
+        unparkFromJvmCount = counter.unparkFromJvmCount;
+        runningTaskCount = counter.getCurrentRunningTaskCount();
+        taskQueueLength = counter.getCurrentTaskQueueLength();
+    }
+
+    void resetMaxValue() {
+        maxEnqueueTime = 0;
+        maxExecutionTime = 0;
+        maxWaitSocketIOTime = 0;
+        maxBlockingTime = 0;
+    }
+
+    void cleanup() {
+        carrier = null;
+    }
+
+    static WispCounter create(WispCarrier carrier) {
+        return new WispCounter(carrier);
+    }
+}
diff --git a/src/linux/classes/com/alibaba/wisp/engine/WispCounterMXBeanImpl.java b/src/linux/classes/com/alibaba/wisp/engine/WispCounterMXBeanImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..97529dca31a2a2b0a1346539da55d84decb42e35
--- /dev/null
+++ b/src/linux/classes/com/alibaba/wisp/engine/WispCounterMXBeanImpl.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Alibaba designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+package com.alibaba.wisp.engine;
+
+import com.alibaba.management.WispCounterMXBean;
+
+import sun.management.Util;
+
+import javax.management.ObjectName;
+import java.util.*;
+import java.util.Map.Entry;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Function;
+
+/**
+ * Implementation class for WispCounterMXBean.
+ */
+public class WispCounterMXBeanImpl implements WispCounterMXBean {
+
+    private final static String WISP_COUNTER_MXBEAN_NAME = "com.alibaba.management:type=WispCounter";
+
+    private final static Map managedEngineCounters;
+
+    static {
+        if (!WispEngine.transparentWispSwitch()) {
+            managedEngineCounters = new HashMap<>();
+        } else {
+            managedEngineCounters = new ConcurrentHashMap<>(100);
+        }
+    }
+
+    @Override
+    public List getRunningStates() {
+        return aggregate(WispCounter::getRunningState);
+    }
+
+    @Override
+    public List getSwitchCount() {
+        return aggregate(WispCounter::getSwitchCount);
+    }
+
+    @Override
+    public List getWaitTimeTotal() {
+        return aggregate(WispCounter::getWaitTimeTotal);
+    }
+
+    @Override
+    public List getRunningTimeTotal() {
+        return aggregate(WispCounter::getRunningTimeTotal);
+    }
+
+    @Override
+    public List getCompleteTaskCount() {
+        return aggregate(WispCounter::getCompletedTaskCount);
+    }
+
+    @Override
+    public List getCreateTaskCount() {
+        return aggregate(WispCounter::getCreateTaskCount);
+    }
+
+    @Override
+    public List getParkCount() {
+        return aggregate(WispCounter::getParkCount);
+    }
+
+    @Override
+    public List getUnparkCount() {
+        return aggregate(WispCounter::getUnparkCount);
+    }
+
+    @Override
+    public List getLazyUnparkCount() {
+        return aggregate(w -> 0L);
+    }
+
+    @Override
+    public List getUnparkInterruptSelectorCount() {
+        return aggregate(WispCounter::getUnparkInterruptSelectorCount);
+    }
+
+    @Override
+    public List getSelectableIOCount() {
+        return aggregate(WispCounter::getSelectableIOCount);
+    }
+
+    @Override
+    public List getTimeOutCount() {
+        return aggregate(WispCounter::getTimeOutCount);
+    }
+
+    @Override
+    public List getEventLoopCount() {
+        return aggregate(WispCounter::getEventLoopCount);
+    }
+
+    @Override
+    public List getQueueLength() {
+        return aggregate(WispCounter::getCurrentTaskQueueLength);
+    }
+
+    @Override
+    public List getNumberOfRunningTasks() {
+        return aggregate(WispCounter::getCurrentRunningTaskCount);
+    }
+
+    @Override
+    public List getTotalEnqueueTime() {
+        return aggregate(WispCounter::getTotalEnqueueTime);
+    }
+
+    @Override
+    public List getEnqueueCount() {
+        return aggregate(WispCounter::getEnqueueCount);
+    }
+
+    @Override
+    public List getTotalExecutionTime() {
+        return aggregate(WispCounter::getTotalExecutionTime);
+    }
+
+    @Override
+    public List getExecutionCount() {
+        return aggregate(WispCounter::getExecutionCount);
+    }
+
+    @Override
+    public List getTotalWaitSocketIOTime() {
+        return aggregate(WispCounter::getTotalWaitSocketIOTime);
+    }
+
+    @Override
+    public List getWaitSocketIOCount() {
+        return aggregate(WispCounter::getWaitSocketIOCount);
+    }
+
+    @Override
+    public List getTotalBlockingTime() {
+        return aggregate(WispCounter::getTotalBlockingTime);
+    }
+
+    /**
+     * @param id WispCarrier id
+     * @return WispCounter
+     */
+    @Override
+    public WispCounter getWispCounter(long id) {
+        return WispEngine.getWispCounter(id);
+    }
+
+    private  List aggregate(Function getter) {
+        List result = new ArrayList<>(managedEngineCounters.size());
+        for (Entry entry : managedEngineCounters.entrySet()) {
+            result.add(getter.apply(entry.getValue()));
+        }
+        return result;
+    }
+
+    @Override
+    public ObjectName getObjectName() {
+        return Util.newObjectName(WISP_COUNTER_MXBEAN_NAME);
+    }
+
+    static void register(WispCounter counter) {
+        managedEngineCounters.put(counter.carrier.getId(), counter);
+    }
+
+    static void deRegister(WispCounter counter) {
+        managedEngineCounters.remove(counter.carrier.getId());
+        counter.cleanup();
+    }
+}
diff --git a/src/linux/classes/com/alibaba/wisp/engine/WispEngine.java b/src/linux/classes/com/alibaba/wisp/engine/WispEngine.java
new file mode 100644
index 0000000000000000000000000000000000000000..59e476f27652a0cc4a20fbea53f01a1d4f753a98
--- /dev/null
+++ b/src/linux/classes/com/alibaba/wisp/engine/WispEngine.java
@@ -0,0 +1,586 @@
+/*
+ * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Alibaba designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+package com.alibaba.wisp.engine;
+
+import sun.misc.JavaLangAccess;
+import sun.misc.SharedSecrets;
+import sun.misc.WispEngineAccess;
+
+import java.dyn.Coroutine;
+import java.dyn.CoroutineExitException;
+import java.dyn.CoroutineSupport;
+import java.io.IOException;
+import java.nio.channels.SelectableChannel;
+import java.util.*;
+import java.util.concurrent.*;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
+import java.util.function.Supplier;
+
+/**
+ * Coroutine Runtime Engine. It's a "wisp" thing, as we want our asynchronization transformation to be transparent
+ * without any modification to user code.
+ * 

+ * WispEngine represents a group of {@link WispCarrier}, which can steal + * tasks from each other to achieve work-stealing. + *

+ * {@code WispEngine#WISP_ROOT_ENGINE} is created by system. + * {@link WispEngine#current().execute(Runnable)} in non-worker thread and WISP_ROOT_ENGINE's + * worker thread will dispatch task in this carrier. + *

+ * User code could also create {@link WispEngine} by calling + * {@link WispEngine#createEngine(int, ThreadFactory)}, + * Calling {@link WispEngine#execute(Runnable)} will dispatch + * WispTask inner created carrier. + * {@link WispEngine#current().execute(Runnable)} in a user created carrier will also + * dispatch task in current carrier. + */ +public class WispEngine extends AbstractExecutorService { + + static { + registerNatives(); + setWispEngineAccess(); + timer = createTimerScheduler(); + } + + public static boolean transparentWispSwitch() { + return WispConfiguration.TRANSPARENT_WISP_SWITCH; + } + + public static boolean enableThreadAsWisp() { + return shiftThreadModel; + } + + @Deprecated + public static boolean isTransparentAsync() { + return transparentWispSwitch(); + } + + private static final String WISP_ROOT_ENGINE_NAME = "Root"; + private static final AtomicReferenceFieldUpdater SHUTDOWN_UPDATER + = AtomicReferenceFieldUpdater.newUpdater(WispEngine.class, Boolean.class, "hasBeenShutdown"); + /* + some of our users change this field by reflection + in the runtime to disable wisp temporarily. + We should move shiftThreadModel to WispConfiguration + after we provide api to control this behavior and + notify the users to modify their code. + TODO refactor to com.alibaba.wisp.enableThreadAsWisp later + */ + static boolean shiftThreadModel = WispConfiguration.ENABLE_THREAD_AS_WISP; + static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess(); + /* + * Wisp specified Thread Group + * all the daemon threads in wisp should be created with the Thread Group. + * In Thread.start(), if the thread should not convert to WispTask, + * check whether the thread's carrier is daemonThreadGroup + */ + final static ThreadGroup DAEMON_THREAD_GROUP = + new ThreadGroup(JLA.currentThread0().getThreadGroup(), "Daemon Thread Group"); + static ScheduledExecutorService timer; + static Set carrierThreads; + static Thread unparkDispatcher; + + static WispEngine WISP_ROOT_ENGINE; + + private static ScheduledExecutorService createTimerScheduler() { + return !WispConfiguration.WISP_HIGH_PRECISION_TIMER ? null : + Executors.newScheduledThreadPool(1, new ThreadFactory() { + @Override + public Thread newThread(Runnable r) { + Thread thread = new Thread(WispEngine.DAEMON_THREAD_GROUP, r); + thread.setDaemon(true); + thread.setName("Wisp-Timer"); + return thread; + } + }); + } + + private static void initializeWispClass() { + assert JLA != null : "WispCarrier should be initialized after System"; + assert JLA.currentThread0().getName().equals("main") : "Wisp need to be loaded by main thread"; + shiftThreadModel = WispConfiguration.ENABLE_THREAD_AS_WISP; + carrierThreads = new ConcurrentSkipListSet<>(new Comparator() { + @Override + public int compare(Thread o1, Thread o2) { + return Long.compare(o1.getId(), o2.getId()); + } + }); + WISP_ROOT_ENGINE = new WispEngine(WISP_ROOT_ENGINE_NAME); + if (transparentWispSwitch()) { + WispEngine.initializeClasses(); + JLA.wispBooted(); + } + } + + private static void initializeClasses() { + try { + Class.forName(CoroutineExitException.class.getName()); + Class.forName(WispThreadWrapper.class.getName()); + Class.forName(TaskDispatcher.class.getName()); + Class.forName(StartShutdown.class.getName()); + Class.forName(NotifyAndWaitTasksForShutdown.class.getName()); + Class.forName(Coroutine.StealResult.class.getName()); + Class.forName(WispCounterMXBeanImpl.class.getName()); + Class.forName(ThreadAsWisp.class.getName()); + Class.forName(WispEventPump.class.getName()); + if (WispConfiguration.WISP_PROFILE) { + Class.forName(WispPerfCounterMonitor.class.getName()); + } + if (WispConfiguration.WISP_HIGH_PRECISION_TIMER) { + timer.submit(new Runnable() { + @Override + public void run() { + } + }); + } + new ConcurrentLinkedQueue<>().iterator(); + new ConcurrentSkipListMap<>().keySet().iterator(); + WispCarrier carrier = WispCarrier.current(); + carrier.addTimer(System.nanoTime() + Integer.MAX_VALUE, false); + carrier.cancelTimer(); + carrier.createResumeEntry(new WispTask(carrier, null, false, false)); + registerPerfCounter(carrier); + deRegisterPerfCounter(carrier); + } catch (Exception e) { + throw new ExceptionInInitializerError(e); + } + } + + private static void startWispDaemons() { + if (transparentWispSwitch()) { + unparkDispatcher = new Thread(DAEMON_THREAD_GROUP, new Runnable() { + @Override + public void run() { + int[] proxyUnparks = new int[12]; + CoroutineSupport.setWispBooted(); + while (true) { + int n = WispEngine.getProxyUnpark(proxyUnparks); + for (int i = 0; i < n; i++) { + WispTask.unparkById(proxyUnparks[i]); + } + } + } + }, "Wisp-Unpark-Dispatcher"); + unparkDispatcher.setDaemon(true); + unparkDispatcher.start(); + WispSysmon.INSTANCE.startDaemon(); + WISP_ROOT_ENGINE.scheduler.startWorkerThreads(); + + if (!WispConfiguration.CARRIER_AS_POLLER) { + WispEventPump.Pool.INSTANCE.startPollerThreads(); + } + if (WispConfiguration.WISP_PROFILE_LOG_ENABLED) { + WispPerfCounterMonitor.INSTANCE.startDaemon(); + } + } + } + + private static void setWispEngineAccess() { + SharedSecrets.setWispEngineAccess(new WispEngineAccess() { + + @Override + public WispTask getCurrentTask() { + return WispCarrier.current().getCurrentTask(); + } + + @Override + public void registerEvent(SelectableChannel ch, int events) throws IOException { + WispCarrier.current().registerEvent(ch, events); + } + + @Override + public void unregisterEvent() { + WispCarrier.current().unregisterEvent(); + } + + @Override + public int epollWait(int epfd, long pollArray, int arraySize, long timeout, + AtomicReference status, Object INTERRUPTED) throws IOException { + return WispEventPump.Pool.INSTANCE.epollWaitForWisp(epfd, pollArray, arraySize, timeout, status, INTERRUPTED); + } + + @Override + public void interruptEpoll(AtomicReference status, Object INTERRUPTED, int interruptFd) { + WispEventPump.Pool.INSTANCE.interruptEpoll(status, INTERRUPTED, interruptFd); + } + + @Override + public void addTimer(long deadlineNano) { + WispCarrier.current().addTimer(deadlineNano, false); + } + + @Override + public void cancelTimer() { + WispCarrier.current().cancelTimer(); + } + + @Override + public void sleep(long ms) { + WispTask.sleep(ms); + } + + @Override + public void yield() { + WispCarrier.current().yield(); + } + + @Override + public boolean isThreadTask(WispTask task) { + return task.isThreadTask(); + } + + @Override + public boolean isTimeout() { + WispTask task = WispCarrier.current().current; + return task.timeOut != null && task.timeOut.expired(); + } + + @Override + public void park(long timeoutNano) { + WispTask.jdkPark(timeoutNano); + } + + @Override + public void unpark(WispTask task) { + if (task != null) { + task.jdkUnpark(); + } + } + + @Override + public void destroy() { + WispCarrier.current().destroy(); + } + + @Override + public boolean hasMoreTasks() { + return WispCarrier.current().getTaskQueueLength() > 0; + } + + @Override + public boolean runningAsCoroutine(Thread t) { + return WispEngine.runningAsCoroutine(t); + } + + @Override + public boolean usingWispEpoll() { + return runningAsCoroutine(null); + } + + public boolean isAlive(WispTask task) { + return task.isAlive(); + } + + @Override + public void interrupt(WispTask task) { + task.interrupt(); + } + + @Override + public boolean testInterruptedAndClear(WispTask task, boolean clear) { + return task.testInterruptedAndClear(clear); + } + + @Override + public boolean tryStartThreadAsWisp(Thread thread, Runnable target) { + return ThreadAsWisp.tryStart(thread, target); + } + + @Override + public boolean isAllThreadAsWisp() { + return WispConfiguration.ALL_THREAD_AS_WISP; + } + + @Override + public boolean useDirectSelectorWakeup() { + return WispConfiguration.USE_DIRECT_SELECTOR_WAKEUP; + } + + @Override + public boolean enableSocketLock() { + return WispConfiguration.WISP_ENABLE_SOCKET_LOCK; + } + + @Override + public StackTraceElement[] getStackTrace(WispTask task) { + return task.getStackTrace(); + } + }); + } + + final WispScheduler scheduler; + final Set carrierEngines; + final Queue groupTaskCache = new ConcurrentLinkedQueue<>(); + final CyclicBarrier shutdownBarrier; + volatile int runningTaskCount = 0; + private CountDownLatch shutdownFuture; + volatile Boolean hasBeenShutdown = false; + volatile boolean detached; + + /** + * Create a new WispEngine for executing tasks. + * + * @param size worker thread counter + * @param tf ThreadFactory used to create worker thread + */ + public static WispEngine createEngine(int size, ThreadFactory tf) { + return new WispEngine(size, tf); + } + + /** + * Create Root Worker. + */ + private WispEngine(String name) { + carrierEngines = new ConcurrentSkipListSet<>(); + // detached carrier won't shut down + shutdownBarrier = null; + scheduler = new WispScheduler( + WispConfiguration.WORKER_COUNT, + WispConfiguration.WISP_SCHEDULE_STEAL_RETRY, + WispConfiguration.WISP_SCHEDULE_PUSH_RETRY, + WispConfiguration.WISP_SCHEDULE_HELP_STEAL_RETRY, + new ThreadFactory() { + final AtomicInteger seq = new AtomicInteger(); + + @Override + public Thread newThread(Runnable r) { + Thread t = new Thread(r, "Wisp-" + name + "-Worker-" + seq.getAndIncrement()); + t.setDaemon(true); + return t; + } + }, this, true); + } + + private WispEngine(int size, ThreadFactory tf) { + carrierEngines = new ConcurrentSkipListSet<>(); + shutdownBarrier = new CyclicBarrier(size); + scheduler = new WispScheduler(size, tf, this); + shutdownFuture = new CountDownLatch(1); + } + + + public static WispEngine current() { + return WispCarrier.current().engine; + } + + + /** + * Create WispTask to run task code + *

+ * The real running thread depends on implementation + * + * @param target target code + */ + public static void dispatch(Runnable target) { + WispEngine.current().execute(target); + } + + @Deprecated + public static boolean isShiftThreadModel() { + return shiftThreadModel; + } + + static boolean isEngineThread(Thread t) { + assert DAEMON_THREAD_GROUP != null; + return DAEMON_THREAD_GROUP == t.getThreadGroup() || carrierThreads.contains(t); + } + + static long getNanoTime() { + return WispConfiguration.WISP_PROFILE ? System.nanoTime() : 0; + } + + /** + * DO NOT use this helper inside WispCarrier, + * because lambda may cause class loading. + */ + static T runInCritical(Supplier supplier) { + WispCarrier carrier = WispCarrier.current(); + boolean critical0 = carrier.isInCritical; + carrier.isInCritical = true; + try { + return supplier.get(); + } finally { + carrier.isInCritical = critical0; + } + } + + static boolean runningAsCoroutine(Thread t) { + WispTask task = t == null ? WispCarrier.current().getCurrentTask() : JLA.getWispTask(t); + assert task != null; + // Only carrierThread could create WispTask, and + // the carrierThread will listen on WispTask's wakeup. + // So we can safely letting the non-worker wispTask block the whore Thread. + return !task.isThreadTask(); + } + + static WispCounter getWispCounter(long id) { + return WispConfiguration.WISP_PROFILE ? WispPerfCounterMonitor.INSTANCE.getWispCounter(id) : null; + } + + + // ----------------------------------------------- shutdown support + @Override + public void shutdown() { + if (!SHUTDOWN_UPDATER.compareAndSet(this, false, true)) { + return; + } + for (WispCarrier carrier : carrierEngines) { + deRegisterPerfCounter(carrier); + } + scheduler.execute(new StartShutdown()); + } + + @Override + public List shutdownNow() { + throw new UnsupportedOperationException(); + } + + class StartShutdown extends StealDisabledRunnable { + @Override + public void run() { + WispCarrier.current().runTaskInternal(new NotifyAndWaitTasksForShutdown(), + WispTask.SHUTDOWN_TASK_NAME, null, null); + } + } + + class NotifyAndWaitTasksForShutdown implements Runnable { + @Override + public void run() { + try { + // wait until current 'shutdown wispTask' is the only + // running wispTask on this carrier + while (runningTaskCount != 1) { + List runningTasks = getRunningTasks(); + for (WispTask task : runningTasks) { + if (task.carrier.engine == WispEngine.this + && task.isAlive() + && !WispTask.SHUTDOWN_TASK_NAME.equals(task.getName())) { + task.interrupt(); + } + } + WispCarrier.current().yield(); + } + assert WispTask.SHUTDOWN_TASK_NAME.equals(WispCarrier.current().current.getName()); + detached = true; + //notify all worker to exit + for (WispCarrier carrier : carrierEngines) { + carrier.worker.signal(); + } + shutdownFuture.countDown(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * 1. In Wisp2, each WispCarrier's runningTask is modified when WispTask is stolen, we can't guarantee + * the accuracy of the task set. + * 2. this function is only called in shutdown, so it's not performance sensitive + * 3. this function should only be called by current WispTask + */ + private List getRunningTasks() { + assert WispTask.SHUTDOWN_TASK_NAME.equals(WispCarrier.current().current.getName()); + WispCarrier carrier = WispCarrier.current(); + ArrayList runningTasks = new ArrayList<>(); + boolean isInCritical0 = carrier.isInCritical; + carrier.isInCritical = true; + try { + for (WispTask task : WispTask.id2Task.values()) { + if (task.isAlive() + && task.carrier.engine == WispEngine.this + && !task.isThreadTask()) { + runningTasks.add(task); + } + } + return runningTasks; + } finally { + carrier.isInCritical = isInCritical0; + } + } + } + + @Override + public boolean isShutdown() { + return hasBeenShutdown; + } + + @Override + public boolean isTerminated() { + return detached; + } + + @Override + public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException { + return shutdownFuture.await(timeout, unit); + } + + @Override + public void execute(Runnable command) { + startAsThread(command, "execute task", null); + } + + public List getWispCarrierIds() { + List carriers = new ArrayList<>(); + for (WispCarrier carrier : carrierEngines) { + carriers.add(carrier.getId()); + } + return carriers; + } + + static void registerPerfCounter(WispCarrier carrier) { + WispEngine.runInCritical(() -> { + if (WispConfiguration.WISP_PROFILE) { + WispPerfCounterMonitor.INSTANCE.register(carrier.counter); + } + WispCounterMXBeanImpl.register(carrier.counter); + return null; + }); + } + + static void deRegisterPerfCounter(WispCarrier carrier) { + WispEngine.runInCritical(() -> { + if (WispConfiguration.WISP_PROFILE) { + WispPerfCounterMonitor.INSTANCE.deRegister(carrier.counter); + } + WispCounterMXBeanImpl.deRegister(carrier.counter); + return null; + }); + } + + void startAsThread(Runnable target, String name, Thread thread) { + scheduler.execute(new TaskDispatcher(WispCarrier.current().current.ctxClassLoader, + target, name, thread)); + } + + abstract static class StealDisabledRunnable implements StealAwareRunnable { + @Override + public final boolean isStealEnable() { + return false; + } + } + + private static native void registerNatives(); + + private static native int getProxyUnpark(int[] res); +} diff --git a/src/linux/classes/com/alibaba/wisp/engine/WispEventPump.java b/src/linux/classes/com/alibaba/wisp/engine/WispEventPump.java new file mode 100644 index 0000000000000000000000000000000000000000..2fb78511f2f143a416b3c70d5939cf62bd8c2d8a --- /dev/null +++ b/src/linux/classes/com/alibaba/wisp/engine/WispEventPump.java @@ -0,0 +1,376 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.alibaba.wisp.engine; + +import sun.misc.SharedSecrets; +import sun.nio.ch.IOEventAccess; +import sun.nio.ch.Net; +import sun.nio.ch.SelChImpl; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.channels.SelectableChannel; +import java.nio.channels.SelectionKey; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; +import java.util.concurrent.atomic.AtomicReference; +import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; + + +class WispEventPump { + private static final int LOW_FD_BOUND = 1024 * 10; + private static final int MAX_EVENTS_TO_POLL = 512; + private static final IOEventAccess IOEA; + + private final int epfd; + private final int pipe0; + private final int pipe1; + private final long epollArray; + + static { + sun.nio.ch.IOUtil.load(); + IOEA = SharedSecrets.getIOEventAccess(); + } + + private WispEventPump() { + try { + epfd = IOEA.eventCreate(); + int[] a = new int[2]; + IOEA.socketpair(a); + pipe0 = a[0]; + pipe1 = a[1]; + if (IOEA.eventCtl(epfd, IOEA.eventCtlAdd(), pipe0, Net.POLLIN) != 0) { + throw new IOException("epoll_ctl fail"); + } + } catch (IOException e) { + throw new UncheckedIOException(e); + } + epollArray = IOEA.allocatePollArray(MAX_EVENTS_TO_POLL); + } + + enum Pool { + INSTANCE; + private final int mask; + private final WispEventPump[] pumps; + + Pool() { + int n = Math.max(1, WispConfiguration.WORKER_COUNT / WispConfiguration.POLLER_SHARDING_SIZE); + n = (n & (n - 1)) == 0 ? n : Integer.highestOneBit(n) * 2; // next power of 2 + mask = n - 1; + pumps = new WispEventPump[n]; + for (int i = 0; i < pumps.length; i++) { + pumps[i] = new WispEventPump(); + } + } + + void startPollerThreads() { + int i = 0; + for (WispEventPump pump : pumps) { + Thread t = new Thread(WispEngine.DAEMON_THREAD_GROUP, new Runnable() { + @Override + public void run() { + while (true) { + pump.pollAndDispatchEvents(-1); + } + } + }, "Wisp-Poller-" + i++); + t.setDaemon(true); + t.start(); + } + } + + private static int hash(int x) { + // implementation of Knuth multiplicative algorithm. + return x * (int) 2654435761L; + } + + void registerEvent(WispTask task, SelectableChannel ch, int event) throws IOException { + if (ch != null && ch.isOpen()) { + int fd = ((SelChImpl) ch).getFDVal(); + pumps[hash(fd) & mask].registerEvent(task, fd, event); + } + } + + int epollWaitForWisp(int epfd, long pollArray, int arraySize, long timeout, AtomicReference status, + final Object INTERRUPTED) throws IOException { + return pumps[hash(epfd) & mask].epollWaitForWisp(epfd, pollArray, arraySize, timeout, status, INTERRUPTED); + } + + void interruptEpoll(AtomicReference status, Object INTERRUPTED, int interruptFd) { + WispEventPump.interruptEpoll(status, INTERRUPTED, interruptFd); + } + + WispEventPump getPump(int ord) { + return pumps[ord & mask]; + } + } + + /** + * fd2ReadTask handles all incoming io events like reading and accepting + */ + private final WispTask[] fd2ReadTaskLow = new WispTask[LOW_FD_BOUND]; + private final ConcurrentHashMap fd2ReadTaskHigh = new ConcurrentHashMap<>(); + + /** + * fd2WriteTask handles all outing io events like connecting and writing + */ + private final WispTask[] fd2WriteTaskLow = new WispTask[LOW_FD_BOUND]; + private final ConcurrentHashMap fd2WriteTaskHigh = new ConcurrentHashMap<>(); + + /** + * whether event is a reading event or an accepting event + */ + private boolean isReadEvent(int events) throws IllegalArgumentException { + int event = (events & (Net.POLLCONN | Net.POLLIN | Net.POLLOUT)); + assert Integer.bitCount(event) == 1; + return (events & Net.POLLIN) != 0; + } + + private WispTask[] getFd2TaskLow(int events) { + return isReadEvent(events) ? fd2ReadTaskLow : fd2WriteTaskLow; + } + + private ConcurrentHashMap getFd2TaskHigh(int events) { + return isReadEvent(events) ? fd2ReadTaskHigh : fd2WriteTaskHigh; + } + + private boolean sanityCheck(int fd, WispTask newTask, int events) { + WispTask oldTask = fd < LOW_FD_BOUND ? getFd2TaskLow(events)[fd] : getFd2TaskHigh(events).get(fd); + // If timeout happened, when oldTask finished, + // the oldTask.ch would be nullified(we didn't get chance to remove it) + return (oldTask == null || oldTask == newTask || oldTask.ch == null + || ((SelChImpl) oldTask.ch).getFDVal() != fd); + } + + /** + * All events are guaranteed to be interested in only one direction since + * all registrations are from WispSocket + */ + private void recordTaskByFD(int fd, WispTask task, int events) { + assert sanityCheck(fd, task, events); + if (fd < LOW_FD_BOUND) { + getFd2TaskLow(events)[fd] = task; + } else { + getFd2TaskHigh(events).put(fd, task); + } + } + + private WispTask removeTaskByFD(int fd, int events) { + WispTask task; + if (fd < LOW_FD_BOUND) { + WispTask[] fd2TaskLow = getFd2TaskLow(events); + task = fd2TaskLow[fd]; + fd2TaskLow[fd] = null; + } else { + task = getFd2TaskHigh(events).remove(fd); + } + return task; + } + + private void registerEvent(WispTask task, int fd, int event) throws IOException { + int ev = 0; + // Translates an interest operation set into a native poll event set + if ((event & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0) ev |= Net.POLLIN; + if ((event & SelectionKey.OP_WRITE) != 0) ev |= Net.POLLOUT; + if ((event & SelectionKey.OP_CONNECT) != 0) ev |= Net.POLLCONN; + // When the socket is closed, the poll event will be triggered + ev |= Net.POLLHUP; + // specify the EPOLLONESHOT flag, to tell epoll to disable the associated + // file descriptor after the receipt of an event with epoll_wait + ev |= IOEA.eventOneShot(); + + recordTaskByFD(fd, task, ev); + task.setRegisterEventTime(); + // we can do it multi-thread, because epoll is protected by spin lock in kernel + // When the EPOLLONESHOT flag is specified, it is the caller's responsibility to + // rearm the file descriptor using epoll_ctl with EPOLL_CTL_MOD + int res = IOEA.eventCtl(epfd, IOEA.eventCtlMod(), fd, ev); // rearm + if (res != 0 && !(res == IOEA.noEvent() && (res = IOEA.eventCtl(epfd, IOEA.eventCtlAdd(), fd, ev)) == 0)) { + removeTaskByFD(fd, ev); + task.resetRegisterEventTime(); + throw new IOException("epoll_ctl " + res); + } + } + + /** + * API for coroutine do epoll_wait + * + * @param epfd epoll fd + * @param pollArray epoll array address + * @param arraySize epoll array size + * @param timeout timeout ms + * @param status interrupt status; + * @param INTERRUPTED const indicate for interrupted + * @return selected event num + */ + private int epollWaitForWisp(int epfd, long pollArray, int arraySize, long timeout, + AtomicReference status, final Object INTERRUPTED) throws IOException { + assert pollArray != 0; + WispTask me = WispCarrier.current().current; + if (!WispEngine.runningAsCoroutine(me.getThreadWrapper())) { + return IOEA.eventWait(epfd, pollArray, arraySize, (int) timeout); + } + if (WispConfiguration.MONOLITHIC_POLL) { + if (timeout == 0) { + // return 0 for selectNow(), prevent calling epoll_wait in non-poller thread + // and the application will retry with timeout + return 0; + } + } else { + int updated = IOEA.eventWait(epfd, pollArray, arraySize, 0); + if (timeout == 0 || updated > 0) { + return updated; + } + } + if (WispConfiguration.USE_DIRECT_SELECTOR_WAKEUP && + (status.get() == INTERRUPTED || !(status.get() == null && status.compareAndSet(null, me)))) { + assert status.get() == INTERRUPTED; + return 0; // already epoll_wait(0), no retry needed. + } + + if (WispConfiguration.MONOLITHIC_POLL) { + assert timeout != 0; + me.epollArraySize = arraySize; + me.setEpollEventNum(0); + me.setEpollArray(pollArray); + } + + if (timeout != 0) { + registerEvent(me, epfd, SelectionKey.OP_READ); + WispTask.jdkPark(TimeUnit.MILLISECONDS.toNanos(timeout)); + } + + if (WispConfiguration.USE_DIRECT_SELECTOR_WAKEUP && + !(status.get() == me && status.compareAndSet(me, null))) { + assert status.get() == INTERRUPTED; + } + + if (WispConfiguration.MONOLITHIC_POLL) { + // already polled by poller, see doMonolithicPoll() + me.setEpollArray(0); + return me.getEpollEventNum(); + } else { + return IOEA.eventWait(epfd, pollArray, arraySize, 0); + } + } + + private void doMonolithicPoll(int fd, WispTask task, long epollArray) throws IOException { + assert WispConfiguration.MONOLITHIC_POLL; + task.setEpollEventNum(IOEA.eventWait(fd, epollArray, task.epollArraySize, 0)); + } + + private static void interruptEpoll(AtomicReference status, Object INTERRUPTED, int interruptFd) { + assert WispConfiguration.USE_DIRECT_SELECTOR_WAKEUP; + while (true) { + final Object st = status.get(); + if (st == INTERRUPTED || st == null && status.compareAndSet(null, INTERRUPTED)) { + if (!WispConfiguration.ALL_THREAD_AS_WISP) { + try { + IOEA.interrupt(interruptFd); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + break; + } else if (st != null) { // waiting + assert st instanceof WispTask; + if (status.compareAndSet(st, INTERRUPTED)) { + ((WispTask) st).jdkUnpark(); + break; + } + } + } + } + + private volatile int wakeupCount; + + boolean pollAndDispatchEvents(long timeout) { + boolean wakened = false; + try { + int n = IOEA.eventWait(epfd, epollArray, MAX_EVENTS_TO_POLL, (int) timeout); + while (n-- > 0) { + long eventAddress = IOEA.getEvent(epollArray, n); + int fd = IOEA.getDescriptor(eventAddress); + if (fd == pipe0) { + wakened = true; + // Conservative strategy, wakeup() can never lost + if (WAKEUP_UPDATER.decrementAndGet(this) == 0) { + IOEA.drain(pipe0); + } + continue; + } + int events = IOEA.getEvents(eventAddress); + if ((events & Net.POLLIN) != 0) { + processEvent(fd, true); + } + if ((events & Net.POLLCONN) != 0 || (events & Net.POLLOUT) != 0) { + processEvent(fd, false); + } + } + } catch (Throwable t) { + t.printStackTrace(); + } + return wakened; + } + + + private void processEvent(int fd, boolean isRead) throws IOException { + WispTask task = removeTaskByFD(fd, isRead ? Net.POLLIN : Net.POLLOUT); + if (task != null) { + long epollArray = task.getEpollArray(); + if (isRead && epollArray != 0) { + doMonolithicPoll(fd, task, epollArray); + } else { + task.countWaitSocketIOTime(); + } + task.jdkUnpark(); + } + } + + void wakeup() { + if (WAKEUP_UPDATER.getAndIncrement(this) == 0) { + try { + IOEA.interrupt(pipe1); + } catch (IOException x) { + throw new UncheckedIOException(x); + } + } + } + + volatile WispScheduler.Worker owner; + + boolean tryAcquire(WispScheduler.Worker worker) { + assert WispConfiguration.CARRIER_AS_POLLER; + return owner == null && OWNER_UPDATER.compareAndSet(this, null, worker); + } + + void release(WispScheduler.Worker worker) { + assert owner == worker; + OWNER_UPDATER.lazySet(this, null); + } + + private final static AtomicReferenceFieldUpdater OWNER_UPDATER = + AtomicReferenceFieldUpdater.newUpdater(WispEventPump.class, WispScheduler.Worker.class, "owner"); + private final static AtomicIntegerFieldUpdater WAKEUP_UPDATER = + AtomicIntegerFieldUpdater.newUpdater(WispEventPump.class, "wakeupCount"); +} diff --git a/src/linux/classes/com/alibaba/wisp/engine/WispPerfCounterMonitor.java b/src/linux/classes/com/alibaba/wisp/engine/WispPerfCounterMonitor.java new file mode 100644 index 0000000000000000000000000000000000000000..aa5cbf8a5aeb0158111b08b583f6891f470af7e0 --- /dev/null +++ b/src/linux/classes/com/alibaba/wisp/engine/WispPerfCounterMonitor.java @@ -0,0 +1,195 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.alibaba.wisp.engine; + +import java.io.File; +import java.io.IOException; +import java.text.SimpleDateFormat; +import java.util.*; +import java.util.Map.Entry; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Function; +import java.util.logging.*; + +enum WispPerfCounterMonitor { + INSTANCE; + + private boolean fileHandleEnable = false; + private Map managedEngineCounters; + private Logger wispLog; + + private final SimpleDateFormat localDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + + WispPerfCounterMonitor() { + if (WispConfiguration.WISP_PROFILE) { + managedEngineCounters = new ConcurrentHashMap<>(100); + } + if (WispConfiguration.WISP_PROFILE_LOG_ENABLED) { + String logPath = WispConfiguration.WISP_PROFILE_LOG_PATH; + wispLog = Logger.getLogger(WispPerfCounterMonitor.class.getName()); + FileHandler fileHandler; + try { + // In a log file, record about 24 hours of data + fileHandler = new FileHandler( + logPath == null ? "wisplog%g.log" : logPath + File.separator + "wisplog%g.log", + 12800000, 4, true); + fileHandler.setLevel(Level.INFO); + fileHandler.setFormatter(new java.util.logging.Formatter() { + @Override + public String format(LogRecord record) { + return record.getMessage() + "\n"; + } + }); + wispLog.setUseParentHandlers(false); + wispLog.addHandler(fileHandler); + fileHandleEnable = true; + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + void startDaemon() { + Thread thread = new Thread(WispEngine.DAEMON_THREAD_GROUP, this::perfCounterLoop, "Wisp-Monitor"); + thread.setDaemon(true); + thread.start(); + } + + void register(WispCounter counter) { + if (WispConfiguration.WISP_PROFILE && sun.misc.VM.isBooted()) { + WispPerfCounter wispPerfCounter = new WispPerfCounter(counter); + managedEngineCounters.put(counter.carrier.getId(), wispPerfCounter); + } + } + + void deRegister(WispCounter counter) { + if (WispConfiguration.WISP_PROFILE) { + managedEngineCounters.remove(counter.carrier.getId()); + } + } + + WispCounter getWispCounter(long id) { + WispPerfCounter perfCounter = WispEngine.runInCritical( + () -> managedEngineCounters.get(id)); + if (perfCounter == null) { + return null; + } + perfCounter.storeCurrentWispCounter(); + return perfCounter.prevCounterValue; + } + + private void perfCounterLoop() { + while (true) { + try { + Thread.sleep((long) WispConfiguration.WISP_PROFILE_LOG_INTERVAL_MS); + } catch (InterruptedException e) { + // pass + } + dumpCounter(); + } + } + + private void appendLogString(StringBuilder strb, String dateTime, String item, int workerID, long data) { + strb.append(dateTime) + .append("\t").append(item).append("\t\t") + .append("worker").append(workerID).append("\t\t") + .append(data).append("\n"); + } + + private void dumpCounter() { + if (!fileHandleEnable) { + return; + } + + StringBuilder strb = new StringBuilder(); + long currentTime = System.currentTimeMillis(); + String dateTime = localDateFormat.format(new Date(currentTime)); + int worker = 0; + /* dump Wisp monitor information */ + WispPerfCounter perfCounter; + for (Entry entry : managedEngineCounters.entrySet()) { + perfCounter = entry.getValue(); + appendLogString(strb, dateTime, "completedTaskCount", worker, perfCounter.getCompletedTaskCount()); + appendLogString(strb, dateTime, "unparkFromJvmCount", worker, perfCounter.getUnparkFromJvmCount()); + appendLogString(strb, dateTime, "averageEnqueueTime", worker, perfCounter.getAverageEnqueueTime()); + appendLogString(strb, dateTime, "averageExecutionTime", worker, perfCounter.getAverageExecutionTime()); + appendLogString(strb, dateTime, "averageWaitSocketIOTime", worker, perfCounter.getAverageWaitSocketIOTime()); + appendLogString(strb, dateTime, "averageBlockingTime", worker, perfCounter.getAverageBlockingTime()); + perfCounter.storeCurrentWispCounter(); + worker++; + } + wispLog.info(strb.toString()); + } + + private class WispPerfCounter { + WispCounter counter; + + WispCounter prevCounterValue; + + long getCompletedTaskCount() { + return counter.getCompletedTaskCount() - prevCounterValue.getCompletedTaskCount(); + } + + long getUnparkFromJvmCount() { + return counter.getUnparkFromJvmCount() - prevCounterValue.getUnparkFromJvmCount(); + } + + long getAverageTime(Function timeFunc, Function countFunc) { + long count = countFunc.apply(counter) - countFunc.apply(prevCounterValue); + if (count == 0) { + return 0; + } + long totalNanos = timeFunc.apply(counter) - timeFunc.apply(prevCounterValue); + return totalNanos / count; + } + + long getAverageEnqueueTime() { + return getAverageTime(WispCounter::getTotalEnqueueTime, WispCounter::getEnqueueCount); + } + + long getAverageExecutionTime() { + return getAverageTime(WispCounter::getTotalExecutionTime, WispCounter::getExecutionCount); + } + + long getAverageWaitSocketIOTime() { + return getAverageTime(WispCounter::getTotalWaitSocketIOTime, WispCounter::getWaitSocketIOCount); + } + + long getAverageBlockingTime() { + return getAverageTime(WispCounter::getTotalBlockingTime, WispCounter::getUnparkCount); + } + + void storeCurrentWispCounter() { + if (counter == null) { + return; + } + prevCounterValue.assign(counter); + counter.resetMaxValue(); + } + + WispPerfCounter(WispCounter counter) { + this.counter = counter; + this.prevCounterValue = new WispCounter(); + this.prevCounterValue.assign(counter); + } + } +} diff --git a/src/linux/classes/com/alibaba/wisp/engine/WispScheduler.java b/src/linux/classes/com/alibaba/wisp/engine/WispScheduler.java new file mode 100644 index 0000000000000000000000000000000000000000..141c8ab39dd2587b5540fd1fedb8882b52410c5c --- /dev/null +++ b/src/linux/classes/com/alibaba/wisp/engine/WispScheduler.java @@ -0,0 +1,560 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.alibaba.wisp.engine; + +import sun.misc.JavaLangAccess; +import sun.misc.SharedSecrets; +import sun.misc.UnsafeAccess; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.Arrays; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; + +/** + * Wisp work-stealing scheduler. + * Every worker thread has a {@link ConcurrentLinkedQueue} to + * receive tasks. + * Once local queue is empty, worker will scan + * others' queue, execute stolen task, then check local queue. + * Again and again, until all the queue is empty. + */ +class WispScheduler { + + private static final SchedulingPolicy SCHEDULING_POLICY = WispConfiguration.SCHEDULING_POLICY; + + private final static int IDX_MASK = 0xffff; // ensure positive + private final static int STEAL_HIGH_WATER_LEVEL = 4; + + // instance const + private int PARALLEL; + private int STEAL_RETRY; + private int PUSH_RETRY; + private int HELP_STEAL_RETRY; + private final boolean IS_ROOT_CARRIER; + + // workers could be changed by handOff(), + // add volatile to avoiding workers's elements + // to be allocated in register. + // we could not add volatile volatile modifier + // to array elements, so make the array volatile. + private volatile Worker[] workers; + private final ThreadFactory threadFactory; + private final WispEngine engine; + private int sharedSeed = randomSeed(); + + WispScheduler(int parallelism, ThreadFactory threadFactory, WispEngine group) { + this(parallelism, Math.max(1, parallelism / 2), parallelism, + Math.max(1, parallelism / 4), threadFactory, group, false); + } + + WispScheduler(int parallelism, int stealRetry, int pushRetry, int helpStealRetry, + ThreadFactory threadFactory, WispEngine engine, boolean isRootCarrier) { + assert parallelism > 0; + PARALLEL = parallelism; + STEAL_RETRY = stealRetry; + PUSH_RETRY = pushRetry; + HELP_STEAL_RETRY = helpStealRetry; + IS_ROOT_CARRIER = isRootCarrier; + this.engine = engine; + this.threadFactory = threadFactory; + workers = new Worker[PARALLEL]; + for (int i = parallelism - 1; i >= 0; i--) { + workers[i] = new Worker(i); + workers[i].next = i == PARALLEL - 1 ? null : workers[i + 1]; + } + workers[PARALLEL - 1].next = workers[0]; + if (!isRootCarrier) { + // root worker threads are started in startWispDaemons() + startWorkerThreads(); + } + } + + void startWorkerThreads() { + for (Worker worker : workers) { + worker.thread.start(); + } + } + + private int generateRandom() { + return sharedSeed = nextRandom(sharedSeed); + } + + class Worker implements Runnable { + ConcurrentLinkedQueue taskQueue; + private final TimeOut.TimerManager timerManager; + private final Thread thread; + private final WispEventPump pump; // ONE pump verse N worker relationship + volatile boolean hasBeenHandoff = false; + private Worker next; + + private final static int QL_PROCESSING_TIMER = -1; // idle than ql=0, but not really idle + private final static int QL_POLLING = -1000002; + private final static int QL_IDLE = -2000002; + + volatile int queueLength; + + Worker(int index) { + thread = threadFactory.newThread(this); + WispEngine.carrierThreads.add(thread); + taskQueue = new ConcurrentLinkedQueue<>(); + timerManager = new TimeOut.TimerManager(); + queueLength = 0; + pump = WispConfiguration.CARRIER_AS_POLLER && IS_ROOT_CARRIER ? + WispEventPump.Pool.INSTANCE.getPump(index) : null; + } + + void processTimer() { + timerManager.processTimeoutEventsAndGetWaitDeadline(System.nanoTime()); + } + + @Override + public void run() { + try { + WispCarrier carrier = WispCarrier.current(); + carrier.engine = WispScheduler.this.engine; + carrier.worker = this; + WispScheduler.this.engine.carrierEngines.add(carrier); + WispEngine.registerPerfCounter(carrier); + WispSysmon.INSTANCE.register(carrier); + runCarrier(carrier); + } finally { + WispEngine.carrierThreads.remove(thread); + try { + engine.shutdownBarrier.await(); + } catch (Exception e) { + StringWriter sw = new StringWriter(); + e.printStackTrace(new PrintWriter(sw)); + System.out.println("[Wisp-ERROR] unexpected error " + + "on current" + Thread.currentThread() + "has exception" + + sw.toString()); + } + } + } + + private void runCarrier(final WispCarrier carrier) { + int r = randomSeed(); + Runnable task; + while (true) { + while ((task = pollTask(false)) != null) { + doExec(task); + } + + if (carrier.engine.detached) { + return; + } else if ((task = SCHEDULING_POLICY.steal(this, r = nextRandom(r))) != null) { + doExec(task); + continue; // process local queue + } + doParkOrPolling(); + } + } + + private void doParkOrPolling() { + int st = QL_PROCESSING_TIMER; + if (queueLength != 0 || !LENGTH_UPDATER.compareAndSet(this, 0, st)) { + return; + } + final long now = System.nanoTime(); + final long deadline = timerManager.processTimeoutEventsAndGetWaitDeadline(now); + assert deadline != 0; + if (queueLength == st) { + int update = pump != null && pump.tryAcquire(this) ? QL_POLLING : QL_IDLE; + if (LENGTH_UPDATER.compareAndSet(this, st, update)) { + st = update; + if (taskQueue.peek() == null) { + if (st == QL_IDLE) { + UA.park0(false, deadline < 0 ? 0 : deadline - now); + } else { // st == QL_POLLING + doPolling(deadline, now); + } + } + } + if (update == QL_POLLING) { + pump.release(this); + } + } + LENGTH_UPDATER.addAndGet(this, -st); + } + + private void doPolling(final long deadline, long now) { + while ((deadline < 0 || deadline > now) && + !pump.pollAndDispatchEvents( + deadline < 0 ? -1 : TimeOut.nanos2Millis(deadline - now)) && + queueLength == QL_POLLING) { // if wakened by event, we can still waiting.. + now = deadline < 0 ? now : System.nanoTime(); + } + } + + /** + * @return if it is idle + */ + private boolean doSignalIfNecessary(int len) { + Thread current = JLA.currentThread0(); + if (thread != current) { + if (len == QL_IDLE) { + UA.unpark0(this.thread); + } else if (len == QL_POLLING) { + pump.wakeup(); + } + } + return len < 0; + } + + boolean idleOrPolling() { + return queueLength == QL_IDLE || queueLength == QL_POLLING; + } + + boolean isProcessingTimer() { + return queueLength == QL_PROCESSING_TIMER; + } + + Runnable pollTask(boolean isSteal) { + StealAwareRunnable task = taskQueue.poll(); + if (task != null) { + LENGTH_UPDATER.decrementAndGet(this); + if (isSteal && !task.isStealEnable()) { + // disable steal is a very uncommon case, + // The overhead of re-enqueue is acceptable + // use pushAndSignal rather than offer, + // let the worker execute the task as soon as possible. + pushAndSignal(task); + return null; + } + } + return task; + } + + /** + * @return if it is idle + */ + boolean pushAndSignal(StealAwareRunnable task) { + taskQueue.offer(task); + return doSignalIfNecessary(LENGTH_UPDATER.getAndIncrement(this)); + } + + void signal() { + doSignalIfNecessary(queueLength); + } + + void copyContextFromDetachedCarrier(Worker detachedWorker) { + // copy timers + timerManager.copyTimer(detachedWorker.timerManager.queue); + // drain wispTasks + StealAwareRunnable task; + while ((task = detachedWorker.taskQueue.poll()) != null) { + pushAndSignal(task); + } + } + + WispScheduler theScheduler() { + return WispScheduler.this; + } + } + + /** + * try steal one task from the most busy worker's queue + * + * @param r random seed + */ + private Runnable trySteal(int r) { + Worker busyWorker = null; + for (int i = 0; i < STEAL_RETRY; i++) { + final Worker w = getWorker(r + i); + int ql = w.queueLength; + if (ql >= STEAL_HIGH_WATER_LEVEL) { + Runnable task = w.pollTask(true); + if (task != null) { + return task; + } + } + if (busyWorker == null || ql > busyWorker.queueLength) { + busyWorker = w; + } + } + // If busyCarrier's head is disable steal, we also miss the + // chance of steal from second busy worker.. + // For disable steal is uncommon path, current implementation is good enough.. + return busyWorker == null ? null : busyWorker.pollTask(true); + } + + /** + * Find an idle worker, and push task to it's work queue + * + * @param n retry times + * @param command the task + * @param force push even all workers are busy + * @return success push to a idle worker + */ + private boolean tryPush(int n, StealAwareRunnable command, boolean force) { + assert n > 0; + int r = generateRandom(); + Worker idleWorker = null; + int idleQl = Integer.MAX_VALUE; + + Worker w = getWorker(r); + for (int i = 0; i < n; i++, w = w.next) { + if (w.idleOrPolling()) { + if (command != null) { + w.pushAndSignal(command); + } else { + w.signal(); + } + return true; + } + int ql = w.queueLength; + if (ql < idleQl) { + idleWorker = w; + idleQl = ql; + } + } + if (force) { + assert idleWorker != null && command != null; + idleWorker.pushAndSignal(command); + return true; + } + return false; + } + + private Worker getWorker(int r) { + return workers[(r & IDX_MASK) % PARALLEL]; + } + + /** + * cast thread to worker + * + * @param detachedAsNull treat detached worker as not worker thread if detachedAsNull is true. + * @return null means thread is not considered as a worker + */ + private Worker castToWorker(Thread thread, boolean detachedAsNull) { + if (thread == null) { + return null; + } + Worker worker = JLA.getWispTask(thread).carrier.worker; + if (worker == null || worker.theScheduler() != this) { + return null; + } else { + return detachedAsNull && worker.hasBeenHandoff ? null : worker; + } + } + + void addTimer(TimeOut timeOut, Thread current) { + Worker worker = castToWorker(current, true); + if (worker != null) { + worker.timerManager.addTimer(timeOut); + } else { + tryPush(1, new StealAwareRunnable() { + @Override + public void run() { + //Adding timer to detached worker is ok since we rely on + //interrupt to wakeup all wispTasks in shutdown + Worker worker = castToWorker(JLA.currentThread0(), false); + assert worker != null; + worker.timerManager.addTimer(timeOut); + } + }, true); + } + } + + void cancelTimer(TimeOut timeOut, Thread current) { + Worker worker = castToWorker(current, true); + if (worker != null) { + worker.timerManager.cancelTimer(timeOut); + } + } + + /** + * Run the command on the specified thread. + * Used to implement Thread affinity for scheduler. + * When execute with detached worker thread, we try to execute this task by + * other workers, if this step failed command would be marked as can't be stolen, + * then we push this command to detached worker. + * + * @param command the code + * @param thread target thread + */ + void executeWithWorkerThread(StealAwareRunnable command, Thread thread) { + final Worker worker = castToWorker(thread, false); + boolean stealEnable = command.isStealEnable(); + if (worker == null || worker.hasBeenHandoff && stealEnable) { + // detached worker try to execute from global scheduler at first + execute(command); + } else { + SCHEDULING_POLICY.enqueue(worker, stealEnable, command); + } + } + + enum SchedulingPolicy { + PULL { + // always enqueue to the bounded worker, but workers will steal tasks from each other. + @Override + void enqueue(Worker worker, boolean stealEnable, StealAwareRunnable command) { + WispScheduler scheduler = worker.theScheduler(); + if (!worker.pushAndSignal(command) && stealEnable && scheduler.HELP_STEAL_RETRY > 0) { + scheduler.signalIdleWorkerToHelpSteal(); + } + } + + @Override + Runnable steal(Worker worker, int r) { + return worker.theScheduler().trySteal(r); + } + }, + PUSH { + // never try to pull other worker's queues, but choose an idle worker when we're enqueueing + @Override + void enqueue(Worker worker, boolean stealEnable, StealAwareRunnable command) { + WispScheduler scheduler = worker.theScheduler(); + if (stealEnable + && !(worker.idleOrPolling() || worker.isProcessingTimer()) + && scheduler.STEAL_RETRY > 0 + && scheduler.tryPush(scheduler.STEAL_RETRY, command, false)) { + return; + } + worker.pushAndSignal(command); + } + + @Override + Runnable steal(Worker worker, int r) { + return null; + } + }; + + abstract void enqueue(Worker worker, boolean stealEnable, StealAwareRunnable command); + + abstract Runnable steal(Worker worker, int r); + } + + private void signalIdleWorkerToHelpSteal() { + tryPush(HELP_STEAL_RETRY, null, false); + } + + /** + * Executes the given command at some time in the future. + * + * @param command the runnable task + * @throws NullPointerException if command is null + */ + public void execute(StealAwareRunnable command) { + tryPush(PUSH_RETRY, command, true); + } + + /** + * Detach worker and create a new worker to replace it. + * This function should only be called by Wisp-Sysmon + */ + void handOffWorkerThread(Thread thread) { + assert WispSysmon.WISP_SYSMON_NAME.equals(Thread.currentThread().getName()); + Worker worker = castToWorker(thread, true); + if (worker != null && !worker.hasBeenHandoff) { + worker.hasBeenHandoff = true; + worker.pushAndSignal(new StealAwareRunnable() { + @Override + public void run() { + } + }); // ensure `detached` visibility + worker.thread.setName(worker.thread.getName() + " (HandOff)"); + Worker[] cs = Arrays.copyOf(this.workers, workers.length); + Worker last = cs[PARALLEL - 1]; + for (int i = 0; i < PARALLEL; i++) { + if (cs[i] == worker) { + cs[i] = new Worker(i); + // tasks blocked on detached worker may not be scheduled in time + // because it's in long-time syscall, so we try our best to delegate + // all context to the new worker + cs[i].copyContextFromDetachedCarrier(worker); + cs[i].next = worker.next; + last.next = cs[i]; + cs[i].thread.start(); + break; + } + last = cs[i]; + } + workers = cs; + WispEngine.deRegisterPerfCounter(JLA.getWispTask(thread).carrier); + } + } + + /** + * Check if current processor number exceeds workers.length, if so we add new workers + * to this scheduler. + * This function should only be called by Wisp-Sysmon + */ + void checkAndGrowWorkers(int availableProcessors) { + assert WispSysmon.WISP_SYSMON_NAME.equals(Thread.currentThread().getName()); + if (availableProcessors <= workers.length) { + return; + } + double growFactor = (double) availableProcessors / (double) workers.length; + Worker[] cs = Arrays.copyOf(this.workers, availableProcessors); + for (int i = availableProcessors - 1; i >= workers.length; i--) { + if (cs[i] == null) { + cs[i] = new Worker(i); + cs[i].next = i == availableProcessors - 1 ? cs[0] : cs[i + 1]; + } + } + cs[workers.length - 1].next = cs[workers.length]; + for (int i = workers.length; i < availableProcessors; i++) { + cs[i].thread.start(); + } + int originLength = workers.length; + workers = cs; + adjustParameters(originLength, growFactor); + } + + private void adjustParameters(int originLength, double growFactor) { + PARALLEL = Integer.min((int) Math.round((double) originLength * growFactor), + workers.length); + PUSH_RETRY = ((int) (PARALLEL * growFactor)); + STEAL_RETRY = ((int) (STEAL_RETRY * growFactor)); + HELP_STEAL_RETRY = ((int) (HELP_STEAL_RETRY * growFactor)); + } + + private static void doExec(Runnable task) { + try { + task.run(); + } catch (Throwable t) { + t.printStackTrace(); + } + } + + private static int nextRandom(int r) { + r ^= r << 13; + r ^= r >>> 17; + return r ^ (r << 5); + } + + private static int randomSeed() { + int r = 0; + while (r == 0) { + r = (int) System.nanoTime(); + } + return r; + } + + private static final AtomicIntegerFieldUpdater LENGTH_UPDATER = + AtomicIntegerFieldUpdater.newUpdater(Worker.class, "queueLength"); + private static final UnsafeAccess UA = SharedSecrets.getUnsafeAccess(); + private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess(); +} diff --git a/src/linux/classes/com/alibaba/wisp/engine/WispSysmon.java b/src/linux/classes/com/alibaba/wisp/engine/WispSysmon.java new file mode 100644 index 0000000000000000000000000000000000000000..c182aca6779a2ac816bd1fa7e677618635db5205 --- /dev/null +++ b/src/linux/classes/com/alibaba/wisp/engine/WispSysmon.java @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.alibaba.wisp.engine; + +import sun.misc.JavaLangAccess; +import sun.misc.SharedSecrets; +import sun.misc.UnsafeAccess; + +import java.util.*; +import java.util.concurrent.ConcurrentSkipListSet; +import java.util.concurrent.TimeUnit; + +enum WispSysmon { + INSTANCE; + + static { + registerNatives(); + } + + private final Set carriers = new ConcurrentSkipListSet<>(); + final static String WISP_SYSMON_NAME = "Wisp-Sysmon"; + + void startDaemon() { + if (WispConfiguration.ENABLE_HANDOFF) { + assert WispConfiguration.HANDOFF_POLICY != null; + Thread thread = new Thread(WispEngine.DAEMON_THREAD_GROUP, + WispSysmon.INSTANCE::sysmonLoop, WISP_SYSMON_NAME); + thread.setDaemon(true); + thread.start(); + } + } + + void register(WispCarrier carrier) { + if (WispConfiguration.ENABLE_HANDOFF) { + carriers.add(carrier); + } + } + + private void sysmonLoop() { + final long interval = TimeUnit.MICROSECONDS.toNanos(WispConfiguration.SYSMON_TICK_US); + final long carrierCheckRate = TimeUnit.MICROSECONDS.toNanos(WispConfiguration.SYSMON_CARRIER_GROW_TICK_US); + final long checkCarrierOnNthTick = carrierCheckRate / interval; + final boolean checkCarrier = WispConfiguration.CARRIER_GROW && checkCarrierOnNthTick > 0 + // Detach carrier's worker cnt is not specified by configuration + && WispConfiguration.WORKER_COUNT == Runtime.getRuntime().availableProcessors(); + long nextTick = System.nanoTime() + interval; + int tick = 0; + + while (true) { + long timeout = nextTick - System.nanoTime(); + if (timeout > 0) { + do { + UA.park0(false, timeout); + } while ((timeout = nextTick - System.nanoTime()) > 0); + handleLongOccupation(); + if (checkCarrier && tick++ == checkCarrierOnNthTick) { + WispEngine.WISP_ROOT_ENGINE.scheduler.checkAndGrowWorkers(Runtime.getRuntime().availableProcessors()); + tick = 0; + } + } // else: we're too slow, skip a tick + nextTick += interval; + } + } + + private final List longOccupationEngines = new ArrayList<>(); + + /** + * Handle a WispTask occupied a worker thread for long time. + */ + private void handleLongOccupation() { + for (WispCarrier carrier : carriers) { + if (carrier.terminated) { + // remove in iteration is OK for ConcurrentSkipListSet + carriers.remove(carrier); + continue; + } + if (carrier.isRunning() && carrier.schedTick == carrier.lastSchedTick) { + longOccupationEngines.add(carrier); + } + carrier.lastSchedTick = carrier.schedTick; + } + + if (!longOccupationEngines.isEmpty()) { + Iterator itr = longOccupationEngines.iterator(); + while (itr.hasNext()) { + WispCarrier carrier = itr.next(); + WispConfiguration.HANDOFF_POLICY.handle(carrier, !itr.hasNext()); + itr.remove(); + } + } + assert longOccupationEngines.isEmpty(); + } + + enum Policy { + HAND_OFF { // handOff the worker + @Override + void handle(WispCarrier carrier, boolean isLast) { + if (JLA.isInSameNative(carrier.thread)) { + carrier.handOff(); + INSTANCE.carriers.remove(carrier); + } + } + }, + PREEMPT { // insert a yield() after next safepoint + @Override + void handle(WispCarrier carrier, boolean isLast) { + markPreempted(carrier.thread, isLast); + } + }, + ADAPTIVE { // depends on thread status + @Override + void handle(WispCarrier carrier, boolean isLast) { + if (JLA.isInSameNative(carrier.thread)) { + carrier.handOff(); + INSTANCE.carriers.remove(carrier); + } else { + markPreempted(carrier.thread, isLast); + } + } + }; + + abstract void handle(WispCarrier carrier, boolean isLast); + + } + + private static native void registerNatives(); + + + /** + * Mark the thread as running single wispTask in java too much time. + * And the Thread.yield() invocation will be emitted after next safepoint. + * + * @param thread the thread to mark + * @param force fire a force_safepoint immediately + */ + private static native void markPreempted(Thread thread, boolean force); + + private static final UnsafeAccess UA = SharedSecrets.getUnsafeAccess(); + private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess(); +} diff --git a/src/linux/classes/com/alibaba/wisp/engine/WispTask.java b/src/linux/classes/com/alibaba/wisp/engine/WispTask.java new file mode 100644 index 0000000000000000000000000000000000000000..9ce1c4e8da03b8013044145d4c1abaca7d43f1a5 --- /dev/null +++ b/src/linux/classes/com/alibaba/wisp/engine/WispTask.java @@ -0,0 +1,635 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.alibaba.wisp.engine; + +import sun.misc.SharedSecrets; +import sun.misc.UnsafeAccess; + +import java.dyn.Coroutine; +import java.dyn.CoroutineExitException; +import java.nio.channels.SelectableChannel; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; +import java.util.concurrent.atomic.AtomicLongFieldUpdater; +import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; + + +/** + * {@link WispTask} provides high-level semantics of {link @Coroutine} + *

+ * Create {@link WispTask} via {@link WispEngine#dispatch(Runnable)} (Callable, String)} to make + * blocking IO operation in {@link WispTask}s to become concurrent. + *

+ * The creator and a newly created {@link WispTask} will automatically have parent-children relationship. + * When the child gets blocked on something, the {@link WispCarrier} will try to execute parent first. + *

+ * A {@link WispTask}'s exit will wake up the waiting parent. + */ +public class WispTask implements Comparable { + private final static AtomicInteger idGenerator = new AtomicInteger(); + + static final Map id2Task = new ConcurrentHashMap<>(360); + // global table used for all WispCarriers + + static WispTask fromId(int id) { + WispCarrier carrier = WispCarrier.current(); + boolean isInCritical0 = carrier.isInCritical; + carrier.isInCritical = true; + try { + return id2Task.get(id); + } finally { + carrier.isInCritical = isInCritical0; + } + } + + static void cleanExitedTasks(List tasks) { + if (!tasks.isEmpty()) { + WispCarrier carrier = tasks.get(0).carrier; + boolean isInCritical0 = carrier.isInCritical; + carrier.isInCritical = true; + try { + for (WispTask t : tasks) { + id2Task.remove(t.id); + t.cleanup(); + } + } finally { + carrier.isInCritical = isInCritical0; + } + } + } + + static void cleanExitedTask(WispTask task) { + WispCarrier carrier = WispCarrier.current(); + boolean isInCritical0 = carrier.isInCritical; + carrier.isInCritical = true; + try { + // cleanup shouldn't be executed for thread task + id2Task.remove(task.id); + } finally { + carrier.isInCritical = isInCritical0; + } + } + + static void trackTask(WispTask task) { + WispCarrier carrier = WispCarrier.current(); + boolean isInCritical0 = carrier.isInCritical; + carrier.isInCritical = true; + try { + id2Task.put(task.id, task); + } finally { + carrier.isInCritical = isInCritical0; + } + } + + private final int id; + + enum Status { + ALIVE, // ALIVE + ZOMBIE // exited + } + + private Runnable runnable; // runnable for created task + + /** + * Task is running in that carrier. + */ + volatile WispCarrier carrier; + + private String name; + final Coroutine ctx; // the low-level coroutine implement + Status status = Status.ALIVE; + SelectableChannel ch; // the interesting channel + TimeOut timeOut; // related timer + ClassLoader ctxClassLoader; + + private final boolean isThreadTask; + private boolean isThreadAsWisp; + + private Thread threadWrapper; // thread returned by Thread::currentThread() + private volatile int interrupted; // 0 means not interrupted + private volatile int alreadyCheckNativeInterrupt; + + private volatile int jdkParkStatus; + private volatile int jvmParkStatus; + volatile int stealLock; + private WispTask from; + /** + * WispTask execution wrapper for schduler should only be used in wakupTask + */ + final StealAwareRunnable resumeEntry; + // counter printed by jstack + private int activeCount; + int stealCount; + int stealFailureCount; + private int preemptCount; + // perf monitor + private long enqueueTime; + private long parkTime; + private long blockingTime; + private long registerEventTime; + + // monolithic epoll support + private volatile long epollArray; + private volatile int epollEventNum; + int epollArraySize; + + WispTask(WispCarrier carrier, Coroutine ctx, boolean isRealTask, boolean isThreadTask) { + this.isThreadTask = isThreadTask; + this.id = isRealTask ? idGenerator.addAndGet(1) : -1; + setCarrier(carrier); + if (isRealTask) { + this.ctx = ctx != null ? ctx : new CacheableCoroutine(WispConfiguration.STACK_SIZE); + this.ctx.setWispTask(id, this, carrier); + } else { + this.ctx = null; + } + resumeEntry = isThreadTask ? null : carrier.createResumeEntry(this); + } + + void reset(Runnable runnable, String name, Thread thread, ClassLoader ctxLoader) { + assert ctx != null; + this.status = Status.ALIVE; + this.runnable = runnable; + this.name = name; + interrupted = 0; + ctxClassLoader = ctxLoader; + ch = null; + enqueueTime = 0; + parkTime = 0; + blockingTime = 0; + registerEventTime = 0; + + activeCount = 0; + stealCount = 0; + stealFailureCount = 0; + preemptCount = 0; + + // thread status + if (thread != null) { // calling from Thread.start() + NATIVE_INTERRUPTED_UPDATER.lazySet(this, 1); + isThreadAsWisp = true; + WispEngine.JLA.setWispTask(thread, this); + threadWrapper = thread; + } else { + // for WispThreadWrapper, skip native interrupt check + NATIVE_INTERRUPTED_UPDATER.lazySet(this, 0); + isThreadAsWisp = false; + if (threadWrapper == null) { + threadWrapper = new WispThreadWrapper(this); + } + WispEngine.JLA.setWispAlive(threadWrapper, true); + } + assert WispEngine.JLA.getWispTask(threadWrapper) == this; + + if (!isThreadTask() && name != null && !threadWrapper.getName().equals(name)) { + threadWrapper.setName(name); + } + } + + void setCarrier(WispCarrier carrier) { + CARRIER_UPDATER.lazySet(this, carrier); + } + + private void cleanup() { + setCarrier(null); + threadWrapper = null; + ctxClassLoader = null; + } + + class CacheableCoroutine extends Coroutine { + CacheableCoroutine(long stacksize) { + super(stacksize); + } + + @Override + protected void run() { + while (true) { + assert WispCarrier.current() == carrier; + assert carrier.current == WispTask.this; + if (runnable != null) { + Throwable throwable = null; + try { + runOutsideWisp(runnable); + } catch (Throwable t) { + throwable = t; + } finally { + assert timeOut == null; + runnable = null; + WispEngine.JLA.setWispAlive(threadWrapper, false); + if (isThreadAsWisp) { + ThreadAsWisp.exit(threadWrapper); + } + if (throwable instanceof CoroutineExitException) { + throw (CoroutineExitException) throwable; + } + carrier.taskExit(); + } + } else { + carrier.schedule(); + } + } + } + } + + /** + * Mark if wisp is running internal scheduling code or user code, this would + * be used in preempt to identify if it's okay to preempt + * Modify Coroutine::is_usermark_frame accordingly if you need to change this + * method, because it's name and sig are used + */ + private static void runOutsideWisp(Runnable runnable) { + runnable.run(); + } + + /** + * Switch task. we need the information of {@code from} task param + * to do classloader switch etc.. + *

+ * {@link #stealLock} is used in {@link WispCarrier#steal(WispTask)} . + */ + static boolean switchTo(WispTask current, WispTask next) { + assert next.ctx != null; + assert WispCarrier.current() == current.carrier; + assert current.carrier == next.carrier; + next.activeCount++; + assert current.isThreadTask() || next.isThreadTask(); + next.from = current; + STEAL_LOCK_UPDATER.lazySet(next, 1); + // store load barrier is not necessary + boolean res = current.carrier.thread.getCoroutineSupport().unsafeSymmetricYieldTo(next.ctx); + assert current.stealLock != 0; + STEAL_LOCK_UPDATER.lazySet(current.from, 0); + assert WispCarrier.current() == current.carrier; + assert current.carrier.current == current; + return res; + } + + /** + * @return {@code false} if current {@link WispTask} is thread-emulated. + */ + boolean isThreadTask() { + return isThreadTask; + } + + /** + * Let currently executing task sleep for specified number of milliseconds. + *

+ * May be wakened up early by an available IO. + */ + static void sleep(long ms) { + if (ms < 0) throw new IllegalArgumentException(); + + if (ms == 0) { + WispCarrier.current().yield(); + } else { + WispCarrier.current().unregisterEvent(); + jdkPark(TimeUnit.MILLISECONDS.toNanos(ms)); + } + } + + @Override + public String toString() { + return "WispTask" + id + "(" + + "name=" + name + ')' + + "{status=" + status + "/" + + jdkParkStatus + ", " + + '}'; + } + + public String getName() { + return name; + } + + + private static final int + WAITING = -1, // was blocked + FREE = 0, // the Initial Park status + PERMITTED = 1; // another task give a permit to make the task not block at next park() + + static final String SHUTDOWN_TASK_NAME = "SHUTDOWN_TASK"; + + boolean isAlive() { + return status != Status.ZOMBIE; + } + + /** + * If a permit is available, it will be consumed and this function returns + * immediately; otherwise + * current task will become blocked until {@link #unpark()} ()} happens. + * + * @param timeoutNano <= 0 park forever + * else park with given timeout + */ + private void parkInternal(long timeoutNano, boolean fromJvm) { + if (timeoutNano > 0 && timeoutNano < WispConfiguration.MIN_PARK_NANOS) { + carrier.yield(); + return; + } + final AtomicIntegerFieldUpdater statusUpdater = fromJvm ? JVM_PARK_UPDATER : JDK_PARK_UPDATER; + final boolean isInCritical0 = carrier.isInCritical; + carrier.isInCritical = true; + try { + carrier.getCounter().incrementParkCount(); + for (;;) { + int s = statusUpdater.get(this); + assert s != WAITING; // if parkStatus == WAITING, should already blocked + + if (s == FREE && statusUpdater.compareAndSet(this, FREE, WAITING)) { + // may become PERMITTED here; need retry. + // another thread unpark here is ok: + // current task is put to unpark queue, + // and will wake up eventually + if (WispEngine.runningAsCoroutine(threadWrapper) && timeoutNano > 0) { + carrier.addTimer(timeoutNano + System.nanoTime(), fromJvm); + } + carrier.isInCritical = isInCritical0; + try { + if (WispEngine.runningAsCoroutine(threadWrapper)) { + setParkTime(); + carrier.schedule(); + } else { + UA.park0(false, timeoutNano < 0 ? 0 : timeoutNano); + } + } finally { + carrier.isInCritical = true; + if (timeoutNano > 0) { + carrier.cancelTimer(); + } + // we'may direct wakeup by current carrier + // the statue may be still WAITING.. + statusUpdater.lazySet(this, FREE); + } + break; + } else if (s == PERMITTED && + (statusUpdater.compareAndSet(this, PERMITTED, FREE))) { + // consume the permit + break; + } + } + } finally { + carrier.isInCritical = isInCritical0; + } + } + + /** + * If the thread was blocked on {@link #park(long)} then it will unblock. + * Otherwise, its next call to {@link #park(long)} is guaranteed not to block. + */ + private void unparkInternal(boolean fromJvm) { + AtomicIntegerFieldUpdater statusUpdater = fromJvm ? JVM_PARK_UPDATER : JDK_PARK_UPDATER; + for (;;) { + int s = statusUpdater.get(this); + if (s == WAITING && statusUpdater.compareAndSet(this, WAITING, FREE)) { + if (WispEngine.runningAsCoroutine(threadWrapper)) { + recordOnUnpark(fromJvm); + carrier.wakeupTask(this); + } else { + UA.unpark0(threadWrapper); + } + break; + } else if (s == PERMITTED || + (s == FREE && statusUpdater.compareAndSet(this, FREE, PERMITTED))) { + // add a permit + break; + } + } + } + + /** + * Park Invoked by jdk, include IO, JUC etc.. + */ + static void jdkPark(long timeoutNano) { + WispCarrier.current().getCurrentTask().parkInternal(timeoutNano, false); + } + + void jdkUnpark() { + unparkInternal(false); + } + + /** + * Invoked by VM to support coroutine switch in object monitor case. + */ + private static void park(long timeoutNano) { + WispCarrier.current().getCurrentTask().parkInternal(timeoutNano, true); + } + + void unpark() { + unparkInternal(true); + } + + // direct called by jvm runtime if UseDirectUnpark + static void unparkById(int id) { + WispTask t = fromId(id); + if (t != null) { + t.unpark(); + } + } + + void interrupt() { + // For JSR166. Unpark even if interrupt status was already set. + interrupted = 1; + unpark(); + jdkUnpark(); + } + + private static void interruptById(int id) { + WispTask t = fromId(id); + if (t != null) { + t.interrupt(); + } + } + + boolean isInterrupted() { + return interrupted != 0; + } + + boolean testInterruptedAndClear(boolean clear) { + boolean nativeInterrupt = false; + if (alreadyCheckNativeInterrupt == 0 && // only do it once + NATIVE_INTERRUPTED_UPDATER.compareAndSet(this, 0, 1) && + !isInterrupted()) { + nativeInterrupt = checkAndClearNativeInterruptForWisp(threadWrapper); + } + boolean res = interrupted != 0 || nativeInterrupt; + if (res && clear) { + INTERRUPTED_UPDATER.lazySet(this, 0); + } + // return old interrupt status. + return res; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + WispTask t = (WispTask) o; + return Objects.equals(id, t.id); + } + + @Override + public int hashCode() { + return Objects.hash(id); + } + + public Thread getThreadWrapper() { + return threadWrapper; + } + + void setThreadWrapper(Thread thread) { + threadWrapper = thread; + WispEngine.JLA.setWispTask(thread, this); + } + + void resetThreadWrapper() { + if (isThreadAsWisp) { + threadWrapper = null; + } + } + + @Override + public int compareTo(WispTask o) { + return Integer.compare(this.id, o.id); + } + + long getEpollArray() { + return epollArray; + } + + void setEpollArray(long epollArray) { + EPOLL_ARRAY_UPDATER.lazySet(this, epollArray); + } + + int getEpollEventNum() { + return epollEventNum; + } + + void setEpollEventNum(int epollEventNum) { + EPOLL_EVENT_NUM_UPDATER.lazySet(this, epollEventNum); + } + + void updateEnqueueTime() { + if (!WispConfiguration.WISP_PROFILE) { + return; + } + // in wisp2, if the task is stealed unsuccessfully, it will be put into queue again + if (enqueueTime != 0) { + return; + } + enqueueTime = System.nanoTime(); + } + + long getEnqueueTime() { + return enqueueTime; + } + + void resetEnqueueTime() { + enqueueTime = 0; + } + + void setRegisterEventTime() { + // only count the time which is spent on WispTask by service + registerEventTime = (!WispConfiguration.WISP_PROFILE || isThreadTask) ? 0 : System.nanoTime(); + } + + void resetRegisterEventTime() { + registerEventTime = 0; + } + + void countWaitSocketIOTime() { + if (registerEventTime != 0) { + carrier.counter.incrementTotalWaitSocketIOTime(System.nanoTime() - registerEventTime); + registerEventTime = 0; + } + } + + private void setParkTime() { + parkTime = (!WispConfiguration.WISP_PROFILE || isThreadTask) ? 0 : System.nanoTime(); + } + + /* When unpark is called, the time is set. + * Since the unpark may be called by non-worker thread, the count is delayed. + */ + private void recordOnUnpark(boolean fromJVM) { + if (!WispConfiguration.WISP_PROFILE) { + return; + } + if (parkTime != 0) { + blockingTime = System.nanoTime() - parkTime; + if (blockingTime < 0) { + blockingTime = 0; + } + parkTime = 0; + } + if (fromJVM) { + carrier.counter.incrementUnparkFromJvmCount(); + } + } + + void countExecutionTime(long beginTime) { + // TaskExit set beginTime to 0, and calls schedule, + // then beginTime is 0. It need to skip it. + if (!WispConfiguration.WISP_PROFILE || beginTime == 0) { + return; + } + carrier.counter.incrementTotalExecutionTime(System.nanoTime() - beginTime); + if (blockingTime != 0) { + carrier.counter.incrementTotalBlockingTime(blockingTime); + blockingTime = 0; + } + } + + StackTraceElement[] getStackTrace() { + return this.ctx.getCoroutineStack(); + } + + private static final AtomicReferenceFieldUpdater CARRIER_UPDATER; + private static final AtomicIntegerFieldUpdater JVM_PARK_UPDATER; + private static final AtomicIntegerFieldUpdater JDK_PARK_UPDATER; + private static final AtomicIntegerFieldUpdater INTERRUPTED_UPDATER; + private static final AtomicIntegerFieldUpdater NATIVE_INTERRUPTED_UPDATER; + private static final AtomicIntegerFieldUpdater STEAL_LOCK_UPDATER; + private static final AtomicLongFieldUpdater EPOLL_ARRAY_UPDATER; + private static final AtomicIntegerFieldUpdater EPOLL_EVENT_NUM_UPDATER; + private static final UnsafeAccess UA = SharedSecrets.getUnsafeAccess(); + + private static native void registerNatives(); + + // only for wisp to clear the native interrupt, for parallel interrupt problem. + private static native boolean checkAndClearNativeInterruptForWisp(Thread cur); + + static { + CARRIER_UPDATER = AtomicReferenceFieldUpdater.newUpdater(WispTask.class, WispCarrier.class, "carrier"); + JVM_PARK_UPDATER = AtomicIntegerFieldUpdater.newUpdater(WispTask.class, "jvmParkStatus"); + JDK_PARK_UPDATER = AtomicIntegerFieldUpdater.newUpdater(WispTask.class, "jdkParkStatus"); + INTERRUPTED_UPDATER = AtomicIntegerFieldUpdater.newUpdater(WispTask.class, "interrupted"); + NATIVE_INTERRUPTED_UPDATER = AtomicIntegerFieldUpdater.newUpdater(WispTask.class, "alreadyCheckNativeInterrupt"); + STEAL_LOCK_UPDATER = AtomicIntegerFieldUpdater.newUpdater(WispTask.class, "stealLock"); + EPOLL_ARRAY_UPDATER = AtomicLongFieldUpdater.newUpdater(WispTask.class, "epollArray"); + EPOLL_EVENT_NUM_UPDATER = AtomicIntegerFieldUpdater.newUpdater(WispTask.class, "epollEventNum"); + registerNatives(); + } +} diff --git a/src/linux/classes/com/alibaba/wisp/engine/WispThreadWrapper.java b/src/linux/classes/com/alibaba/wisp/engine/WispThreadWrapper.java new file mode 100644 index 0000000000000000000000000000000000000000..5675dad24a96550ffaf5cc13dd661da3ed1baaff --- /dev/null +++ b/src/linux/classes/com/alibaba/wisp/engine/WispThreadWrapper.java @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.alibaba.wisp.engine; + +import sun.misc.JavaLangAccess; +import sun.misc.SharedSecrets; +import sun.reflect.CallerSensitive; + +import java.dyn.CoroutineSupport; + +/** + * An wrapper of {@link Thread} let every {@link WispTask} get different thread + * object from {@link Thread#currentThread()}. + * In this way, we make the listed class (not only but include) behave expected without + * changing their code. + *

+ * 1. {@link ThreadLocal} + * 2. {@link java.util.concurrent.locks.AbstractQueuedSynchronizer} based synchronizer + * 3. Netty's judgment of weather we are running in it's worker thread. + */ +class WispThreadWrapper extends Thread { + + WispThreadWrapper(WispTask task) { + JLA.setWispTask(this, task); + setName(task.getName()); + } + + @Override + public CoroutineSupport getCoroutineSupport() { + return JLA.getWispTask(this).carrier.thread.getCoroutineSupport(); + } + + @Override + public void start() { + throw new UnsupportedOperationException(); + } + + + @Override + @Deprecated + public void destroy() { + } + + @Override + @Deprecated + public int countStackFrames() { + return JLA.getWispTask(this).carrier.thread.countStackFrames(); + } + + @Override + @CallerSensitive + public ClassLoader getContextClassLoader() { + return JLA.getWispTask(this).ctxClassLoader; + } + + @Override + public void setContextClassLoader(ClassLoader cl) { + JLA.getWispTask(this).ctxClassLoader = cl; + } + + @Override + public StackTraceElement[] getStackTrace() { + return super.getStackTrace(); + } + + @Override + public long getId() { + return super.getId(); + } + + @Override + public State getState() { + return JLA.getWispTask(this).carrier.thread.getState(); + } + + @Override + public UncaughtExceptionHandler getUncaughtExceptionHandler() { + return JLA.getWispTask(this).carrier.thread.getUncaughtExceptionHandler(); + } + + @Override + public void setUncaughtExceptionHandler(UncaughtExceptionHandler eh) { + JLA.getWispTask(this).carrier.thread.setUncaughtExceptionHandler(eh); + } + + @Override + public String toString() { + return "WispThreadWrapper{" + + "wispTask=" + JLA.getWispTask(this) + + '}'; + } + + private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess(); + +} diff --git a/src/linux/classes/sun/nio/ch/IOEvent.java b/src/linux/classes/sun/nio/ch/IOEvent.java new file mode 100644 index 0000000000000000000000000000000000000000..179d307675141deeef19edc2e1f7ab3bdf7500bb --- /dev/null +++ b/src/linux/classes/sun/nio/ch/IOEvent.java @@ -0,0 +1,9 @@ +package sun.nio.ch; + +public class IOEvent { + + public static Class eventClass() { + return EPollPort.class; + } + +} \ No newline at end of file diff --git a/src/linux/classes/sun/nio/ch/WispServerSocketImpl.java b/src/linux/classes/sun/nio/ch/WispServerSocketImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..9a17cf6860301438d92d7bcb6abce104d4876132 --- /dev/null +++ b/src/linux/classes/sun/nio/ch/WispServerSocketImpl.java @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package sun.nio.ch; + +import sun.misc.SharedSecrets; +import sun.misc.WispEngineAccess; + +import java.io.IOException; +import java.net.*; +import java.nio.channels.SelectionKey; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; +import java.util.concurrent.TimeUnit; + + +// Make a server socket channel be like a socket and yield on block + +public class WispServerSocketImpl +{ + private static WispEngineAccess WEA = SharedSecrets.getWispEngineAccess(); + + private WispSocketLockSupport wispSocketLockSupport = new WispSocketLockSupport(); + // The channel being adapted + private ServerSocketChannelImpl ssc = null; + + // Timeout "option" value for accepts + private volatile int timeout = 0; + + public WispServerSocketImpl() { + } + + public void bind(SocketAddress local) throws IOException { + bind(local, 50); + } + + public void bind(SocketAddress local, int backlog) throws IOException { + if (local == null) + local = new InetSocketAddress(0); + try { + getChannelImpl().bind(local, backlog); + } catch (Exception x) { + Net.translateException(x); + } + } + + public InetAddress getInetAddress() { + if (ssc == null || !ssc.isBound()) + return null; + return Net.getRevealedLocalAddress(ssc.localAddress()).getAddress(); + } + + public int getLocalPort() { + if (ssc == null || !ssc.isBound()) + return -1; + return Net.asInetSocketAddress(ssc.localAddress()).getPort(); + } + + public Socket accept() throws IOException { + try { + wispSocketLockSupport.beginRead(); + return accept0(); + } finally { + wispSocketLockSupport.endRead(); + } + } + + private Socket accept0() throws IOException { + final ServerSocketChannel ch = getChannelImpl(); + try { + SocketChannel res; + + if (getSoTimeout() > 0) { + WEA.addTimer(System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(getSoTimeout())); + } + + while ((res = ch.accept()) == null) { + WEA.registerEvent(ch, SelectionKey.OP_ACCEPT); + WEA.park(-1); + + if (getSoTimeout() > 0 && WEA.isTimeout()) { + throw new SocketTimeoutException("time out"); + } + } + res.configureBlocking(false); + return new Socket(res); + + } catch (Exception x) { + Net.translateException(x, true); + return null; + } finally { + if (getSoTimeout() > 0) { + WEA.cancelTimer(); + } + WEA.unregisterEvent(); + } + } + + public void close() throws IOException { + if (ssc != null) { + ssc.close(); + wispSocketLockSupport.unparkBlockedWispTask(); + } + } + + public ServerSocketChannel getChannel() { + return ssc; + } + + public boolean isBound() { + return ssc != null && ssc.isBound(); + } + + public boolean isClosed() { + return ssc != null && !ssc.isOpen(); + } + + public void setSoTimeout(int timeout) throws SocketException { + this.timeout = timeout; + } + + public int getSoTimeout() throws IOException { + return timeout; + } + + public void setReuseAddress(boolean on) throws SocketException { + try { + getChannelImpl().setOption(StandardSocketOptions.SO_REUSEADDR, on); + } catch (IOException x) { + Net.translateToSocketException(x); + } + } + + public boolean getReuseAddress() throws SocketException { + try { + return getChannelImpl().getOption(StandardSocketOptions.SO_REUSEADDR); + } catch (IOException x) { + Net.translateToSocketException(x); + return false; // Never happens + } + } + + public String toString() { + if (!isBound()) + return "ServerSocket[unbound]"; + return "ServerSocket[addr=" + getInetAddress() + + ",localport=" + getLocalPort() + "]"; + } + + public void setReceiveBufferSize(int size) throws SocketException { + if (size <= 0) + throw new IllegalArgumentException("size can not be 0 or negative"); + try { + getChannelImpl().setOption(StandardSocketOptions.SO_RCVBUF, size); + } catch (IOException x) { + Net.translateToSocketException(x); + } + } + + public int getReceiveBufferSize() throws SocketException { + try { + return getChannelImpl().getOption(StandardSocketOptions.SO_RCVBUF); + } catch (IOException x) { + Net.translateToSocketException(x); + return -1; // Never happens + } + } + + private ServerSocketChannelImpl getChannelImpl() throws SocketException { + if (ssc == null) { + try { + ssc = (ServerSocketChannelImpl) ServerSocketChannel.open(); + ssc.configureBlocking(false); + } catch (IOException e) { + throw new SocketException(e.getMessage()); + } + } + return ssc; + } +} diff --git a/src/linux/classes/sun/nio/ch/WispSocketImpl.java b/src/linux/classes/sun/nio/ch/WispSocketImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..1a45331e69e18c8b008868522379e9374a31cbc6 --- /dev/null +++ b/src/linux/classes/sun/nio/ch/WispSocketImpl.java @@ -0,0 +1,592 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package sun.nio.ch; + +import sun.misc.SharedSecrets; +import sun.misc.WispEngineAccess; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.*; +import java.nio.ByteBuffer; +import java.nio.channels.SelectionKey; +import java.nio.channels.SocketChannel; +import java.security.AccessController; +import java.security.PrivilegedExceptionAction; +import java.util.concurrent.TimeUnit; + + +// Make a socket channel be like a socket and yield on block + +public class WispSocketImpl +{ + private static WispEngineAccess WEA = SharedSecrets.getWispEngineAccess(); + + WispSocketLockSupport wispSocketLockSupport = new WispSocketLockSupport(); + // The channel being adapted + private SocketChannelImpl sc = null; + // 1 verse 1 related socket + private Socket so; + // Timeout "option" value for reads + protected int timeout = 0; + private InputStream socketInputStream = null; + + public WispSocketImpl(Socket so) { + this.so = so; + } + + public WispSocketImpl(SocketChannel sc, Socket so) { + this.so = so; + this.sc = (SocketChannelImpl) sc; + } + + public SocketChannel getChannel() { + return sc; + } + + // Override this method just to protect against changes in the superclass + // + public void connect(SocketAddress remote) throws IOException { + connect(remote, 0); + } + + public void connect(SocketAddress remote, int timeout) throws IOException { + if (remote == null) + throw new IllegalArgumentException("connect: The address can't be null"); + if (timeout < 0) + throw new IllegalArgumentException("connect: timeout can't be negative"); + + final SocketChannel ch = getChannelImpl(); + try { + wispSocketLockSupport.beginWrite(); + if (ch.connect(remote)) return; + + if (timeout > 0) + WEA.addTimer(System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(timeout)); + + do { + WEA.registerEvent(ch, SelectionKey.OP_CONNECT); + WEA.park(-1); + + if (timeout > 0 && WEA.isTimeout()) { + throw new SocketTimeoutException("time out"); + } + } while (!ch.finishConnect()); + + } catch (Exception x) { + // see AbstractPlainSocketImpl#doConnect + try { + Net.translateException(x, true); + } catch (IOException e) { + sc.close(); + throw e; + } + } finally { + wispSocketLockSupport.endWrite(); + if (timeout > 0) { + WEA.cancelTimer(); + } + + if (ch.isBlocking()) + ch.configureBlocking(false); + + WEA.unregisterEvent(); + } + } + + public void bind(SocketAddress local) throws IOException { + try { + getChannelImpl().bind(local); + } catch (Exception x) { + Net.translateException(x); + } + } + + public InetAddress getInetAddress() { + SocketAddress remote = sc == null ? null : sc.remoteAddress(); + if (remote == null) { + return null; + } else { + return ((InetSocketAddress)remote).getAddress(); + } + } + + public InetAddress getLocalAddress() { + SocketChannelImpl ch = null; + try { + ch = getChannelImpl(); + } catch (SocketException e) { + // return 0.0.0.0 + } + if (ch != null && ch.isOpen()) { + InetSocketAddress local = ch.localAddress(); + if (local != null) { + return Net.getRevealedLocalAddress(local).getAddress(); + } + } + return new InetSocketAddress(0).getAddress(); + } + + public int getPort() { + SocketAddress remote = sc == null ? null : sc.remoteAddress(); + if (remote == null) { + return 0; + } else { + return ((InetSocketAddress)remote).getPort(); + } + } + + public int getLocalPort() { + SocketChannelImpl ch = null; + try { + ch = getChannelImpl(); + } catch (SocketException e) { + // return 0.0.0.0 + } + SocketAddress local = ch == null ? null : ch.localAddress(); + if (local == null) { + return -1; + } else { + return ((InetSocketAddress)local).getPort(); + } + } + + public InputStream getInputStream() throws IOException { + if (isClosed()) + throw new SocketException("Socket is closed"); + if (!isConnected()) + throw new SocketException("Socket is not connected"); + if (isInputShutdown()) + throw new SocketException("Socket input is shutdown"); + if (socketInputStream == null) { + try { + socketInputStream = AccessController.doPrivileged( + new PrivilegedExceptionAction() { + public InputStream run() throws IOException { + return new InputStream() { + protected final SocketChannel ch = getChannelImpl(); + private ByteBuffer bb = null; + // Invoker's previous array + private byte[] bs = null; + private byte[] b1 = null; + + private ByteBuffer readAhead = null; + + @Override + public int read() throws IOException { + if (b1 == null) { + b1 = new byte[1]; + } + int n = this.read(b1); + if (n == 1) + return b1[0] & 0xff; + return -1; + } + + @Override + public int read(byte[] bs, int off, int len) + throws IOException { + if (len <= 0 || off < 0 || off + len > bs.length) { + if (len == 0) { + return 0; + } + throw new ArrayIndexOutOfBoundsException(); + } + + ByteBuffer bb = ((this.bs == bs) ? this.bb : ByteBuffer.wrap(bs)); + + bb.limit(Math.min(off + len, bb.capacity())); + bb.position(off); + this.bb = bb; + this.bs = bs; + return read(bb); + } + + + private int read(ByteBuffer bb) throws IOException { + try { + wispSocketLockSupport.beginRead(); + return read0(bb); + } finally { + wispSocketLockSupport.endRead(); + } + } + + private int read0(ByteBuffer bb) + throws IOException { + int n; + try { + if (readAhead != null && readAhead.hasRemaining()) { + if (bb.remaining() >= readAhead.remaining()) { + n = readAhead.remaining(); + bb.put(readAhead); + } else { + n = bb.remaining(); + for (int i = 0; i < n; i++) { + bb.put(readAhead.get()); + } + } + return n; + } + + if ((n = ch.read(bb)) != 0) { + return n; + } + + if (so.getSoTimeout() > 0) { + WEA.addTimer(System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(so.getSoTimeout())); + } + + do { + WEA.registerEvent(ch, SelectionKey.OP_READ); + WEA.park(-1); + + if (so.getSoTimeout() > 0 && WEA.isTimeout()) { + throw new SocketTimeoutException("time out"); + + } + } while ((n = ch.read(bb)) == 0); + } finally { + if (so.getSoTimeout() > 0) { + WEA.cancelTimer(); + } + WEA.unregisterEvent(); + } + + return n; + } + + @Override + public int available() throws IOException { + if (readAhead == null) { + readAhead = ByteBuffer.allocate(4096); + } else if (readAhead.hasRemaining()) { + return readAhead.remaining(); + } + + readAhead.clear(); + ch.read(readAhead); + readAhead.flip(); + + return readAhead.remaining(); + } + + @Override + public void close() throws IOException { + WispSocketImpl.this.close(); + } + }; + } + }); + } catch (java.security.PrivilegedActionException e) { + throw (IOException)e.getException(); + } + } + return socketInputStream; + } + + public OutputStream getOutputStream() throws IOException { + if (isClosed()) + throw new SocketException("Socket is closed"); + if (!isConnected()) + throw new SocketException("Socket is not connected"); + if (isOutputShutdown()) + throw new SocketException("Socket output is shutdown"); + try { + return AccessController.doPrivileged( + new PrivilegedExceptionAction() { + public OutputStream run() throws IOException { + return new OutputStream() { + protected final SocketChannel ch = getChannelImpl(); + private ByteBuffer bb = null; + // Invoker's previous array + private byte[] bs = null; + private byte[] b1 = null; + + + @Override + public void write(int b) throws IOException { + if (b1 == null) { + b1 = new byte[1]; + } + b1[0] = (byte) b; + this.write(b1); + } + + @Override + public void write(byte[] bs, int off, int len) + throws IOException + { + if (len <= 0 || off < 0 || off + len > bs.length) { + if (len == 0) { + return; + } + throw new ArrayIndexOutOfBoundsException(); + } + ByteBuffer bb = ((this.bs == bs) ? this.bb : ByteBuffer.wrap(bs)); + bb.limit(Math.min(off + len, bb.capacity())); + bb.position(off); + this.bb = bb; + this.bs = bs; + + write(bb); + } + + private void write(ByteBuffer bb) throws IOException { + try { + wispSocketLockSupport.beginWrite(); + write0(bb); + } finally { + wispSocketLockSupport.endWrite(); + } + } + + private void write0(ByteBuffer bb) + throws IOException { + + try { + int writeLen = bb.remaining(); + if (ch.write(bb) == writeLen) { + return; + } + + if (so.getSoTimeout() > 0) { + WEA.addTimer(System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(so.getSoTimeout())); + } + + do { + WEA.registerEvent(ch, SelectionKey.OP_WRITE); + WEA.park(-1); + + if (so.getSoTimeout() > 0 && WEA.isTimeout()) { + throw new SocketTimeoutException("time out"); + } + ch.write(bb); + } while (bb.remaining() > 0); + } finally { + if (so.getSoTimeout() > 0) { + WEA.cancelTimer(); + } + WEA.unregisterEvent(); + } + } + + @Override + public void close() throws IOException { + WispSocketImpl.this.close(); + } + }; + } + }); + } catch (java.security.PrivilegedActionException e) { + throw (IOException)e.getException(); + } + } + + private void setBooleanOption(SocketOption name, boolean value) + throws SocketException { + try { + getChannelImpl().setOption(name, value); + } catch (IOException x) { + Net.translateToSocketException(x); + } + } + + private void setIntOption(SocketOption name, int value) + throws SocketException { + try { + getChannelImpl().setOption(name, value); + } catch (IOException x) { + Net.translateToSocketException(x); + } + } + + private boolean getBooleanOption(SocketOption name) throws SocketException { + try { + return getChannelImpl().getOption(name); + } catch (IOException x) { + Net.translateToSocketException(x); + return false; // keep compiler happy + } + } + + private int getIntOption(SocketOption name) throws SocketException { + try { + return getChannelImpl().getOption(name); + } catch (IOException x) { + Net.translateToSocketException(x); + return -1; // keep compiler happy + } + } + + public void setTcpNoDelay(boolean on) throws SocketException { + setBooleanOption(StandardSocketOptions.TCP_NODELAY, on); + } + + public boolean getTcpNoDelay() throws SocketException { + return getBooleanOption(StandardSocketOptions.TCP_NODELAY); + } + + public void setSoLinger(boolean on, int linger) throws SocketException { + if (!on) + linger = -1; + setIntOption(StandardSocketOptions.SO_LINGER, linger); + } + + public int getSoLinger() throws SocketException { + return getIntOption(StandardSocketOptions.SO_LINGER); + } + + public void sendUrgentData(int data) throws IOException { + int n = getChannelImpl().sendOutOfBandData((byte) data); + if (n == 0) + throw new IOException("Socket buffer full"); + } + + public void setOOBInline(boolean on) throws SocketException { + setBooleanOption(ExtendedSocketOption.SO_OOBINLINE, on); + } + + public boolean getOOBInline() throws SocketException { + return getBooleanOption(ExtendedSocketOption.SO_OOBINLINE); + } + + public void setSoTimeout(int timeout) throws SocketException { + if (timeout < 0) + throw new IllegalArgumentException("timeout can't be negative"); + this.timeout = timeout; + } + + public int getSoTimeout() throws SocketException { + return timeout; + } + + public void setSendBufferSize(int size) throws SocketException { + // size 0 valid for SocketChannel, invalid for Socket + if (size <= 0) + throw new IllegalArgumentException("Invalid send size"); + setIntOption(StandardSocketOptions.SO_SNDBUF, size); + } + + public int getSendBufferSize() throws SocketException { + return getIntOption(StandardSocketOptions.SO_SNDBUF); + } + + public void setReceiveBufferSize(int size) throws SocketException { + // size 0 valid for SocketChannel, invalid for Socket + if (size <= 0) + throw new IllegalArgumentException("Invalid receive size"); + setIntOption(StandardSocketOptions.SO_RCVBUF, size); + } + + public int getReceiveBufferSize() throws SocketException { + return getIntOption(StandardSocketOptions.SO_RCVBUF); + } + + public void setKeepAlive(boolean on) throws SocketException { + setBooleanOption(StandardSocketOptions.SO_KEEPALIVE, on); + } + + public boolean getKeepAlive() throws SocketException { + return getBooleanOption(StandardSocketOptions.SO_KEEPALIVE); + } + + public void setTrafficClass(int tc) throws SocketException { + setIntOption(StandardSocketOptions.IP_TOS, tc); + } + + public int getTrafficClass() throws SocketException { + return getIntOption(StandardSocketOptions.IP_TOS); + } + + public void setReuseAddress(boolean on) throws SocketException { + setBooleanOption(StandardSocketOptions.SO_REUSEADDR, on); + } + + public boolean getReuseAddress() throws SocketException { + return getBooleanOption(StandardSocketOptions.SO_REUSEADDR); + } + + public void close() throws IOException { + if (sc != null) { + sc.close(); + wispSocketLockSupport.unparkBlockedWispTask(); + } + } + + public void shutdownInput() throws IOException { + try { + getChannelImpl().shutdownInput(); + } catch (Exception x) { + Net.translateException(x); + } + } + + public void shutdownOutput() throws IOException { + try { + getChannelImpl().shutdownOutput(); + } catch (Exception x) { + Net.translateException(x); + } + } + + public String toString() { + if (isConnected()) + return "Socket[addr=" + getInetAddress() + + ",port=" + getPort() + + ",localport=" + getLocalPort() + "]"; + return "Socket[unconnected]"; + } + + public boolean isConnected() { + return sc != null && sc.isConnected(); + } + + public boolean isBound() { + return sc != null && sc.localAddress() != null; + } + + public boolean isClosed() { + return sc != null && !sc.isOpen(); + } + + public boolean isInputShutdown() { + return sc != null && !sc.isInputOpen(); + } + + public boolean isOutputShutdown() { + return sc != null && !sc.isOutputOpen(); + } + + private SocketChannelImpl getChannelImpl() throws SocketException { + if (sc == null) { + try { + sc = (SocketChannelImpl) SocketChannel.open(); + sc.configureBlocking(false); + } catch (IOException e) { + throw new SocketException(e.getMessage()); + } + } + return sc; + } +} diff --git a/src/linux/classes/sun/nio/ch/WispSocketLockSupport.java b/src/linux/classes/sun/nio/ch/WispSocketLockSupport.java new file mode 100644 index 0000000000000000000000000000000000000000..f4ec5eefa33b856e4c340b268fa20cad8b24a4e4 --- /dev/null +++ b/src/linux/classes/sun/nio/ch/WispSocketLockSupport.java @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package sun.nio.ch; + +import com.alibaba.wisp.engine.WispEngine; +import com.alibaba.wisp.engine.WispTask; +import sun.misc.SharedSecrets; +import sun.misc.WispEngineAccess; + +import java.util.concurrent.locks.ReentrantLock; + +/** + * This class supports fd use across {@link WispTask} by adding read/write reentrantLocks for + * {@link WispSocketImpl} {@link WispServerSocketImpl} and {@link WispUdpSocketImpl}, + * {@link WispTask}s will park once they encountered a contended socket. + */ +class WispSocketLockSupport { + private static WispEngineAccess WEA = SharedSecrets.getWispEngineAccess(); + + /** Lock held when reading and accepting + */ + private final ReentrantLock readLock = WEA.enableSocketLock()? new ReentrantLock() : null; + + /** Lock held when writing and connecting + */ + private final ReentrantLock writeLock = WEA.enableSocketLock()? new ReentrantLock() : null; + + /** Lock held when tracking current blocked WispTask and closing + */ + private final ReentrantLock stateLock = WEA.enableSocketLock()? new ReentrantLock() : null; + + WispTask blockedReadWispTask = null; + WispTask blockedWriteWispTask = null; + + /** + * This is not a ReadWriteLock, they are separate locks here, readLock protect + * reading to fd while writeLock protect writing to fd. + */ + private void lockRead() { + readLock.lock(); + } + + private void lockWrite() { + writeLock.lock(); + } + + private void unLockRead() { + readLock.unlock(); + } + + private void unLockWrite() { + writeLock.unlock(); + } + + void beginRead() { + if (!WEA.enableSocketLock()) { + return; + } + lockRead(); + stateLock.lock(); + try { + blockedReadWispTask = WEA.getCurrentTask(); + } finally { + stateLock.unlock(); + } + } + + void endRead() { + if (!WEA.enableSocketLock()) { + return; + } + + stateLock.lock(); + try { + blockedReadWispTask = null; + } finally { + stateLock.unlock(); + } + unLockRead(); + } + + void beginWrite() { + if (!WEA.enableSocketLock()) { + return; + } + lockWrite(); + stateLock.lock(); + try { + blockedWriteWispTask = WEA.getCurrentTask(); + } finally { + stateLock.unlock(); + } + } + + void endWrite() { + if (!WEA.enableSocketLock()) { + return; + } + stateLock.lock(); + try { + blockedWriteWispTask = null; + } finally { + stateLock.unlock(); + } + unLockWrite(); + } + + void unparkBlockedWispTask() { + stateLock.lock(); + try { + if (blockedReadWispTask != null) { + WEA.unpark(blockedReadWispTask); + blockedReadWispTask = null; + } + if (blockedWriteWispTask != null) { + WEA.unpark(blockedWriteWispTask); + blockedWriteWispTask = null; + } + } finally { + stateLock.unlock(); + } + } +} diff --git a/src/linux/classes/sun/nio/ch/WispUdpSocketImpl.java b/src/linux/classes/sun/nio/ch/WispUdpSocketImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..4cd5c781b9fd3eddca83706e7246ab320243844f --- /dev/null +++ b/src/linux/classes/sun/nio/ch/WispUdpSocketImpl.java @@ -0,0 +1,390 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package sun.nio.ch; + +import sun.misc.SharedSecrets; +import sun.misc.WispEngineAccess; + +import java.io.IOException; +import java.net.*; +import java.nio.channels.DatagramChannel; +import java.nio.channels.SelectionKey; +import java.nio.ByteBuffer; +import java.util.concurrent.TimeUnit; + + +// Make a udp socket channel look like a datagram socket. + +public class WispUdpSocketImpl { + + private static WispEngineAccess WEA = SharedSecrets.getWispEngineAccess(); + + private WispSocketLockSupport wispSocketLockSupport = new WispSocketLockSupport(); + // 1 verse 1 related socket + private DatagramChannelImpl dc; + + private DatagramSocket so; + + private volatile int timeout = 0; + + public WispUdpSocketImpl(DatagramSocket so) { + this.so = so; + } + + public WispUdpSocketImpl(DatagramChannel dc, DatagramSocket so) { + this.so = so; + this.dc = (DatagramChannelImpl) dc; + } + + public void bind(SocketAddress local) throws SocketException { + try { + if (local == null) + local = new InetSocketAddress(0); + getChannelImpl().bind(local); + } catch (Exception x) { + Net.translateToSocketException(x); + } + } + + public void connect(InetAddress address, int port) { + try { + connect(new InetSocketAddress(address, port)); + } catch (SocketException x) { + } + } + + public void connect(SocketAddress remote) throws SocketException { + if (remote == null) + throw new IllegalArgumentException("Address can't be null"); + + InetSocketAddress isa = Net.asInetSocketAddress(remote); + int port = isa.getPort(); + if (port < 0 || port > 0xFFFF) + throw new IllegalArgumentException("connect: " + port); + if (remote == null) + throw new IllegalArgumentException("connect: null address"); + if (isClosed()) + return; + try { + getChannelImpl().connect(remote); + } catch (Exception x) { + Net.translateToSocketException(x); + } + } + + public void disconnect() { + try { + getChannelImpl().disconnect(); + } catch (IOException x) { + throw new Error(x); + } + } + + public boolean isBound() { + return dc != null && dc.localAddress() != null; + } + + public boolean isConnected() { + return dc != null && dc.remoteAddress() != null; + } + + public InetAddress getInetAddress() { + return (isConnected() + ? Net.asInetSocketAddress(dc.remoteAddress()).getAddress() + : null); + } + + public int getPort() { + return (isConnected() + ? Net.asInetSocketAddress(dc.remoteAddress()).getPort() + : -1); + } + + public void send(DatagramPacket p) throws IOException { + try { + wispSocketLockSupport.beginWrite(); + send0(p); + } finally { + wispSocketLockSupport.endWrite(); + } + } + + private SocketAddress receive(ByteBuffer bb) throws IOException { + try { + wispSocketLockSupport.beginRead(); + return receive0(bb); + } finally { + wispSocketLockSupport.endRead(); + } + } + + + private void send0(DatagramPacket p) throws IOException { + final DatagramChannelImpl ch = getChannelImpl(); + try { + ByteBuffer bb = ByteBuffer.wrap(p.getData(), + p.getOffset(), + p.getLength()); + + boolean useWrite = ch.isConnected() && p.getAddress() == null; + if (useWrite) { + InetSocketAddress isa = (InetSocketAddress) + ch.remoteAddress(); + p.setPort(isa.getPort()); + p.setAddress(isa.getAddress()); + } + + int writeLen = bb.remaining(); + if ((useWrite ? ch.write(bb) : ch.send(bb, p.getSocketAddress())) == writeLen) + return; + + /* Don't call so.getSoTimeout() here as it will try to acquire the lock of datagram socket + which might be already held by receiver, hence deadlock happens */ + if (getSoTimeout() > 0) + WEA.addTimer(System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(getSoTimeout())); + + do { + WEA.registerEvent(ch, SelectionKey.OP_WRITE); + WEA.park(-1); + + if (getSoTimeout() > 0 && WEA.isTimeout()) { + throw new SocketTimeoutException("time out"); + } + if (useWrite) { + ch.write(bb); + } else { + ch.send(bb, p.getSocketAddress()); + } + } while (bb.remaining() > 0); + + } catch (IOException x) { + Net.translateException(x); + } finally { + if (getSoTimeout() > 0) { + WEA.cancelTimer(); + } + WEA.unregisterEvent(); + } + } + + + private SocketAddress receive0(ByteBuffer bb) throws IOException { + final DatagramChannelImpl ch = getChannelImpl(); + SocketAddress sa; + + try { + if ((sa = ch.receive(bb)) != null) + return sa; + + if (getSoTimeout() > 0) + WEA.addTimer(System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(getSoTimeout())); + + do { + WEA.registerEvent(ch, SelectionKey.OP_READ); + WEA.park(-1); + + if (getSoTimeout() > 0 && WEA.isTimeout()) { + throw new SocketTimeoutException("time out"); + } + } while ((sa = ch.receive(bb)) == null); + } finally { + if (getSoTimeout() > 0) { + WEA.cancelTimer(); + } + WEA.unregisterEvent(); + } + + return sa; + } + + public int receive(DatagramPacket p, int bufLen) throws IOException { + int dataLen = 0; + try { + ByteBuffer bb = ByteBuffer.wrap(p.getData(), + p.getOffset(), + bufLen); + SocketAddress sender = receive(bb); + p.setSocketAddress(sender); + dataLen = bb.position() - p.getOffset(); + } catch (IOException x) { + Net.translateException(x); + } + return dataLen; + } + + public InetAddress getLocalAddress() { + if (isClosed()) + return null; + DatagramChannelImpl ch = null; + try { + ch = getChannelImpl(); + } catch (SocketException e) { + // return 0.0.0.0 + } + SocketAddress local = ch == null ? null : ch.localAddress(); + if (local == null) + local = new InetSocketAddress(0); + InetAddress result = ((InetSocketAddress) local).getAddress(); + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + try { + sm.checkConnect(result.getHostAddress(), -1); + } catch (SecurityException x) { + return new InetSocketAddress(0).getAddress(); + } + } + return result; + } + + public int getLocalPort() { + if (isClosed()) + return -1; + try { + SocketAddress local = getChannelImpl().getLocalAddress(); + if (local != null) { + return ((InetSocketAddress) local).getPort(); + } + } catch (Exception x) { + } + return 0; + } + + public void setSoTimeout(int timeout) throws SocketException { + this.timeout = timeout; + } + + public int getSoTimeout() throws SocketException { + return timeout; + } + + private void setBooleanOption(SocketOption name, boolean value) + throws SocketException { + try { + getChannelImpl().setOption(name, value); + } catch (IOException x) { + Net.translateToSocketException(x); + } + } + + private void setIntOption(SocketOption name, int value) + throws SocketException { + try { + getChannelImpl().setOption(name, value); + } catch (IOException x) { + Net.translateToSocketException(x); + } + } + + private boolean getBooleanOption(SocketOption name) throws SocketException { + try { + return getChannelImpl().getOption(name); + } catch (IOException x) { + Net.translateToSocketException(x); + return false; // keep compiler happy + } + } + + private int getIntOption(SocketOption name) throws SocketException { + try { + return getChannelImpl().getOption(name); + } catch (IOException x) { + Net.translateToSocketException(x); + return -1; // keep compiler happy + } + } + + public void setSendBufferSize(int size) throws SocketException { + if (size <= 0) + throw new IllegalArgumentException("Invalid send size"); + setIntOption(StandardSocketOptions.SO_SNDBUF, size); + } + + public int getSendBufferSize() throws SocketException { + return getIntOption(StandardSocketOptions.SO_SNDBUF); + } + + public void setReceiveBufferSize(int size) throws SocketException { + if (size <= 0) + throw new IllegalArgumentException("Invalid receive size"); + setIntOption(StandardSocketOptions.SO_RCVBUF, size); + } + + public int getReceiveBufferSize() throws SocketException { + return getIntOption(StandardSocketOptions.SO_RCVBUF); + } + + public void setReuseAddress(boolean on) throws SocketException { + setBooleanOption(StandardSocketOptions.SO_REUSEADDR, on); + } + + public boolean getReuseAddress() throws SocketException { + return getBooleanOption(StandardSocketOptions.SO_REUSEADDR); + + } + + public void setBroadcast(boolean on) throws SocketException { + setBooleanOption(StandardSocketOptions.SO_BROADCAST, on); + } + + public boolean getBroadcast() throws SocketException { + return getBooleanOption(StandardSocketOptions.SO_BROADCAST); + } + + public void setTrafficClass(int tc) throws SocketException { + setIntOption(StandardSocketOptions.IP_TOS, tc); + } + + public int getTrafficClass() throws SocketException { + return getIntOption(StandardSocketOptions.IP_TOS); + } + + public void close() { + if (dc != null) { + try { + dc.close(); + } catch (IOException x) { + throw new Error(x); + } + wispSocketLockSupport.unparkBlockedWispTask(); + } + } + + public boolean isClosed() { + return dc != null && !dc.isOpen(); + } + + public DatagramChannel getChannel() { + return dc; + } + + private DatagramChannelImpl getChannelImpl() throws SocketException { + if (dc == null) { + try { + dc = (DatagramChannelImpl) DatagramChannel.open(); + dc.configureBlocking(false); + } catch (IOException e) { + throw new SocketException(e.getMessage()); + } + } + return dc; + } +} diff --git a/src/linux/native/com/alibaba/wisp/engine/WispEngine.c b/src/linux/native/com/alibaba/wisp/engine/WispEngine.c new file mode 100644 index 0000000000000000000000000000000000000000..9363be4acdee56af03763e6631c3fd4f062e9269 --- /dev/null +++ b/src/linux/native/com/alibaba/wisp/engine/WispEngine.c @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "jni.h" +#include "jvm.h" +#include "com_alibaba_wisp_engine_WispEngine.h" + +#define ARRAY_LENGTH(a) (sizeof(a)/sizeof(a[0])) + +static JNINativeMethod methods[] = { + {"getProxyUnpark", "([I)I", (void *)&JVM_GetProxyUnpark}, +}; + +JNIEXPORT void JNICALL +Java_com_alibaba_wisp_engine_WispEngine_registerNatives(JNIEnv *env, jclass cls) +{ + (*env)->RegisterNatives(env, cls, methods, ARRAY_LENGTH(methods)); +} diff --git a/src/linux/native/com/alibaba/wisp/engine/WispSysmon.c b/src/linux/native/com/alibaba/wisp/engine/WispSysmon.c new file mode 100644 index 0000000000000000000000000000000000000000..22d38780f7bccc68f23c9c5595720592ee57589c --- /dev/null +++ b/src/linux/native/com/alibaba/wisp/engine/WispSysmon.c @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "jni.h" +#include "jvm.h" +#include "com_alibaba_wisp_engine_WispSysmon.h" + +#define ARRAY_LENGTH(a) (sizeof(a)/sizeof(a[0])) + +static JNINativeMethod methods[] = { + {"markPreempted","(Ljava/lang/Thread;Z)V", (void *)&JVM_MarkPreempted}, +}; + +JNIEXPORT void JNICALL +Java_com_alibaba_wisp_engine_WispSysmon_registerNatives(JNIEnv *env, jclass cls) +{ + (*env)->RegisterNatives(env, cls, methods, ARRAY_LENGTH(methods)); +} diff --git a/src/linux/native/com/alibaba/wisp/engine/WispTask.c b/src/linux/native/com/alibaba/wisp/engine/WispTask.c new file mode 100644 index 0000000000000000000000000000000000000000..c7566b626ee759993d44a94449ba78ff3c618115 --- /dev/null +++ b/src/linux/native/com/alibaba/wisp/engine/WispTask.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "jni.h" +#include "jvm.h" +#include "com_alibaba_wisp_engine_WispTask.h" + +#define ARRAY_LENGTH(a) (sizeof(a)/sizeof(a[0])) + +#define THD "Ljava/lang/Thread;" + +static JNINativeMethod methods[] = { + {"checkAndClearNativeInterruptForWisp", "(" THD ")Z", (void *)&JVM_CheckAndClearNativeInterruptForWisp}, +}; + +JNIEXPORT void JNICALL +Java_com_alibaba_wisp_engine_WispTask_registerNatives(JNIEnv *env, jclass cls) +{ + (*env)->RegisterNatives(env, cls, methods, ARRAY_LENGTH(methods)); +} diff --git a/src/share/classes/java/dyn/AsymRunnable.java b/src/macosx/classes/com/alibaba/wisp/engine/WispCounter.java similarity index 61% rename from src/share/classes/java/dyn/AsymRunnable.java rename to src/macosx/classes/com/alibaba/wisp/engine/WispCounter.java index e28f3528439937de1e133cebef66e249b80637ac..0975f673773484378f4e1eec88d918d2983a5c9e 100644 --- a/src/share/classes/java/dyn/AsymRunnable.java +++ b/src/macosx/classes/com/alibaba/wisp/engine/WispCounter.java @@ -1,30 +1,26 @@ /* - * Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this + * published by the Free Software Foundation. Alibaba designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. */ -package java.dyn; +package com.alibaba.wisp.engine; + +final public class WispCounter { -public interface AsymRunnable { - public OutT run(AsymCoroutine coroutine, InT value); } diff --git a/src/macosx/classes/com/alibaba/wisp/engine/WispCounterMXBeanImpl.java b/src/macosx/classes/com/alibaba/wisp/engine/WispCounterMXBeanImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..6438cc340fa97601eec5dfe5b8bbb484889b4104 --- /dev/null +++ b/src/macosx/classes/com/alibaba/wisp/engine/WispCounterMXBeanImpl.java @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.alibaba.wisp.engine; + +import com.alibaba.management.WispCounterMXBean; + +import javax.management.ObjectName; +import java.util.List; + +public class WispCounterMXBeanImpl implements WispCounterMXBean { + + @Override + public List getRunningStates() { + throw new UnsupportedOperationException(); + } + + @Override + public List getSwitchCount() { + throw new UnsupportedOperationException(); + } + + @Override + public List getWaitTimeTotal() { + throw new UnsupportedOperationException(); + } + + @Override + public List getRunningTimeTotal() { + throw new UnsupportedOperationException(); + } + + @Override + public List getCompleteTaskCount() { + throw new UnsupportedOperationException(); + } + + @Override + public List getCreateTaskCount() { + throw new UnsupportedOperationException(); + } + + @Override + public List getParkCount() { + throw new UnsupportedOperationException(); + } + + @Override + public List getUnparkCount() { + throw new UnsupportedOperationException(); + } + + @Override + public List getLazyUnparkCount() { + throw new UnsupportedOperationException(); + } + + @Override + public List getUnparkInterruptSelectorCount() { + throw new UnsupportedOperationException(); + } + + @Override + public List getSelectableIOCount() { + throw new UnsupportedOperationException(); + } + + @Override + public List getTimeOutCount() { + throw new UnsupportedOperationException(); + } + + @Override + public List getEventLoopCount() { + throw new UnsupportedOperationException(); + } + + @Override + public List getQueueLength() { + throw new UnsupportedOperationException(); + } + + @Override + public List getNumberOfRunningTasks() { + throw new UnsupportedOperationException(); + } + + @Override + public List getTotalEnqueueTime() { + throw new UnsupportedOperationException(); + } + + @Override + public List getEnqueueCount() { + throw new UnsupportedOperationException(); + } + + @Override + public List getTotalExecutionTime() { + throw new UnsupportedOperationException(); + } + + @Override + public List getExecutionCount() { + throw new UnsupportedOperationException(); + } + + @Override + public List getTotalWaitSocketIOTime() { + throw new UnsupportedOperationException(); + } + + @Override + public List getWaitSocketIOCount() { + throw new UnsupportedOperationException(); + } + + @Override + public List getTotalBlockingTime() { + throw new UnsupportedOperationException(); + } + + @Override + public WispCounter getWispCounter(long id) { + throw new UnsupportedOperationException(); + } + + @Override + public ObjectName getObjectName() { + throw new UnsupportedOperationException(); + } +} diff --git a/src/macosx/classes/com/alibaba/wisp/engine/WispEngine.java b/src/macosx/classes/com/alibaba/wisp/engine/WispEngine.java new file mode 100644 index 0000000000000000000000000000000000000000..e822b03630f4c77913898e0a6e00c53faabf67ea --- /dev/null +++ b/src/macosx/classes/com/alibaba/wisp/engine/WispEngine.java @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.alibaba.wisp.engine; + +import sun.misc.JavaLangAccess; +import sun.misc.SharedSecrets; +import sun.misc.WispEngineAccess; + +import java.io.IOException; +import java.nio.channels.SelectableChannel; +import java.util.concurrent.atomic.AtomicReference; + +public class WispEngine { + + public static boolean transparentWispSwitch() { + return false; + } + + public static boolean enableThreadAsWisp() { + return false; + } + + private static void setWispEngineAccess() { + SharedSecrets.setWispEngineAccess(new WispEngineAccess() { + + @Override + public WispTask getCurrentTask() { + throw new UnsupportedOperationException(); + } + + @Override + public void registerEvent(SelectableChannel ch, int events) throws IOException { + throw new UnsupportedOperationException(); + } + + @Override + public void unregisterEvent() { + throw new UnsupportedOperationException(); + } + + @Override + public int epollWait(int epfd, long pollArray, int arraySize, long timeout, + AtomicReference status, Object INTERRUPTED) throws IOException { + throw new UnsupportedOperationException(); + } + + @Override + public void interruptEpoll(AtomicReference status, Object INTERRUPTED, int interruptFd) { + throw new UnsupportedOperationException(); + } + + @Override + public void addTimer(long deadlineNano) { + throw new UnsupportedOperationException(); + } + + @Override + public void cancelTimer() { + throw new UnsupportedOperationException(); + } + + @Override + public void sleep(long ms) { + throw new UnsupportedOperationException(); + } + + @Override + public void yield() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isThreadTask(WispTask task) { + return false; + } + + @Override + public boolean isTimeout() { + return false; + } + + @Override + public void park(long timeoutNano) { + throw new UnsupportedOperationException(); + } + + @Override + public void unpark(WispTask task) { + throw new UnsupportedOperationException(); + } + + @Override + public void destroy() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean hasMoreTasks() { + return false; + } + + @Override + public boolean runningAsCoroutine(Thread t) { + return false; + } + + @Override + public boolean usingWispEpoll() { + return false; + } + + public boolean isAlive(WispTask task) { + return false; + } + + @Override + public void interrupt(WispTask task) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean testInterruptedAndClear(WispTask task, boolean clear) { + return false; + } + + @Override + public boolean tryStartThreadAsWisp(Thread thread, Runnable target) { + return false; + } + + @Override + public boolean isAllThreadAsWisp() { + return false; + } + + @Override + public boolean useDirectSelectorWakeup() { + return false; + } + + @Override + public boolean enableSocketLock() { + return false; + } + + @Override + public StackTraceElement[] getStackTrace(WispTask task) { + throw new UnsupportedOperationException(); + } + }); + } + +} diff --git a/src/macosx/classes/com/alibaba/wisp/engine/WispTask.java b/src/macosx/classes/com/alibaba/wisp/engine/WispTask.java new file mode 100644 index 0000000000000000000000000000000000000000..40ef3fc58213f8897097718b48eebe978e613cc1 --- /dev/null +++ b/src/macosx/classes/com/alibaba/wisp/engine/WispTask.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.alibaba.wisp.engine; + + +public class WispTask { + + public Thread getThreadWrapper() { + throw new UnsupportedOperationException(); + } + +} diff --git a/src/macosx/classes/com/alibaba/wisp/engine/WispThreadWrapper.java b/src/macosx/classes/com/alibaba/wisp/engine/WispThreadWrapper.java new file mode 100644 index 0000000000000000000000000000000000000000..67999b115507afb9e8a02f1ebc7bbee356fc8677 --- /dev/null +++ b/src/macosx/classes/com/alibaba/wisp/engine/WispThreadWrapper.java @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.alibaba.wisp.engine; + + +import sun.reflect.CallerSensitive; + +import java.dyn.CoroutineSupport; + +class WispThreadWrapper extends Thread { + + @Override + public CoroutineSupport getCoroutineSupport() { + throw new UnsupportedOperationException(); + } + + @Override + public void start() { + throw new UnsupportedOperationException(); + } + + + @Override + @Deprecated + public void destroy() { + throw new UnsupportedOperationException(); + } + + @Override + @Deprecated + public int countStackFrames() { + throw new UnsupportedOperationException(); + } + + @Override + @CallerSensitive + public ClassLoader getContextClassLoader() { + throw new UnsupportedOperationException(); + } + + @Override + public void setContextClassLoader(ClassLoader cl) { + throw new UnsupportedOperationException(); + } + + @Override + public StackTraceElement[] getStackTrace() { + throw new UnsupportedOperationException(); + } + + @Override + public long getId() { + throw new UnsupportedOperationException(); + } + + @Override + public State getState() { + throw new UnsupportedOperationException(); + } + + @Override + public UncaughtExceptionHandler getUncaughtExceptionHandler() { + throw new UnsupportedOperationException(); + } + + @Override + public void setUncaughtExceptionHandler(UncaughtExceptionHandler eh) { + throw new UnsupportedOperationException(); + } + + @Override + public String toString() { + throw new UnsupportedOperationException(); + } + +} diff --git a/src/macosx/classes/sun/nio/ch/IOEvent.java b/src/macosx/classes/sun/nio/ch/IOEvent.java new file mode 100644 index 0000000000000000000000000000000000000000..51db2db85782dbb1f5e11edff72073c70edb7de8 --- /dev/null +++ b/src/macosx/classes/sun/nio/ch/IOEvent.java @@ -0,0 +1,8 @@ +package sun.nio.ch; + +public class IOEvent { + + public static Class eventClass() { + return IOEventRegister.class; + } +} \ No newline at end of file diff --git a/src/macosx/classes/sun/nio/ch/IOEventRegister.java b/src/macosx/classes/sun/nio/ch/IOEventRegister.java new file mode 100644 index 0000000000000000000000000000000000000000..06f724db98d04d20d991d0d51ad6fe9afebf6b83 --- /dev/null +++ b/src/macosx/classes/sun/nio/ch/IOEventRegister.java @@ -0,0 +1,99 @@ +package sun.nio.ch; + +import sun.misc.SharedSecrets; + +import java.io.IOException; + +public class IOEventRegister { + + static { + SharedSecrets.setIOEventAccess(new IOEventAccess() { + + @Override + public int eventCtlAdd() { + throw new UnsupportedOperationException(); + } + + @Override + public int eventCtlDel() { + throw new UnsupportedOperationException(); + } + + @Override + public int eventCtlMod() { + throw new UnsupportedOperationException(); + } + + @Override + public int eventOneShot() { + throw new UnsupportedOperationException(); + } + + @Override + public int noEvent() { + throw new UnsupportedOperationException(); + } + + @Override + public long allocatePollArray(int count) { + throw new UnsupportedOperationException(); + } + + @Override + public void freePollArray(long address) { + throw new UnsupportedOperationException(); + } + + @Override + public long getEvent(long address, int i) { + throw new UnsupportedOperationException(); + } + + @Override + public int getDescriptor(long eventAddress) { + throw new UnsupportedOperationException(); + } + + @Override + public int getEvents(long eventAddress) { + throw new UnsupportedOperationException(); + } + + @Override + public int eventCreate() throws IOException { + throw new UnsupportedOperationException(); + } + + @Override + public int eventCtl(int epfd, int opcode, int fd, int events) { + throw new UnsupportedOperationException(); + } + + @Override + public int eventWait(int epfd, long pollAddress, int numfds, int timeout) throws IOException { + throw new UnsupportedOperationException(); + } + + @Override + public void socketpair(int[] sv) throws IOException { + throw new UnsupportedOperationException(); + } + + @Override + public void interrupt(int fd) throws IOException { + throw new UnsupportedOperationException(); + } + + @Override + public void drain(int fd) throws IOException { + throw new UnsupportedOperationException(); + } + + @Override + public void close(int fd) { + throw new UnsupportedOperationException(); + } + }); + } + +} \ No newline at end of file diff --git a/src/macosx/classes/sun/nio/ch/WispServerSocketImpl.java b/src/macosx/classes/sun/nio/ch/WispServerSocketImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..b6fdd26984c6197a8bcc47b30f0ba5423e88b47a --- /dev/null +++ b/src/macosx/classes/sun/nio/ch/WispServerSocketImpl.java @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package sun.nio.ch; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.Socket; +import java.net.SocketAddress; +import java.net.SocketException; +import java.nio.channels.ServerSocketChannel; + + +public class WispServerSocketImpl +{ + + public WispServerSocketImpl() { + } + + public void bind(SocketAddress local) throws IOException { + throw new UnsupportedOperationException(); + } + + public void bind(SocketAddress local, int backlog) throws IOException { + throw new UnsupportedOperationException(); + } + + public InetAddress getInetAddress() { + throw new UnsupportedOperationException(); + } + + public int getLocalPort() { + throw new UnsupportedOperationException(); + } + + public Socket accept() throws IOException { + throw new UnsupportedOperationException(); + } + + public void close() throws IOException { + throw new UnsupportedOperationException(); + } + + public ServerSocketChannel getChannel() { + throw new UnsupportedOperationException(); + } + + public boolean isBound() { + throw new UnsupportedOperationException(); + } + + public boolean isClosed() { + throw new UnsupportedOperationException(); + } + + public void setSoTimeout(int timeout) throws SocketException { + throw new UnsupportedOperationException(); + } + + public int getSoTimeout() throws IOException { + throw new UnsupportedOperationException(); + } + + public void setReuseAddress(boolean on) throws SocketException { + throw new UnsupportedOperationException(); + } + + public boolean getReuseAddress() throws SocketException { + throw new UnsupportedOperationException(); + } + + public String toString() { + throw new UnsupportedOperationException(); + } + + public void setReceiveBufferSize(int size) throws SocketException { + throw new UnsupportedOperationException(); + } + + public int getReceiveBufferSize() throws SocketException { + throw new UnsupportedOperationException(); + } + +} diff --git a/src/macosx/classes/sun/nio/ch/WispSocketImpl.java b/src/macosx/classes/sun/nio/ch/WispSocketImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..318ad6436fe376a472c9c2dc4283a41e60c72f56 --- /dev/null +++ b/src/macosx/classes/sun/nio/ch/WispSocketImpl.java @@ -0,0 +1,197 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package sun.nio.ch; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.InetAddress; +import java.net.Socket; +import java.net.SocketAddress; +import java.net.SocketException; +import java.nio.channels.SocketChannel; + + +public class WispSocketImpl +{ + + public WispSocketImpl(Socket so) { + throw new UnsupportedOperationException(); + } + + public WispSocketImpl(SocketChannel sc, Socket so) { + throw new UnsupportedOperationException(); + } + + public SocketChannel getChannel() { + throw new UnsupportedOperationException(); + } + + public void connect(SocketAddress remote) throws IOException { + throw new UnsupportedOperationException(); + } + + public void connect(SocketAddress remote, int timeout) throws IOException { + throw new UnsupportedOperationException(); + } + + public void bind(SocketAddress local) throws IOException { + throw new UnsupportedOperationException(); + } + + public InetAddress getInetAddress() { + throw new UnsupportedOperationException(); + } + + public InetAddress getLocalAddress() { + throw new UnsupportedOperationException(); + } + + public int getPort() { + throw new UnsupportedOperationException(); + } + + public int getLocalPort() { + throw new UnsupportedOperationException(); + } + + public InputStream getInputStream() throws IOException { + throw new UnsupportedOperationException(); + } + + public OutputStream getOutputStream() throws IOException { + throw new UnsupportedOperationException(); + } + + public void setTcpNoDelay(boolean on) throws SocketException { + throw new UnsupportedOperationException(); + } + + public boolean getTcpNoDelay() throws SocketException { + throw new UnsupportedOperationException(); + } + + public void setSoLinger(boolean on, int linger) throws SocketException { + throw new UnsupportedOperationException(); + } + + public int getSoLinger() throws SocketException { + throw new UnsupportedOperationException(); + } + + public void sendUrgentData(int data) throws IOException { + throw new UnsupportedOperationException(); + } + + public void setOOBInline(boolean on) throws SocketException { + throw new UnsupportedOperationException(); + } + + public boolean getOOBInline() throws SocketException { + throw new UnsupportedOperationException(); + } + + public void setSoTimeout(int timeout) throws SocketException { + throw new UnsupportedOperationException(); + } + + public int getSoTimeout() throws SocketException { + throw new UnsupportedOperationException(); + } + + public void setSendBufferSize(int size) throws SocketException { + throw new UnsupportedOperationException(); + } + + public int getSendBufferSize() throws SocketException { + throw new UnsupportedOperationException(); + } + + public void setReceiveBufferSize(int size) throws SocketException { + throw new UnsupportedOperationException(); + } + + public int getReceiveBufferSize() throws SocketException { + throw new UnsupportedOperationException(); + } + + public void setKeepAlive(boolean on) throws SocketException { + throw new UnsupportedOperationException(); + } + + public boolean getKeepAlive() throws SocketException { + throw new UnsupportedOperationException(); + } + + public void setTrafficClass(int tc) throws SocketException { + throw new UnsupportedOperationException(); + } + + public int getTrafficClass() throws SocketException { + throw new UnsupportedOperationException(); + } + + public void setReuseAddress(boolean on) throws SocketException { + throw new UnsupportedOperationException(); + } + + public boolean getReuseAddress() throws SocketException { + throw new UnsupportedOperationException(); + } + + public void close() throws IOException { + throw new UnsupportedOperationException(); + } + + public void shutdownInput() throws IOException { + throw new UnsupportedOperationException(); + } + + public void shutdownOutput() throws IOException { + throw new UnsupportedOperationException(); + } + + public String toString() { + throw new UnsupportedOperationException(); + } + + public boolean isConnected() { + throw new UnsupportedOperationException(); + } + + public boolean isBound() { + throw new UnsupportedOperationException(); + } + + public boolean isClosed() { + throw new UnsupportedOperationException(); + } + + public boolean isInputShutdown() { + throw new UnsupportedOperationException(); + } + + public boolean isOutputShutdown() { + throw new UnsupportedOperationException(); + } + +} diff --git a/src/macosx/classes/sun/nio/ch/WispSocketLockSupport.java b/src/macosx/classes/sun/nio/ch/WispSocketLockSupport.java new file mode 100644 index 0000000000000000000000000000000000000000..62373f0c88135b99343803d7189288fc920bb27e --- /dev/null +++ b/src/macosx/classes/sun/nio/ch/WispSocketLockSupport.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package sun.nio.ch; + +class WispSocketLockSupport { + +} diff --git a/src/macosx/classes/sun/nio/ch/WispUdpSocketImpl.java b/src/macosx/classes/sun/nio/ch/WispUdpSocketImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..e3d5f79fd9d5b1ba4f7b460eab9729fb2c2b0f5e --- /dev/null +++ b/src/macosx/classes/sun/nio/ch/WispUdpSocketImpl.java @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package sun.nio.ch; + +import java.io.IOException; +import java.net.*; +import java.nio.ByteBuffer; +import java.nio.channels.DatagramChannel; + + +// Make a udp socket channel look like a datagram socket. + +public class WispUdpSocketImpl { + + public WispUdpSocketImpl(DatagramSocket so) { + throw new UnsupportedOperationException(); + } + + public void bind(SocketAddress local) throws SocketException { + throw new UnsupportedOperationException(); + } + + public void connect(InetAddress address, int port) { + throw new UnsupportedOperationException(); + } + + public void connect(SocketAddress remote) throws SocketException { + throw new UnsupportedOperationException(); + } + + public void disconnect() { + throw new UnsupportedOperationException(); + } + + public boolean isBound() { + throw new UnsupportedOperationException(); + } + + public boolean isConnected() { + throw new UnsupportedOperationException(); + } + + public InetAddress getInetAddress() { + throw new UnsupportedOperationException(); + } + + public int getPort() { + throw new UnsupportedOperationException(); + } + + public void send(DatagramPacket p) throws IOException { + throw new UnsupportedOperationException(); + } + + private SocketAddress receive(ByteBuffer bb) throws IOException { + throw new UnsupportedOperationException(); + } + + public int receive(DatagramPacket p, int bufLen) throws IOException { + throw new UnsupportedOperationException(); + } + + public InetAddress getLocalAddress() { + throw new UnsupportedOperationException(); + } + + public int getLocalPort() { + throw new UnsupportedOperationException(); + } + + public void setSoTimeout(int timeout) throws SocketException { + throw new UnsupportedOperationException(); + } + + public int getSoTimeout() throws SocketException { + throw new UnsupportedOperationException(); + } + + public void setSendBufferSize(int size) throws SocketException { + throw new UnsupportedOperationException(); + } + + public int getSendBufferSize() throws SocketException { + throw new UnsupportedOperationException(); + } + + public void setReceiveBufferSize(int size) throws SocketException { + throw new UnsupportedOperationException(); + } + + public int getReceiveBufferSize() throws SocketException { + throw new UnsupportedOperationException(); + } + + public void setReuseAddress(boolean on) throws SocketException { + throw new UnsupportedOperationException(); + } + + public boolean getReuseAddress() throws SocketException { + throw new UnsupportedOperationException(); + } + + public void setBroadcast(boolean on) throws SocketException { + throw new UnsupportedOperationException(); + } + + public boolean getBroadcast() throws SocketException { + throw new UnsupportedOperationException(); + } + + public void setTrafficClass(int tc) throws SocketException { + throw new UnsupportedOperationException(); + } + + public int getTrafficClass() throws SocketException { + throw new UnsupportedOperationException(); + } + + public void close() { + throw new UnsupportedOperationException(); + } + + public boolean isClosed() { + throw new UnsupportedOperationException(); + } + + public DatagramChannel getChannel() { + throw new UnsupportedOperationException(); + } + +} diff --git a/src/share/classes/com/alibaba/management/WispCounterMXBean.java b/src/share/classes/com/alibaba/management/WispCounterMXBean.java new file mode 100644 index 0000000000000000000000000000000000000000..2771100241bb82de9b75f13f2e5857d00a1f099e --- /dev/null +++ b/src/share/classes/com/alibaba/management/WispCounterMXBean.java @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.alibaba.management; + +import com.alibaba.wisp.engine.WispCounter; + +import java.lang.management.PlatformManagedObject; +import java.util.List; + +public interface WispCounterMXBean extends PlatformManagedObject { + + /** + * @return list of managed wisp worker running state + */ + List getRunningStates(); + + /** + * @return list of managed wisp worker switch count + */ + List getSwitchCount(); + + /** + * @return list of managed wisp worker wait time total, unit ns + */ + List getWaitTimeTotal(); + + /** + * @return list of managed wisp worker running time total, unit ns + */ + List getRunningTimeTotal(); + + /** + * @return list of managed wisp worker complete task count + */ + List getCompleteTaskCount(); + + /** + * @return list of managed wisp worker create task count + */ + List getCreateTaskCount(); + + /** + * @return list of managed wisp worker park count + */ + List getParkCount(); + + /** + * @return list of managed wisp worker unpark count + */ + List getUnparkCount(); + + /** + * @return list of managed wisp worker lazy unpark count + * @deprecated the lazy unpark mechanism has been removed since ajdk 8.6.11 + */ + @Deprecated + List getLazyUnparkCount(); + + /** + * @return list of managed wisp worker unpark interrupt selector count + */ + List getUnparkInterruptSelectorCount(); + + /** + * @return list of managed wisp worker do IO count + */ + List getSelectableIOCount(); + + /** + * @return list of managed wisp worker timeout count + */ + List getTimeOutCount(); + + /** + * @return list of managed wisp worker do event loop count + */ + List getEventLoopCount(); + + /** + * @return list of managed wisp worker task queue length + */ + List getQueueLength(); + + /** + * @return list of number of running tasks from managed wisp workers + */ + List getNumberOfRunningTasks(); + + /** + * @return list of total blocking time in nanos from managed wisp workers + */ + List getTotalBlockingTime(); + + /** + * @return list of total execution time in nanos from managed wisp workers + */ + List getTotalExecutionTime(); + + /** + * @return list of execution count from managed wisp workers + */ + List getExecutionCount(); + + /** + * @return list of total enqueue time in nanos from managed wisp workers + */ + List getTotalEnqueueTime(); + + /** + * @return list of enqueue count from managed wisp workers + */ + List getEnqueueCount(); + + /** + * @return list of total wait socket io time in nanos from managed wisp workers + */ + List getTotalWaitSocketIOTime(); + + /** + * @return list of wait socket io event count from managed wisp workers + */ + List getWaitSocketIOCount(); + + /** + * @param id WispEngine id + * @return WispCounter data + */ + WispCounter getWispCounter(long id); +} diff --git a/src/share/classes/com/sun/demo/jvmti/hprof/Tracker.java b/src/share/classes/com/sun/demo/jvmti/hprof/Tracker.java index dc3d61b5eb962a43ca0864696d7fbfeb10a8e386..00deff667304f2b42203d24fa87bb2fc309ef305 100644 --- a/src/share/classes/com/sun/demo/jvmti/hprof/Tracker.java +++ b/src/share/classes/com/sun/demo/jvmti/hprof/Tracker.java @@ -37,6 +37,9 @@ package com.sun.demo.jvmti.hprof; * for more details. */ +import sun.misc.JavaLangAccess; +import sun.misc.SharedSecrets; + public class Tracker { /* Master switch that activates calls to native functions. */ @@ -57,7 +60,7 @@ public class Tracker { if (obj == null) { throw new IllegalArgumentException("Null object."); } - nativeObjectInit(Thread.currentThread(), obj); + nativeObjectInit(SharedSecrets.getJavaLangAccess().currentThread0(), obj); } } @@ -73,7 +76,7 @@ public class Tracker { if (obj == null) { throw new IllegalArgumentException("Null object."); } - nativeNewArray(Thread.currentThread(), obj); + nativeNewArray(SharedSecrets.getJavaLangAccess().currentThread0(), obj); } } @@ -96,7 +99,11 @@ public class Tracker { throw new IllegalArgumentException("Negative method index"); } - nativeCallSite(Thread.currentThread(), cnum, mnum); + /* + * calling Thread.currentThread() will bring us here again, which will cause + * StackOverflow error. Instead, we use Thread.currentThread0(). + */ + nativeCallSite(SharedSecrets.getJavaLangAccess().currentThread0(), cnum, mnum); } } @@ -117,7 +124,7 @@ public class Tracker { throw new IllegalArgumentException("Negative method index"); } - nativeReturnSite(Thread.currentThread(), cnum, mnum); + nativeReturnSite(SharedSecrets.getJavaLangAccess().currentThread0(), cnum, mnum); } } diff --git a/src/share/classes/java/dyn/AsymCoroutine.java b/src/share/classes/java/dyn/AsymCoroutine.java deleted file mode 100644 index 3a34d026e764ea01da0c03ceea3c4e9a75c23ac9..0000000000000000000000000000000000000000 --- a/src/share/classes/java/dyn/AsymCoroutine.java +++ /dev/null @@ -1,179 +0,0 @@ -/* - * Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package java.dyn; - -import java.util.Iterator; -import java.util.NoSuchElementException; - -/** - * Implementation of asymmetric coroutines. A AsymCoroutine can be called by any coroutine (Coroutine and AsymCoroutine) and will return to - * its caller upon {@link #ret()}. - *

- * Similar to {@link Thread} there are two ways to implement a AsymCoroutine: either by implementing a subclass of AsymCoroutine (and - * overriding {@link #run()}) or by providing a {@link Runnable} to the AsymCoroutine constructor. - *

- * An implementation of a simple AsymCoroutine that always returns the average of all its previous inputs might look like this: - *

- *


- *
- * - *
- * class Average extends AsymCoroutine<Integer, Integer> {
- * 	public Integer run(Integer value) {
- * 		int sum = value;
- * 		int count = 1;
- * 		while (true) {
- * 			sum += ret(sum / count++);
- * 		}
- * 	}
- * }
- * 
- * - *
- *
- *

- * This AsymCoroutine can be invoked either by reading and writing the {@link #output} and {@link #input} fields and invoking the - * {@link #call()} method: - *

- *

- * - *
- * Average avg = new Average();
- * avg.input = 10;
- * avg.call();
- * System.out.println(avg.output);
- * 
- * - *
- *

- * Another way to invoke this AsymCoroutine is by using the shortcut {@link #call(Object)} methods: - *

- *

- * - *
- * Average avg = new Average();
- * System.out.println(avg.call(10));
- * 
- * - *
- *

- * - * @author Lukas Stadler - * - * @param - * input type of this AsymCoroutine, Void if no input value is expected - * @param - * output type of this AsymCoroutine, Void if no output is produced - */ -public class AsymCoroutine extends CoroutineBase implements Iterable { - CoroutineBase caller; - - private final AsymRunnable target; - - private InT input; - private OutT output; - - public AsymCoroutine() { - target = null; - threadSupport.addCoroutine(this, -1); - } - - public AsymCoroutine(long stacksize) { - target = null; - threadSupport.addCoroutine(this, stacksize); - } - - public AsymCoroutine(AsymRunnable target) { - this.target = target; - threadSupport.addCoroutine(this, -1); - } - - public AsymCoroutine(AsymRunnable target, long stacksize) { - this.target = target; - threadSupport.addCoroutine(this, stacksize); - } - - public final OutT call(final InT input) { - this.input = input; - Thread.currentThread().getCoroutineSupport().asymmetricCall(this); - return output; - } - - public final OutT call() { - Thread.currentThread().getCoroutineSupport().asymmetricCall(this); - return output; - } - - public final InT ret(final OutT value) { - output = value; - Thread.currentThread().getCoroutineSupport().asymmetricReturn(this); - return input; - } - - public final InT ret() { - Thread.currentThread().getCoroutineSupport().asymmetricReturn(this); - return input; - } - - protected OutT run(InT value) { - return target.run(this, value); - } - - private static class Iter implements Iterator { - private final AsymCoroutine fiber; - - public Iter(final AsymCoroutine fiber) { - this.fiber = fiber; - } - - @Override - public boolean hasNext() { - return !fiber.isFinished(); - } - - @Override - public OutT next() { - if (fiber.isFinished()) - throw new NoSuchElementException(); - return fiber.call(); - } - - @Override - public void remove() { - throw new UnsupportedOperationException(); - } - - } - - @Override - public final Iterator iterator() { - return new Iter(this); - } - - protected final void run() { - output = run(input); - } -} diff --git a/src/share/classes/java/dyn/Coroutine.java b/src/share/classes/java/dyn/Coroutine.java index 479d4335f6974618cc6fcdb292760ee98251816d..9cd81757eb115e5fea4820d6f4448a099f45713b 100644 --- a/src/share/classes/java/dyn/Coroutine.java +++ b/src/share/classes/java/dyn/Coroutine.java @@ -25,6 +25,8 @@ package java.dyn; +import sun.misc.SharedSecrets; + /** * Implementation of symmetric coroutines. A Coroutine will take part in thread-wide scheduling of coroutines. It transfers control to * the next coroutine whenever yield is called. @@ -66,56 +68,92 @@ package java.dyn; * @author Lukas Stadler */ public class Coroutine extends CoroutineBase { - private final Runnable target; - - Coroutine next; - Coroutine last; - - public Coroutine() { - this.target = null; - threadSupport.addCoroutine(this, -1); - } - - public Coroutine(Runnable target) { - this.target = target; - threadSupport.addCoroutine(this, -1); - } - - public Coroutine(long stacksize) { - this.target = null; - threadSupport.addCoroutine(this, stacksize); - } - - public Coroutine(Runnable target, long stacksize) { - this.target = target; - threadSupport.addCoroutine(this, stacksize); - } - - // creates the initial coroutine for a new thread - Coroutine(CoroutineSupport threadSupport, long data) { - super(threadSupport, data); - this.target = null; - } - - /** - * Yields execution to the next coroutine in the current threads coroutine queue. - */ - public static void yield() { - Thread.currentThread().getCoroutineSupport().symmetricYield(); - } - - public static void yieldTo(Coroutine target) { - Thread.currentThread().getCoroutineSupport().symmetricYieldTo(target); - } - - public void stop() { - Thread.currentThread().getCoroutineSupport().symmetricStopCoroutine(this); - } - - protected void run() { - assert Thread.currentThread() == threadSupport.getThread(); - if (target != null) { - target.run(); - } - } + public enum StealResult { + SUCCESS, + FAIL_BY_CONTENTION, + FAIL_BY_STATUS, + FAIL_BY_NATIVE_FRAME + } + + private final Runnable target; + + public Coroutine() { + this.target = null; + threadSupport.addCoroutine(this, -1); + } + + public Coroutine(Runnable target) { + this.target = target; + threadSupport.addCoroutine(this, -1); + } + + public Coroutine(long stacksize) { + this.target = null; + threadSupport.addCoroutine(this, stacksize); + } + + public Coroutine(Runnable target, long stacksize) { + this.target = target; + threadSupport.addCoroutine(this, stacksize); + } + + // creates the initial coroutine for a new thread + Coroutine(CoroutineSupport threadSupport, long nativeCoroutine) { + super(threadSupport, nativeCoroutine); + this.target = null; + } + + + public static void yieldTo(Coroutine target) { + SharedSecrets.getJavaLangAccess().currentThread0().getCoroutineSupport().symmetricYieldTo(target); + } + + /** + * optimized version of yieldTo function for wisp based on the following assumptions: + * 1. we won't simultaneously steal a {@link Coroutine} from other threads + * 2. we won't switch to a {@link Coroutine} that's being stolen + * 3. we won't steal a running {@link Coroutine} + * @param target target coroutine + */ + public static void unsafeYieldTo(Coroutine target) { + SharedSecrets.getJavaLangAccess().currentThread0().getCoroutineSupport().unsafeSymmetricYieldTo(target); + } + + /** + * Steal a coroutine from it's carrier thread to current thread. + * + * @param failOnContention steal fail if there's too much lock contention + * + * @return result described by Coroutine.STEAL_*. Also return SUCCESS directly if coroutine's carrier thread is current. + */ + public StealResult steal(boolean failOnContention) { + return threadSupport.steal(this, failOnContention); + } + + public void stop() { + SharedSecrets.getJavaLangAccess().currentThread0().getCoroutineSupport().symmetricStopCoroutine(this); + } + + public void setWispTask(int id, Object task, Object engine) { + setWispTask(nativeCoroutine, id, task, engine); + } + + protected void run() { + assert Thread.currentThread() == threadSupport.getThread(); + if (target != null) { + target.run(); + } + } + + static { + registerNatives(); + } + + private static native void registerNatives(); + + private static native void setWispTask(long coroutine, int id, Object task, Object engine); + + public StackTraceElement[] getCoroutineStack() { + return CoroutineSupport.getCoroutineStack(this.nativeCoroutine); + } } \ No newline at end of file diff --git a/src/share/classes/java/dyn/CoroutineBase.java b/src/share/classes/java/dyn/CoroutineBase.java index aab2d59141d7d75c8b4da4f4975bd4e4d7ca2913..a8172c8e5555357ae91fd70fd46212d6f68c207b 100644 --- a/src/share/classes/java/dyn/CoroutineBase.java +++ b/src/share/classes/java/dyn/CoroutineBase.java @@ -25,70 +25,69 @@ package java.dyn; +import sun.misc.SharedSecrets; + public abstract class CoroutineBase { - transient long data; + transient long nativeCoroutine; + + boolean finished = false; - transient CoroutineLocal.CoroutineLocalMap coroutineLocals = null; + boolean needsUnlock = false; - boolean finished = false; + transient CoroutineSupport threadSupport; - transient CoroutineSupport threadSupport; + CoroutineBase() { + Thread thread = SharedSecrets.getJavaLangAccess().currentThread0(); + assert thread.getCoroutineSupport() != null; + this.threadSupport = thread.getCoroutineSupport(); + } - CoroutineBase() { - Thread thread = Thread.currentThread(); - assert thread.getCoroutineSupport() != null; - this.threadSupport = thread.getCoroutineSupport(); - } + // creates the initial coroutine for a new thread + CoroutineBase(CoroutineSupport threadSupport, long nativeCoroutine) { + this.threadSupport = threadSupport; + this.nativeCoroutine = nativeCoroutine; + } - // creates the initial coroutine for a new thread - CoroutineBase(CoroutineSupport threadSupport, long data) { - this.threadSupport = threadSupport; - this.data = data; - } + protected abstract void run(); - protected abstract void run(); + @SuppressWarnings({"unused"}) + private final void startInternal() { + assert threadSupport.getThread() == SharedSecrets.getJavaLangAccess().currentThread0(); + try { + // When we symmetricYieldTo a newly created coroutine, + // we'll expect the new coroutine release lock as soon as possible + threadSupport.beforeResume(this); + run(); + } catch (Throwable t) { + if (!(t instanceof CoroutineExitException)) { + t.printStackTrace(); + } + } finally { + finished = true; + // threadSupport is fixed by steal() + threadSupport.beforeResume(this); - @SuppressWarnings({ "unused" }) - private final void startInternal() { - assert threadSupport.getThread() == Thread.currentThread(); - try { - if (CoroutineSupport.DEBUG) { - System.out.println("starting coroutine " + this); - } - run(); - } catch (Throwable t) { - if (!(t instanceof CoroutineExitException)) { - t.printStackTrace(); - } - } finally { - finished = true; - // use Thread.currentThread().getCoroutineSupport() because we might have been migrated to another thread! - if (this instanceof Coroutine) { - Thread.currentThread().getCoroutineSupport().terminateCoroutine(); - } else { - Thread.currentThread().getCoroutineSupport().terminateCallable(); - } - } - assert threadSupport.getThread() == Thread.currentThread(); - } + threadSupport.terminateCoroutine(); + } + assert threadSupport.getThread() == SharedSecrets.getJavaLangAccess().currentThread0(); + } - /** - * Returns true if this coroutine has reached its end. Under normal circumstances this happens when the {@link #run()} method returns. - */ - public final boolean isFinished() { - return finished; - } + /** + * Returns true if this coroutine has reached its end. Under normal circumstances this happens when the {@link #run()} method returns. + */ + public final boolean isFinished() { + return finished; + } - /** - * @return the thread that this coroutine is associated with - * @throws NullPointerException - * if the coroutine has terminated - */ - public Thread getThread() { - return threadSupport.getThread(); - } + /** + * @return the thread that this coroutine is associated with + * @throws NullPointerException if the coroutine has been terminated + */ + public Thread getThread() { + return threadSupport.getThread(); + } - public static CoroutineBase current() { - return Thread.currentThread().getCoroutineSupport().getCurrent(); - } + public static CoroutineBase current() { + return SharedSecrets.getJavaLangAccess().currentThread0().getCoroutineSupport().getCurrent(); + } } diff --git a/src/share/classes/java/dyn/CoroutineLocal.java b/src/share/classes/java/dyn/CoroutineLocal.java deleted file mode 100644 index 60468923762c4eba4b467f42825ec540f8f8a635..0000000000000000000000000000000000000000 --- a/src/share/classes/java/dyn/CoroutineLocal.java +++ /dev/null @@ -1,541 +0,0 @@ -/* - * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package java.dyn; - -import java.lang.Object; -import java.lang.Thread; -import java.lang.UnsupportedOperationException; -import java.lang.ref.*; -import java.util.concurrent.atomic.AtomicInteger; - -@SuppressWarnings({ "rawtypes", "unchecked" }) -public class CoroutineLocal { - private final int coroutineLocalHashCode = nextHashCode(); - private static AtomicInteger nextHashCode = new AtomicInteger(); - private static final int HASH_INCREMENT = 0x61c88647; - - private static int nextHashCode() { - return nextHashCode.getAndAdd(HASH_INCREMENT); - } - - protected T initialValue() { - return null; - } - - public CoroutineLocal() { - } - - public T get() { - assert Thread.currentThread().getCoroutineSupport() != null; - CoroutineBase t = Thread.currentThread().getCoroutineSupport().getCurrent(); - CoroutineLocalMap map = getMap(t); - if (map != null) { - CoroutineLocalMap.Entry e = map.getEntry(this); - if (e != null) - return (T) e.value; - } - return setInitialValue(); - } - - /** - * Variant of set() to establish initialValue. Used instead of set() in case user has overridden the set() method. - * - * @return the initial value - */ - private T setInitialValue() { - T value = initialValue(); - assert Thread.currentThread().getCoroutineSupport() != null; - CoroutineBase t = Thread.currentThread().getCoroutineSupport().getCurrent(); - CoroutineLocalMap map = getMap(t); - if (map != null) - map.set(this, value); - else - createMap(t, value); - return value; - } - - /** - * Sets the current thread's copy of this thread-local variable to the specified value. Most subclasses will have no need to override - * this method, relying solely on the {@link #initialValue} method to set the values of thread-locals. - * - * @param value the value to be stored in the current thread's copy of this thread-local. - */ - public void set(T value) { - assert Thread.currentThread().getCoroutineSupport() != null; - CoroutineBase t = Thread.currentThread().getCoroutineSupport().getCurrent(); - CoroutineLocalMap map = getMap(t); - if (map != null) - map.set(this, value); - else - createMap(t, value); - } - - /** - * Removes the current thread's value for this thread-local variable. If this thread-local variable is subsequently {@linkplain #get - * read} by the current thread, its value will be reinitialized by invoking its {@link #initialValue} method, unless its value is - * {@linkplain #set set} by the current thread in the interim. This may result in multiple invocations of the initialValue - * method in the current thread. - * - * @since 1.5 - */ - public void remove() { - assert Thread.currentThread().getCoroutineSupport() != null; - CoroutineLocalMap m = getMap(Thread.currentThread().getCoroutineSupport().getCurrent()); - if (m != null) - m.remove(this); - } - - CoroutineLocalMap getMap(CoroutineBase t) { - return t.coroutineLocals; - } - - /** - * Create the map associated with a ThreadLocal. Overridden in InheritableThreadLocal. - * - * @param t the current thread - * @param firstValue value for the initial entry of the map - * @param map the map to store. - */ - void createMap(CoroutineBase t, T firstValue) { - t.coroutineLocals = new CoroutineLocalMap(this, firstValue); - } - - /** - * Factory method to create map of inherited thread locals. Designed to be called only from Thread constructor. - * - * @param parentMap the map associated with parent thread - * @return a map containing the parent's inheritable bindings - */ - static CoroutineLocalMap createInheritedMap(CoroutineLocalMap parentMap) { - return new CoroutineLocalMap(parentMap); - } - - /** - * Method childValue is visibly defined in subclass InheritableThreadLocal, but is internally defined here for the sake of providing - * createInheritedMap factory method without needing to subclass the map class in InheritableThreadLocal. This technique is preferable - * to the alternative of embedding instanceof tests in methods. - */ - T childValue(T parentValue) { - throw new UnsupportedOperationException(); - } - - /** - * ThreadLocalMap is a customized hash map suitable only for maintaining thread local values. No operations are exported outside of the - * ThreadLocal class. The class is package private to allow declaration of fields in class Thread. To help deal with very large and - * long-lived usages, the hash table entries use WeakReferences for keys. However, since reference queues are not used, stale entries - * are guaranteed to be removed only when the table starts running out of space. - */ - static class CoroutineLocalMap { - - /** - * The entries in this hash map extend WeakReference, using its main ref field as the key (which is always a ThreadLocal object). - * Note that null keys (i.e. entry.get() == null) mean that the key is no longer referenced, so the entry can be expunged from - * table. Such entries are referred to as "stale entries" in the code that follows. - */ - static class Entry extends WeakReference { - /** The value associated with this ThreadLocal. */ - Object value; - - Entry(CoroutineLocal k, Object v) { - super(k); - value = v; - } - } - - /** - * The initial capacity -- MUST be a power of two. - */ - private static final int INITIAL_CAPACITY = 16; - - /** - * The table, resized as necessary. table.length MUST always be a power of two. - */ - private Entry[] table; - - /** - * The number of entries in the table. - */ - private int size = 0; - - /** - * The next size value at which to resize. - */ - private int threshold; // Default to 0 - - /** - * Set the resize threshold to maintain at worst a 2/3 load factor. - */ - private void setThreshold(int len) { - threshold = len * 2 / 3; - } - - /** - * Increment i modulo len. - */ - private static int nextIndex(int i, int len) { - return ((i + 1 < len) ? i + 1 : 0); - } - - /** - * Decrement i modulo len. - */ - private static int prevIndex(int i, int len) { - return ((i - 1 >= 0) ? i - 1 : len - 1); - } - - /** - * Construct a new map initially containing (firstKey, firstValue). ThreadLocalMaps are constructed lazily, so we only create one - * when we have at least one entry to put in it. - */ - CoroutineLocalMap(CoroutineLocal firstKey, Object firstValue) { - table = new Entry[INITIAL_CAPACITY]; - int i = firstKey.coroutineLocalHashCode & (INITIAL_CAPACITY - 1); - table[i] = new Entry(firstKey, firstValue); - size = 1; - setThreshold(INITIAL_CAPACITY); - } - - /** - * Construct a new map including all Inheritable ThreadLocals from given parent map. Called only by createInheritedMap. - * - * @param parentMap the map associated with parent thread. - */ - private CoroutineLocalMap(CoroutineLocalMap parentMap) { - Entry[] parentTable = parentMap.table; - int len = parentTable.length; - setThreshold(len); - table = new Entry[len]; - - for (int j = 0; j < len; j++) { - Entry e = parentTable[j]; - if (e != null) { - CoroutineLocal key = e.get(); - if (key != null) { - Object value = key.childValue(e.value); - Entry c = new Entry(key, value); - int h = key.coroutineLocalHashCode & (len - 1); - while (table[h] != null) - h = nextIndex(h, len); - table[h] = c; - size++; - } - } - } - } - - /** - * Get the entry associated with key. This method itself handles only the fast path: a direct hit of existing key. It otherwise - * relays to getEntryAfterMiss. This is designed to maximize performance for direct hits, in part by making this method readily - * inlinable. - * - * @param key the thread local object - * @return the entry associated with key, or null if no such - */ - private Entry getEntry(CoroutineLocal key) { - int i = key.coroutineLocalHashCode & (table.length - 1); - Entry e = table[i]; - if (e != null && e.get() == key) - return e; - else - return getEntryAfterMiss(key, i, e); - } - - /** - * Version of getEntry method for use when key is not found in its direct hash slot. - * - * @param key the thread local object - * @param i the table index for key's hash code - * @param e the entry at table[i] - * @return the entry associated with key, or null if no such - */ - private Entry getEntryAfterMiss(CoroutineLocal key, int i, Entry e) { - Entry[] tab = table; - int len = tab.length; - - while (e != null) { - CoroutineLocal k = e.get(); - if (k == key) - return e; - if (k == null) - expungeStaleEntry(i); - else - i = nextIndex(i, len); - e = tab[i]; - } - return null; - } - - /** - * Set the value associated with key. - * - * @param key the thread local object - * @param value the value to be set - */ - private void set(CoroutineLocal key, Object value) { - - // We don't use a fast path as with get() because it is at - // least as common to use set() to create new entries as - // it is to replace existing ones, in which case, a fast - // path would fail more often than not. - - Entry[] tab = table; - int len = tab.length; - int i = key.coroutineLocalHashCode & (len - 1); - - for (Entry e = tab[i]; e != null; e = tab[i = nextIndex(i, len)]) { - CoroutineLocal k = e.get(); - - if (k == key) { - e.value = value; - return; - } - - if (k == null) { - replaceStaleEntry(key, value, i); - return; - } - } - - tab[i] = new Entry(key, value); - int sz = ++size; - if (!cleanSomeSlots(i, sz) && sz >= threshold) - rehash(); - } - - /** - * Remove the entry for key. - */ - private void remove(CoroutineLocal key) { - Entry[] tab = table; - int len = tab.length; - int i = key.coroutineLocalHashCode & (len - 1); - for (Entry e = tab[i]; e != null; e = tab[i = nextIndex(i, len)]) { - if (e.get() == key) { - e.clear(); - expungeStaleEntry(i); - return; - } - } - } - - /** - * Replace a stale entry encountered during a set operation with an entry for the specified key. The value passed in the value - * parameter is stored in the entry, whether or not an entry already exists for the specified key. - * - * As a side effect, this method expunges all stale entries in the "run" containing the stale entry. (A run is a sequence of entries - * between two null slots.) - * - * @param key the key - * @param value the value to be associated with key - * @param staleSlot index of the first stale entry encountered while searching for key. - */ - private void replaceStaleEntry(CoroutineLocal key, Object value, int staleSlot) { - Entry[] tab = table; - int len = tab.length; - Entry e; - - // Back up to check for prior stale entry in current run. - // We clean out whole runs at a time to avoid continual - // incremental rehashing due to garbage collector freeing - // up refs in bunches (i.e., whenever the collector runs). - int slotToExpunge = staleSlot; - for (int i = prevIndex(staleSlot, len); (e = tab[i]) != null; i = prevIndex(i, len)) - if (e.get() == null) - slotToExpunge = i; - - // Find either the key or trailing null slot of run, whichever - // occurs first - for (int i = nextIndex(staleSlot, len); (e = tab[i]) != null; i = nextIndex(i, len)) { - CoroutineLocal k = e.get(); - - // If we find key, then we need to swap it - // with the stale entry to maintain hash table order. - // The newly stale slot, or any other stale slot - // encountered above it, can then be sent to expungeStaleEntry - // to remove or rehash all of the other entries in run. - if (k == key) { - e.value = value; - - tab[i] = tab[staleSlot]; - tab[staleSlot] = e; - - // Start expunge at preceding stale entry if it exists - if (slotToExpunge == staleSlot) - slotToExpunge = i; - cleanSomeSlots(expungeStaleEntry(slotToExpunge), len); - return; - } - - // If we didn't find stale entry on backward scan, the - // first stale entry seen while scanning for key is the - // first still present in the run. - if (k == null && slotToExpunge == staleSlot) - slotToExpunge = i; - } - - // If key not found, put new entry in stale slot - tab[staleSlot].value = null; - tab[staleSlot] = new Entry(key, value); - - // If there are any other stale entries in run, expunge them - if (slotToExpunge != staleSlot) - cleanSomeSlots(expungeStaleEntry(slotToExpunge), len); - } - - /** - * Expunge a stale entry by rehashing any possibly colliding entries lying between staleSlot and the next null slot. This also - * expunges any other stale entries encountered before the trailing null. See Knuth, Section 6.4 - * - * @param staleSlot index of slot known to have null key - * @return the index of the next null slot after staleSlot (all between staleSlot and this slot will have been checked for - * expunging). - */ - private int expungeStaleEntry(int staleSlot) { - Entry[] tab = table; - int len = tab.length; - - // expunge entry at staleSlot - tab[staleSlot].value = null; - tab[staleSlot] = null; - size--; - - // Rehash until we encounter null - Entry e; - int i; - for (i = nextIndex(staleSlot, len); (e = tab[i]) != null; i = nextIndex(i, len)) { - CoroutineLocal k = e.get(); - if (k == null) { - e.value = null; - tab[i] = null; - size--; - } - else { - int h = k.coroutineLocalHashCode & (len - 1); - if (h != i) { - tab[i] = null; - - // Unlike Knuth 6.4 Algorithm R, we must scan until - // null because multiple entries could have been stale. - while (tab[h] != null) - h = nextIndex(h, len); - tab[h] = e; - } - } - } - return i; - } - - /** - * Heuristically scan some cells looking for stale entries. This is invoked when either a new element is added, or another stale one - * has been expunged. It performs a logarithmic number of scans, as a balance between no scanning (fast but retains garbage) and a - * number of scans proportional to number of elements, that would find all garbage but would cause some insertions to take O(n) - * time. - * - * @param i a position known NOT to hold a stale entry. The scan starts at the element after i. - * - * @param n scan control: log2(n) cells are scanned, unless a stale entry is found, in which case - * log2(table.length)-1 additional cells are scanned. When called from insertions, this parameter is the number - * of elements, but when from replaceStaleEntry, it is the table length. (Note: all this could be changed to be either - * more or less aggressive by weighting n instead of just using straight log n. But this version is simple, fast, and - * seems to work well.) - * - * @return true if any stale entries have been removed. - */ - private boolean cleanSomeSlots(int i, int n) { - boolean removed = false; - Entry[] tab = table; - int len = tab.length; - do { - i = nextIndex(i, len); - Entry e = tab[i]; - if (e != null && e.get() == null) { - n = len; - removed = true; - i = expungeStaleEntry(i); - } - } - while ((n >>>= 1) != 0); - return removed; - } - - /** - * Re-pack and/or re-size the table. First scan the entire table removing stale entries. If this doesn't sufficiently shrink the - * size of the table, double the table size. - */ - private void rehash() { - expungeStaleEntries(); - - // Use lower threshold for doubling to avoid hysteresis - if (size >= threshold - threshold / 4) - resize(); - } - - /** - * Double the capacity of the table. - */ - private void resize() { - Entry[] oldTab = table; - int oldLen = oldTab.length; - int newLen = oldLen * 2; - Entry[] newTab = new Entry[newLen]; - int count = 0; - - for (int j = 0; j < oldLen; ++j) { - Entry e = oldTab[j]; - if (e != null) { - CoroutineLocal k = e.get(); - if (k == null) { - e.value = null; // Help the GC - } - else { - int h = k.coroutineLocalHashCode & (newLen - 1); - while (newTab[h] != null) - h = nextIndex(h, newLen); - newTab[h] = e; - count++; - } - } - } - - setThreshold(newLen); - size = count; - table = newTab; - } - - /** - * Expunge all stale entries in the table. - */ - private void expungeStaleEntries() { - Entry[] tab = table; - int len = tab.length; - for (int j = 0; j < len; j++) { - Entry e = tab[j]; - if (e != null && e.get() == null) - expungeStaleEntry(j); - } - } - } -} diff --git a/src/share/classes/java/dyn/CoroutineSupport.java b/src/share/classes/java/dyn/CoroutineSupport.java index cc245cd4acac29d8e654a6b0a3bea1ffb4dc975e..5b2cfdf1037def0d75bf05bd1542b601d20be8b0 100644 --- a/src/share/classes/java/dyn/CoroutineSupport.java +++ b/src/share/classes/java/dyn/CoroutineSupport.java @@ -25,289 +25,356 @@ package java.dyn; +import com.alibaba.wisp.engine.WispTask; +import sun.misc.Contended; +import sun.misc.SharedSecrets; import sun.reflect.generics.reflectiveObjects.NotImplementedException; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; + +@Contended public class CoroutineSupport { - // Controls debugging and tracing, for maximum performance the actual if(DEBUG/TRACE) code needs to be commented out - static final boolean DEBUG = false; - static final boolean TRACE = false; - - static final Object TERMINATED = new Object(); - - // The thread that this CoroutineSupport belongs to. There's only one CoroutineSupport per Thread - private final Thread thread; - // The initial coroutine of the Thread - private final Coroutine threadCoroutine; - - // The currently executing, symmetric or asymmetric coroutine - CoroutineBase currentCoroutine; - // The anchor of the doubly-linked ring of coroutines - Coroutine scheduledCoroutines; - - static { - registerNatives(); - } - - public CoroutineSupport(Thread thread) { - if (thread.getCoroutineSupport() != null) { - throw new IllegalArgumentException("Cannot instantiate CoroutineThreadSupport for existing Thread"); - } - this.thread = thread; - threadCoroutine = new Coroutine(this, getThreadCoroutine()); - threadCoroutine.next = threadCoroutine; - threadCoroutine.last = threadCoroutine; - currentCoroutine = threadCoroutine; - scheduledCoroutines = threadCoroutine; - } - - public Coroutine threadCoroutine() { - return threadCoroutine; - } - - void addCoroutine(Coroutine coroutine, long stacksize) { - assert scheduledCoroutines != null; - assert currentCoroutine != null; - - coroutine.data = createCoroutine(coroutine, stacksize); - if (DEBUG) { - System.out.println("add Coroutine " + coroutine + ", data" + coroutine.data); - } - - // add the coroutine into the doubly linked ring - coroutine.next = scheduledCoroutines.next; - coroutine.last = scheduledCoroutines; - scheduledCoroutines.next = coroutine; - coroutine.next.last = coroutine; - } - - void addCoroutine(AsymCoroutine coroutine, long stacksize) { - coroutine.data = createCoroutine(coroutine, stacksize); - if (DEBUG) { - System.out.println("add AsymCoroutine " + coroutine + ", data" + coroutine.data); - } - - coroutine.caller = null; - } - - Thread getThread() { - return thread; - } - - public void drain() { - if (Thread.currentThread() != thread) { - throw new IllegalArgumentException("Cannot drain another threads CoroutineThreadSupport"); - } - - if (DEBUG) { - System.out.println("draining"); - } - try { - // drain all scheduled coroutines - while (scheduledCoroutines.next != scheduledCoroutines) { - symmetricExitInternal(scheduledCoroutines.next); - } - - CoroutineBase coro; - while ((coro = cleanupCoroutine()) != null) { - System.out.println(coro); - throw new NotImplementedException(); - } - } catch (Throwable t) { - t.printStackTrace(); - } - } - - void symmetricYield() { - if (scheduledCoroutines != currentCoroutine) { - throw new IllegalThreadStateException("Cannot call yield from within an asymmetric coroutine"); - } - assert currentCoroutine instanceof Coroutine; - - if (TRACE) { - System.out.println("locking for symmetric yield..."); - } - - Coroutine next = scheduledCoroutines.next; - if (next == scheduledCoroutines) { - return; - } - - if (TRACE) { - System.out.println("symmetric yield to " + next); - } - - final Coroutine current = scheduledCoroutines; - scheduledCoroutines = next; - currentCoroutine = next; - - switchTo(current, next); - } - - public void symmetricYieldTo(Coroutine target) { - if (scheduledCoroutines != currentCoroutine) { - throw new IllegalThreadStateException("Cannot call yield from within an asymmetric coroutine"); - } - assert currentCoroutine instanceof Coroutine; - - moveCoroutine(scheduledCoroutines, target); - - final Coroutine current = scheduledCoroutines; - scheduledCoroutines = target; - currentCoroutine = target; - - switchTo(current, target); - } - - private void moveCoroutine(Coroutine a, Coroutine position) { - // remove a from the ring - a.last.next = a.next; - a.next.last = a.last; - - // ... and insert at the new position - a.next = position.next; - a.last = position; - a.next.last = a; - position.next = a; - } - - public void symmetricStopCoroutine(Coroutine target) { - if (scheduledCoroutines != currentCoroutine) { - throw new IllegalThreadStateException("Cannot call yield from within an asymmetric coroutine"); - } - assert currentCoroutine instanceof Coroutine; - - moveCoroutine(scheduledCoroutines, target); - - final Coroutine current = scheduledCoroutines; - scheduledCoroutines = target; - currentCoroutine = target; - - switchToAndExit(current, target); - } - - void symmetricExitInternal(Coroutine coroutine) { - if (scheduledCoroutines != currentCoroutine) { - throw new IllegalThreadStateException("Cannot call exitNext from within an unscheduled coroutine"); - } - assert currentCoroutine instanceof Coroutine; - assert currentCoroutine != coroutine; - - // remove the coroutine from the ring - coroutine.last.next = coroutine.next; - coroutine.next.last = coroutine.last; - - if (!isDisposable(coroutine.data)) { - // and insert it before the current coroutine - coroutine.last = scheduledCoroutines.last; - coroutine.next = scheduledCoroutines; - coroutine.last.next = coroutine; - scheduledCoroutines.last = coroutine; - - final Coroutine current = scheduledCoroutines; - scheduledCoroutines = coroutine; - currentCoroutine = coroutine; - switchToAndExit(current, coroutine); - } - } - - void asymmetricCall(AsymCoroutine target) { - if (target.threadSupport != this) { - throw new IllegalArgumentException("Cannot activate a coroutine that belongs to another thread"); - } - if (target.caller != null) { - throw new IllegalArgumentException("Coroutine already in use"); - } - if (target.data == 0) { - throw new IllegalArgumentException("Target coroutine has already finished"); - } - if (TRACE) { - System.out.println("yieldCall " + target + " (" + target.data + ")"); - } - - final CoroutineBase current = currentCoroutine; - target.caller = current; - currentCoroutine = target; - switchTo(target.caller, target); - } - - void asymmetricReturn(final AsymCoroutine current) { - if (current != currentCoroutine) { - throw new IllegalThreadStateException("cannot return from non-current fiber"); - } - final CoroutineBase caller = current.caller; - if (TRACE) { - System.out.println("yieldReturn " + caller + " (" + caller.data + ")"); - } - - current.caller = null; - currentCoroutine = caller; - switchTo(current, currentCoroutine); - } - - void asymmetricReturnAndTerminate(final AsymCoroutine current) { - if (current != currentCoroutine) { - throw new IllegalThreadStateException("cannot return from non-current fiber"); - } - final CoroutineBase caller = current.caller; - if (TRACE) { - System.out.println("yieldReturn " + caller + " (" + caller.data + ")"); - } - - current.caller = null; - currentCoroutine = caller; - switchToAndTerminate(current, currentCoroutine); - } - - void terminateCoroutine() { - assert currentCoroutine == scheduledCoroutines; - assert currentCoroutine != threadCoroutine : "cannot exit thread coroutine"; - assert scheduledCoroutines != scheduledCoroutines.next : "last coroutine shouldn't call coroutineexit"; - - Coroutine old = scheduledCoroutines; - Coroutine forward = old.next; - currentCoroutine = forward; - scheduledCoroutines = forward; - old.last.next = old.next; - old.next.last = old.last; - - if (DEBUG) { - System.out.println("to be terminated: " + old); - } - switchToAndTerminate(old, forward); - } - - void terminateCallable() { - assert currentCoroutine != scheduledCoroutines; - assert currentCoroutine instanceof AsymCoroutine; - - if (DEBUG) { - System.out.println("to be terminated: " + currentCoroutine); - } - asymmetricReturnAndTerminate((AsymCoroutine) currentCoroutine); - } - - public boolean isCurrent(CoroutineBase coroutine) { - return coroutine == currentCoroutine; - } - - public CoroutineBase getCurrent() { - return currentCoroutine; - } - - private static native void registerNatives(); - - private static native long getThreadCoroutine(); - - private static native long createCoroutine(CoroutineBase coroutine, long stacksize); - - private static native void switchTo(CoroutineBase current, CoroutineBase target); - - private static native void switchToAndTerminate(CoroutineBase current, CoroutineBase target); - - private static native void switchToAndExit(CoroutineBase current, CoroutineBase target); - - private static native boolean isDisposable(long coroutine); - - private static native CoroutineBase cleanupCoroutine(); + private static final boolean CHECK_LOCK = true; + private static final int SPIN_BACKOFF_LIMIT = 2 << 8; + + private static AtomicInteger idGen = new AtomicInteger(); + + // The thread that this CoroutineSupport belongs to. There's only one CoroutineSupport per Thread + private final Thread thread; + // The initial coroutine of the Thread + private final Coroutine threadCoroutine; + + // The currently executing coroutine + private Coroutine currentCoroutine; + + private volatile Thread lockOwner = null; // also protect double link list of JavaThread->coroutine_list() + private int lockRecursive; // volatile is not need + + private final int id; + private boolean terminated = false; + + static { + registerNatives(); + } + + public CoroutineSupport(Thread thread) { + if (thread.getCoroutineSupport() != null) { + throw new IllegalArgumentException("Cannot instantiate CoroutineThreadSupport for existing Thread"); + } + id = idGen.incrementAndGet(); + this.thread = thread; + threadCoroutine = new Coroutine(this, getNativeThreadCoroutine()); + markThreadCoroutine(threadCoroutine.nativeCoroutine, threadCoroutine); + currentCoroutine = threadCoroutine; + } + + public Coroutine threadCoroutine() { + return threadCoroutine; + } + + void addCoroutine(Coroutine coroutine, long stacksize) { + assert currentCoroutine != null; + lock(); + try { + coroutine.nativeCoroutine = createCoroutine(coroutine, stacksize); + } finally { + unlock(); + } + } + + Thread getThread() { + return thread; + } + + public static void checkAndThrowException(Coroutine coroutine) { + checkAndThrowException0(coroutine.nativeCoroutine); + } + + public void drain() { + if (Thread.currentThread() != thread) { + throw new IllegalArgumentException("Cannot drain another threads CoroutineThreadSupport"); + } + + lock(); + try { + // drain all coroutines + Coroutine next = null; + while ((next = getNextCoroutine(currentCoroutine.nativeCoroutine)) != currentCoroutine) { + symmetricExitInternal(next); + } + + CoroutineBase coro; + while ((coro = cleanupCoroutine()) != null) { + System.out.println(coro); + throw new NotImplementedException(); + } + } catch (Throwable t) { + t.printStackTrace(); + } finally { + assert lockOwner == thread && lockRecursive == 0; + terminated = true; + unlock(); + } + } + + /** + * optimized version of symmetricYieldTo based on assumptions: + * 1. we won't simultaneously steal a {@link Coroutine} from other threads + * 2. we won't switch to a {@link Coroutine} that's being stolen + * 3. we won't steal a running {@link Coroutine} + * this function should only be called in + * {@link com.alibaba.wisp.engine.WispTask#switchTo(WispTask, WispTask)}, + * we skipped unnecessary lock to improve performance. + * @param target + */ + public boolean unsafeSymmetricYieldTo(Coroutine target) { + if (target.threadSupport != this) { + return false; + } + final Coroutine current = currentCoroutine; + currentCoroutine = target; + switchTo(current, target); + //check if locked by exiting coroutine + beforeResume(current); + return true; + } + + public void symmetricYieldTo(Coroutine target) { + lock(); + if (target.threadSupport != this) { + unlock(); + return; + } + moveCoroutine(currentCoroutine.nativeCoroutine, target.nativeCoroutine); + unlockLater(target); + unsafeSymmetricYieldTo(target); + } + + + public void symmetricStopCoroutine(Coroutine target) { + Coroutine current; + lock(); + try { + if (target.threadSupport != this) { + unlock(); + return; + } + current = currentCoroutine; + currentCoroutine = target; + moveCoroutine(current.nativeCoroutine, target.nativeCoroutine); + } finally { + unlock(); + } + switchToAndExit(current, target); + } + + + /** + * switch to coroutine and throw Exception in coroutine + */ + void symmetricExitInternal(Coroutine coroutine) { + assert currentCoroutine != coroutine; + assert coroutine.threadSupport == this; + + if (!testDisposableAndTryReleaseStack(coroutine.nativeCoroutine)) { + moveCoroutine(currentCoroutine.nativeCoroutine, coroutine.nativeCoroutine); + + final Coroutine current = currentCoroutine; + currentCoroutine = coroutine; + switchToAndExit(current, coroutine); + beforeResume(current); + } + } + + + /** + * terminate current coroutine and yield forward + */ + void terminateCoroutine() { + assert currentCoroutine != threadCoroutine : "cannot exit thread coroutine"; + assert currentCoroutine != getNextCoroutine(currentCoroutine.nativeCoroutine) : "last coroutine shouldn't call coroutineexit"; + + lock(); + Coroutine old = currentCoroutine; + Coroutine forward = getNextCoroutine(old.nativeCoroutine); + currentCoroutine = forward; + + unlockLater(forward); + switchToAndTerminate(old, forward); + } + + /** + * Steal coroutine from it's carrier thread to current thread. + * + * @param failOnContention steal fail if there's too much lock contention + * + * @param coroutine to be stolen + */ + Coroutine.StealResult steal(Coroutine coroutine, boolean failOnContention) { + assert coroutine.threadSupport.threadCoroutine() != coroutine; + CoroutineSupport source = this; + CoroutineSupport target = SharedSecrets.getJavaLangAccess().currentThread0().getCoroutineSupport(); + + if (source == target) { + return Coroutine.StealResult.SUCCESS; + } + + if (source.id < target.id) { // prevent dead lock + if (!source.lockInternal(failOnContention)) { + return Coroutine.StealResult.FAIL_BY_CONTENTION; + } + target.lock(); + } else { + target.lock(); + if (!source.lockInternal(failOnContention)) { + target.unlock(); + return Coroutine.StealResult.FAIL_BY_CONTENTION; + } + } + + try { + if (source.terminated || coroutine.finished || + coroutine.threadSupport != source || // already been stolen + source.currentCoroutine == coroutine) { + return Coroutine.StealResult.FAIL_BY_STATUS; + } + if (!stealCoroutine(coroutine.nativeCoroutine)) { // native frame + return Coroutine.StealResult.FAIL_BY_NATIVE_FRAME; + } + coroutine.threadSupport = target; + } finally { + source.unlock(); + target.unlock(); + } + + return Coroutine.StealResult.SUCCESS; + } + + /** + * Can not be stolen while executing this, because lock is held + */ + void beforeResume(CoroutineBase source) { + if (source.needsUnlock) { + source.needsUnlock = false; + source.threadSupport.unlock(); + } + } + + private void unlockLater(CoroutineBase next) { + if (CHECK_LOCK && next.needsUnlock) { + throw new InternalError("pending unlock"); + } + next.needsUnlock = true; + } + + private void lock() { + boolean success = lockInternal(false); + assert success; + } + + private boolean lockInternal(boolean tryingLock) { + final Thread th = SharedSecrets.getJavaLangAccess().currentThread0(); + if (lockOwner == th) { + lockRecursive++; + return true; + } + for (int spin = 1; ; ) { + if (lockOwner == null && LOCK_UPDATER.compareAndSet(this, null, th)) { + return true; + } + for (int i = 0; i < spin; ) { + i++; + } + if (spin == SPIN_BACKOFF_LIMIT) { + if (tryingLock) { + return false; + } + SharedSecrets.getJavaLangAccess().yield0(); // yield safepoint + } else { // back off + spin *= 2; + } + } + } + + private void unlock() { + if (CHECK_LOCK && SharedSecrets.getJavaLangAccess().currentThread0() != lockOwner) { + throw new InternalError("unlock from non-owner thread"); + } + if (lockRecursive > 0) { + lockRecursive--; + } else { + LOCK_UPDATER.lazySet(this, null); + } + } + + private static final AtomicReferenceFieldUpdater LOCK_UPDATER; + + static { + LOCK_UPDATER = AtomicReferenceFieldUpdater.newUpdater(CoroutineSupport.class, Thread.class, "lockOwner"); + } + + public boolean isCurrent(CoroutineBase coroutine) { + return coroutine == currentCoroutine; + } + + public CoroutineBase getCurrent() { + return currentCoroutine; + } + + + private static native void registerNatives(); + + private static native long getNativeThreadCoroutine(); + + /** + * need lock because below methods will operate on thread->coroutine_list() + */ + private static native long createCoroutine(CoroutineBase coroutine, long stacksize); + + private static native void switchToAndTerminate(CoroutineBase current, CoroutineBase target); + + private static native boolean testDisposableAndTryReleaseStack(long coroutine); + + private static native boolean stealCoroutine(long coroPtr); + // end of locking + + /** + * get next {@link Coroutine} from current thread's doubly linked {@link Coroutine} list + * @param coroPtr hotspot coroutine + * @return java Coroutine + */ + private static native Coroutine getNextCoroutine(long coroPtr); + + /** + * move coroPtr to targetPtr's next field in underlying hotspot coroutine list + * @param coroPtr current threadCoroutine + * @param targetPtr coroutine that is about to exit + */ + private static native void moveCoroutine(long coroPtr, long targetPtr); + + /** + * track hotspot couroutine with java coroutine. + * @param coroPtr threadCoroutine in hotspot + * @param threadCoroutine threadCoroutine in java + */ + private static native void markThreadCoroutine(long coroPtr, CoroutineBase threadCoroutine); + + private static native void switchTo(CoroutineBase current, CoroutineBase target); + + private static native void switchToAndExit(CoroutineBase current, CoroutineBase target); + + private static native CoroutineBase cleanupCoroutine(); + + public static native void setWispBooted(); + + /** + * this will turn on a safepoint to stop all threads. + * @param coroPtr + * @return target coroutine's stack + */ + public static native StackTraceElement[] getCoroutineStack(long coroPtr); + + private static native void checkAndThrowException0(long coroPtr); } diff --git a/src/share/classes/java/lang/SecurityManager.java b/src/share/classes/java/lang/SecurityManager.java index 2784ffd5c6c09362724fc968b2bbcb7eb03097ba..676073b20cbee9e0421bc8dd966ea1dff5284db9 100644 --- a/src/share/classes/java/lang/SecurityManager.java +++ b/src/share/classes/java/lang/SecurityManager.java @@ -1773,7 +1773,7 @@ class SecurityManager { * @see java.lang.ThreadGroup */ public ThreadGroup getThreadGroup() { - return Thread.currentThread().getThreadGroup(); + return Thread.currentThread0().getThreadGroup(); } } diff --git a/src/share/classes/java/lang/System.java b/src/share/classes/java/lang/System.java index fc67d54afa8cca44a41ae33af902ad3b1deb0dd8..1de404d3ab7648c81b7759e4b6f34e771564933f 100644 --- a/src/share/classes/java/lang/System.java +++ b/src/share/classes/java/lang/System.java @@ -34,12 +34,14 @@ import java.util.StringTokenizer; import java.util.Map; import java.security.AccessController; import java.security.PrivilegedAction; -import java.security.AllPermission; import java.nio.channels.Channel; import java.nio.channels.spi.SelectorProvider; import com.alibaba.rcm.internal.AbstractResourceContainer; import com.alibaba.tenant.TenantContainer; import com.alibaba.tenant.TenantGlobals; + +import com.alibaba.wisp.engine.WispEngine; +import com.alibaba.wisp.engine.WispTask; import sun.misc.VM; import sun.nio.ch.Interruptible; import sun.reflect.CallerSensitive; @@ -1318,6 +1320,44 @@ public final class System { public AbstractResourceContainer getInheritedResourceContainer(Thread thread) { return thread.inheritedResourceContainer; } + public Thread currentThread0() { + return Thread.currentThread0(); + } + + @Override + public void yield0() { + Thread.yield0(); + } + + @Override + public void setWispTask(Thread thread, WispTask task) { + thread.wispTask = task; + } + + @Override + public WispTask getWispTask(Thread thread) { + return thread.wispTask; + } + + @Override + public void setWispAlive(Thread thread, boolean b) { + thread.wispIsAlive = b; + } + + @Override + public boolean isInSameNative(Thread thread) { + return thread.isInSameNative(); + } + + @Override + public void threadExit(Thread thread) { + thread.exit(); + } + + @Override + public void wispBooted() { + Thread.wispBooted(); + } }); } } diff --git a/src/share/classes/java/lang/Thread.java b/src/share/classes/java/lang/Thread.java index 005a47e3817226faf3595356b9ebf5420da3cf1a..e69e583da03566dd7a2827721de0c44b8b4a664b 100644 --- a/src/share/classes/java/lang/Thread.java +++ b/src/share/classes/java/lang/Thread.java @@ -39,6 +39,9 @@ import java.util.concurrent.ConcurrentMap; import java.util.concurrent.locks.LockSupport; import com.alibaba.rcm.internal.AbstractResourceContainer; import sun.misc.VM; +import com.alibaba.wisp.engine.*; +import sun.misc.SharedSecrets; +import sun.misc.WispEngineAccess; import sun.nio.ch.Interruptible; import sun.reflect.CallerSensitive; import sun.reflect.Reflection; @@ -277,22 +280,58 @@ class Thread implements Runnable { private CoroutineSupport coroutineSupport; + WispTask wispTask; + + volatile boolean wispIsAlive; + // wispTask is set by carrier thread async, so we need additional flag + public CoroutineSupport getCoroutineSupport() { - return coroutineSupport; + if (coroutineSupport != null) { + return coroutineSupport; + } + + Thread t = currentThread0(); + return t == this ? null : t.getCoroutineSupport(); } private void initializeCoroutineSupport() { - if (sun.misc.VM.isEnableCoroutine()) { + if (coroutineSupport == null) { coroutineSupport = new CoroutineSupport(this); } } + private void destroyCoroutineSupport() { + try { + if (coroutineSupport != null) { + if (WEA != null) { + WEA.destroy(); + } + coroutineSupport.drain(); + } + } catch (Throwable t) { + t.printStackTrace(); + } + } + /** * Returns a reference to the currently executing thread object. * + * Returns a thread wrapper of the currently executing wispTask if + * WispCarrier.transparentWispSwitch() is on. + * * @return the currently executing thread. */ - public static native Thread currentThread(); + public static Thread currentThread() { + if (WEA != null) { + return WEA.getCurrentTask().getThreadWrapper(); + } + return currentThread0(); + } + + /** + * Always return the underlying Java thread. + */ + static native Thread currentThread0(); /** * A hint to the scheduler that the current thread is willing to yield @@ -310,7 +349,15 @@ class Thread implements Runnable { * concurrency control constructs such as the ones in the * {@link java.util.concurrent.locks} package. */ - public static native void yield(); + public static void yield() { + if (WEA != null) { + WEA.yield(); + } else { + yield0(); + } + } + + static native void yield0(); /** * Causes the currently executing thread to sleep (temporarily cease @@ -329,7 +376,22 @@ class Thread implements Runnable { * interrupted status of the current thread is * cleared when this exception is thrown. */ - public static native void sleep(long millis) throws InterruptedException; + public static void sleep(long millis) throws InterruptedException { + if (WEA != null) { + if (Thread.interrupted()) { + throw new InterruptedException("sleep interrupted"); + } + WEA.sleep(millis); + if (Thread.interrupted()) { + throw new InterruptedException("sleep interrupted"); + } + return; + } else { + sleep0(millis); + } + } + + public static native void sleep0(long millis) throws InterruptedException; /** * Causes the currently executing thread to sleep (temporarily cease @@ -754,7 +816,10 @@ class Thread implements Runnable { boolean started = false; try { - start0(); + if (!(WEA != null && WispEngine.enableThreadAsWisp() && + WEA.tryStartThreadAsWisp(this, target))) { + start0(); + } started = true; } finally { try { @@ -793,11 +858,7 @@ class Thread implements Runnable { * This method is called by the system to give a Thread * a chance to clean up before it actually exits. */ - private void exit() { - if (sun.misc.VM.isEnableCoroutine() && (coroutineSupport != null)) { - coroutineSupport.drain(); - } - + void exit() { if (group != null) { group.threadTerminated(this); group = null; @@ -959,18 +1020,29 @@ class Thread implements Runnable { * @spec JSR-51 */ public void interrupt() { - if (this != Thread.currentThread()) + + if (this != Thread.currentThread()) { checkAccess(); + } synchronized (blockerLock) { Interruptible b = blocker; if (b != null) { - interrupt0(); // Just to set the interrupt flag + if (WEA != null && wispTask != null) { + WEA.interrupt(wispTask); + } else { + interrupt0(); // Just to set the interrupt flag + } b.interrupt(this); return; } } - interrupt0(); + + if (WEA != null && wispTask != null) { + WEA.interrupt(wispTask); + } else { + interrupt0(); + } } /** @@ -991,7 +1063,14 @@ class Thread implements Runnable { * @revised 6.0 */ public static boolean interrupted() { - return currentThread().isInterrupted(true); + Thread current = currentThread0(); + if (WEA != null) { + WispTask task = WEA.getCurrentTask(); + if (task != null) { + return WEA.testInterruptedAndClear(task, true); + } + } + return current.isInterrupted(true); } /** @@ -1008,6 +1087,9 @@ class Thread implements Runnable { * @revised 6.0 */ public boolean isInterrupted() { + if (WEA != null && wispTask != null) { + return WEA.testInterruptedAndClear(wispTask, false); + } return isInterrupted(false); } @@ -1047,7 +1129,14 @@ class Thread implements Runnable { * @return true if this thread is alive; * false otherwise. */ - public final native boolean isAlive(); + public final boolean isAlive() { + if (WEA != null && wispIsAlive) { + return true; + } + return isAlive0(); + } + + private final native boolean isAlive0(); /** * Suspends this thread. @@ -1581,7 +1670,29 @@ class Thread implements Runnable { * @since 1.5 */ public StackTraceElement[] getStackTrace() { - if (this != Thread.currentThread()) { + boolean slowPath; + if (WEA != null) { + WispTask task = this.wispTask; + if (task == null) { + // When we create a thread, coroutine will not be created immediately. + // So if we take the stack trace at once, it will NCE. + // See: NullStackTrace.java and 6571589, Thread return an array which size is 0. + return new StackTraceElement[0]; + } + if (!WEA.runningAsCoroutine(this)) { + slowPath = this != Thread.currentThread(); + } else { + boolean isCurrentTask = WEA.getCurrentTask() == task; + slowPath = !isCurrentTask; + if (!WEA.isThreadTask(task) && !isCurrentTask) { + return WEA.getStackTrace(task); + } + } + } else { + slowPath = this != Thread.currentThread(); + } + + if (slowPath) { // check for getStackTrace permission SecurityManager security = System.getSecurityManager(); if (security != null) { @@ -1864,6 +1975,11 @@ class Thread implements Runnable { return sun.misc.VM.toThreadState(threadStatus); } + /** + * @return if this thread is still executing the same JNI code + */ + native boolean isInSameNative(); + // Added in JSR-166 /** @@ -2094,4 +2210,15 @@ class Thread implements Runnable { private native void resume0(); private native void interrupt0(); private native void setNativeName(String name); + + // prevent load Wisp classes accidentally + private static WispEngineAccess WEA; + + static void wispBooted() { + if (!WispEngine.transparentWispSwitch()) { + // assert in this class will crash jvm + throw new AssertionError(); + } + WEA = SharedSecrets.getWispEngineAccess(); + } } diff --git a/src/share/classes/java/lang/management/PlatformComponent.java b/src/share/classes/java/lang/management/PlatformComponent.java index bf84bdc983b82b920e8a0a2f85c5f0766cd6963b..93fe022a965693aba4c5e7a4a1312748624dd0a8 100644 --- a/src/share/classes/java/lang/management/PlatformComponent.java +++ b/src/share/classes/java/lang/management/PlatformComponent.java @@ -37,6 +37,7 @@ import javax.management.ObjectName; import com.alibaba.management.TenantContainerMXBean; import com.alibaba.management.ElasticHeapMXBean; +import com.alibaba.management.WispCounterMXBean; import com.sun.management.HotSpotDiagnosticMXBean; import com.sun.management.UnixOperatingSystemMXBean; @@ -303,6 +304,19 @@ enum PlatformComponent { public List getMXBeans() { return Collections.singletonList(ManagementFactoryHelper.getElasticHeapMXBean()); } + }), + + /** + * Wisp Counter. + */ + WISP_COUNTER( + "com.alibaba.management.WispCounterMXBean", + "com.alibaba.management", "WispCounter", defaultKeyProperties(), + true, // singleton + new MXBeanFetcher() { + public List getMXBeans() { + return Collections.singletonList(ManagementFactoryHelper.getWispCounterMXBean()); + } }); diff --git a/src/share/classes/java/net/DatagramSocket.java b/src/share/classes/java/net/DatagramSocket.java index 18f2fe1d518a7609cf8b748b9055894b15cdb9e3..73ceca624477d1da6a4a3867c17394e550a5920b 100644 --- a/src/share/classes/java/net/DatagramSocket.java +++ b/src/share/classes/java/net/DatagramSocket.java @@ -25,6 +25,11 @@ package java.net; +import com.alibaba.wisp.engine.WispEngine; +import sun.misc.SharedSecrets; +import sun.misc.WispEngineAccess; +import sun.nio.ch.WispUdpSocketImpl; + import java.io.IOException; import java.nio.channels.DatagramChannel; import java.security.AccessController; @@ -65,6 +70,13 @@ import java.security.PrivilegedExceptionAction; */ public class DatagramSocket implements java.io.Closeable { + + /** + * If WispEngine.transparentWispSwitch(), proxy all + * socket request to this impl. + */ + private WispUdpSocketImpl asyncImpl; + /** * Various states of this socket. */ @@ -235,8 +247,12 @@ class DatagramSocket implements java.io.Closeable { * @since 1.4 */ public DatagramSocket(SocketAddress bindaddr) throws SocketException { - // create a datagram socket. - createImpl(); + if (WispEngine.transparentWispSwitch()) { + asyncImpl = new WispUdpSocketImpl(this); + } else { + // create a datagram socket. + createImpl(); + } if (bindaddr != null) { try { bind(bindaddr); @@ -322,6 +338,8 @@ class DatagramSocket implements java.io.Closeable { static Class implClass = null; void createImpl() throws SocketException { + if (WispEngine.transparentWispSwitch()) + throw new UnsupportedOperationException(); if (impl == null) { if (factory != null) { impl = factory.createDatagramSocketImpl(); @@ -349,6 +367,8 @@ class DatagramSocket implements java.io.Closeable { * @since 1.4 */ DatagramSocketImpl getImpl() throws SocketException { + if (WispEngine.transparentWispSwitch()) + throw new UnsupportedOperationException(); if (!created) createImpl(); return impl; @@ -378,6 +398,10 @@ class DatagramSocket implements java.io.Closeable { addr = new InetSocketAddress(0); if (!(addr instanceof InetSocketAddress)) throw new IllegalArgumentException("Unsupported address type!"); + if (WispEngine.transparentWispSwitch()) { + asyncImpl.bind(addr); + return; + } InetSocketAddress epoint = (InetSocketAddress) addr; if (epoint.isUnresolved()) throw new SocketException("Unresolved address"); @@ -454,6 +478,10 @@ class DatagramSocket implements java.io.Closeable { * @see #disconnect */ public void connect(InetAddress address, int port) { + if (WispEngine.transparentWispSwitch()) { + asyncImpl.connect(address, port); + return; + } try { connectInternal(address, port); } catch (SocketException se) { @@ -484,6 +512,10 @@ class DatagramSocket implements java.io.Closeable { * @since 1.4 */ public void connect(SocketAddress addr) throws SocketException { + if (WispEngine.transparentWispSwitch()) { + asyncImpl.connect(addr); + return; + } if (addr == null) throw new IllegalArgumentException("Address can't be null"); if (!(addr instanceof InetSocketAddress)) @@ -501,6 +533,10 @@ class DatagramSocket implements java.io.Closeable { * @see #connect */ public void disconnect() { + if (WispEngine.transparentWispSwitch()) { + asyncImpl.disconnect(); + return; + } synchronized (this) { if (isClosed()) return; @@ -525,6 +561,9 @@ class DatagramSocket implements java.io.Closeable { * @since 1.4 */ public boolean isBound() { + if (WispEngine.transparentWispSwitch()) { + return asyncImpl.isBound(); + } return bound; } @@ -539,6 +578,9 @@ class DatagramSocket implements java.io.Closeable { * @since 1.4 */ public boolean isConnected() { + if (WispEngine.transparentWispSwitch()) { + return asyncImpl.isConnected(); + } return connectState != ST_NOT_CONNECTED; } @@ -553,6 +595,9 @@ class DatagramSocket implements java.io.Closeable { * @return the address to which this socket is connected. */ public InetAddress getInetAddress() { + if (WispEngine.transparentWispSwitch()) { + return asyncImpl.getInetAddress(); + } return connectedAddress; } @@ -567,6 +612,9 @@ class DatagramSocket implements java.io.Closeable { * @return the port number to which this socket is connected. */ public int getPort() { + if (WispEngine.transparentWispSwitch()) { + return asyncImpl.getPort(); + } return connectedPort; } @@ -652,6 +700,10 @@ class DatagramSocket implements java.io.Closeable { * @spec JSR-51 */ public void send(DatagramPacket p) throws IOException { + if (WispEngine.transparentWispSwitch()) { + asyncImpl.send(p); + return; + } InetAddress packetAddress = null; synchronized (p) { if (isClosed()) @@ -726,6 +778,10 @@ class DatagramSocket implements java.io.Closeable { * @spec JSR-51 */ public synchronized void receive(DatagramPacket p) throws IOException { + if (WispEngine.transparentWispSwitch()) { + p.length = asyncImpl.receive(p, p.bufLength); + return; + } synchronized (p) { if (!isBound()) bind(new InetSocketAddress(0)); @@ -845,6 +901,9 @@ class DatagramSocket implements java.io.Closeable { * @since 1.1 */ public InetAddress getLocalAddress() { + if (WispEngine.transparentWispSwitch()) { + return asyncImpl.getLocalAddress(); + } if (isClosed()) return null; InetAddress in = null; @@ -872,6 +931,9 @@ class DatagramSocket implements java.io.Closeable { {@code 0} if it is not bound yet. */ public int getLocalPort() { + if (WispEngine.transparentWispSwitch()) { + return asyncImpl.getLocalPort(); + } if (isClosed()) return -1; try { @@ -897,6 +959,10 @@ class DatagramSocket implements java.io.Closeable { * @see #getSoTimeout() */ public synchronized void setSoTimeout(int timeout) throws SocketException { + if (WispEngine.transparentWispSwitch()) { + asyncImpl.setSoTimeout(timeout); + return; + } if (isClosed()) throw new SocketException("Socket is closed"); getImpl().setOption(SocketOptions.SO_TIMEOUT, new Integer(timeout)); @@ -912,6 +978,9 @@ class DatagramSocket implements java.io.Closeable { * @see #setSoTimeout(int) */ public synchronized int getSoTimeout() throws SocketException { + if (WispEngine.transparentWispSwitch()) { + return asyncImpl.getSoTimeout(); + } if (isClosed()) throw new SocketException("Socket is closed"); if (getImpl() == null) @@ -956,6 +1025,10 @@ class DatagramSocket implements java.io.Closeable { */ public synchronized void setSendBufferSize(int size) throws SocketException{ + if (WispEngine.transparentWispSwitch()) { + asyncImpl.setSendBufferSize(size); + return; + } if (!(size > 0)) { throw new IllegalArgumentException("negative send size"); } @@ -974,6 +1047,9 @@ class DatagramSocket implements java.io.Closeable { * @see #setSendBufferSize */ public synchronized int getSendBufferSize() throws SocketException { + if (WispEngine.transparentWispSwitch()) { + return asyncImpl.getSendBufferSize(); + } if (isClosed()) throw new SocketException("Socket is closed"); int result = 0; @@ -1014,6 +1090,10 @@ class DatagramSocket implements java.io.Closeable { */ public synchronized void setReceiveBufferSize(int size) throws SocketException{ + if (WispEngine.transparentWispSwitch()) { + asyncImpl.setReceiveBufferSize(size); + return; + } if (size <= 0) { throw new IllegalArgumentException("invalid receive size"); } @@ -1032,6 +1112,9 @@ class DatagramSocket implements java.io.Closeable { */ public synchronized int getReceiveBufferSize() throws SocketException{ + if (WispEngine.transparentWispSwitch()) { + return asyncImpl.getReceiveBufferSize(); + } if (isClosed()) throw new SocketException("Socket is closed"); int result = 0; @@ -1077,6 +1160,10 @@ class DatagramSocket implements java.io.Closeable { * @see #isClosed() */ public synchronized void setReuseAddress(boolean on) throws SocketException { + if (WispEngine.transparentWispSwitch()) { + asyncImpl.setReuseAddress(on); + return; + } if (isClosed()) throw new SocketException("Socket is closed"); // Integer instead of Boolean for compatibility with older DatagramSocketImpl @@ -1096,6 +1183,9 @@ class DatagramSocket implements java.io.Closeable { * @see #setReuseAddress(boolean) */ public synchronized boolean getReuseAddress() throws SocketException { + if (WispEngine.transparentWispSwitch()) { + return asyncImpl.getReuseAddress(); + } if (isClosed()) throw new SocketException("Socket is closed"); Object o = getImpl().getOption(SocketOptions.SO_REUSEADDR); @@ -1120,6 +1210,10 @@ class DatagramSocket implements java.io.Closeable { * @see #getBroadcast() */ public synchronized void setBroadcast(boolean on) throws SocketException { + if (WispEngine.transparentWispSwitch()) { + asyncImpl.setBroadcast(on); + return; + } if (isClosed()) throw new SocketException("Socket is closed"); getImpl().setOption(SocketOptions.SO_BROADCAST, Boolean.valueOf(on)); @@ -1134,6 +1228,9 @@ class DatagramSocket implements java.io.Closeable { * @see #setBroadcast(boolean) */ public synchronized boolean getBroadcast() throws SocketException { + if (WispEngine.transparentWispSwitch()) { + return asyncImpl.getBroadcast(); + } if (isClosed()) throw new SocketException("Socket is closed"); return ((Boolean)(getImpl().getOption(SocketOptions.SO_BROADCAST))).booleanValue(); @@ -1177,6 +1274,10 @@ class DatagramSocket implements java.io.Closeable { * @see #getTrafficClass */ public synchronized void setTrafficClass(int tc) throws SocketException { + if (WispEngine.transparentWispSwitch()) { + asyncImpl.setTrafficClass(tc); + return; + } if (tc < 0 || tc > 255) throw new IllegalArgumentException("tc is not in range 0 -- 255"); @@ -1209,6 +1310,9 @@ class DatagramSocket implements java.io.Closeable { * @see #setTrafficClass(int) */ public synchronized int getTrafficClass() throws SocketException { + if (WispEngine.transparentWispSwitch()) { + return asyncImpl.getTrafficClass(); + } if (isClosed()) throw new SocketException("Socket is closed"); return ((Integer)(getImpl().getOption(SocketOptions.IP_TOS))).intValue(); @@ -1227,6 +1331,10 @@ class DatagramSocket implements java.io.Closeable { * @spec JSR-51 */ public void close() { + if (WispEngine.transparentWispSwitch()) { + asyncImpl.close(); + return; + } synchronized(closeLock) { if (isClosed()) return; @@ -1242,6 +1350,9 @@ class DatagramSocket implements java.io.Closeable { * @since 1.4 */ public boolean isClosed() { + if (WispEngine.transparentWispSwitch()) { + return asyncImpl.isClosed(); + } synchronized(closeLock) { return closed; } @@ -1262,6 +1373,9 @@ class DatagramSocket implements java.io.Closeable { * @spec JSR-51 */ public DatagramChannel getChannel() { + if (WispEngine.transparentWispSwitch()) { + return asyncImpl.getChannel(); + } return null; } diff --git a/src/share/classes/java/net/ServerSocket.java b/src/share/classes/java/net/ServerSocket.java index 6dd0496223bc5e0831a2416cddc83be1d9e97f42..f56ec85566ac412a102e062e1b3d10f1e6ad10df 100644 --- a/src/share/classes/java/net/ServerSocket.java +++ b/src/share/classes/java/net/ServerSocket.java @@ -25,6 +25,9 @@ package java.net; +import com.alibaba.wisp.engine.WispEngine; +import sun.nio.ch.WispServerSocketImpl; + import java.io.FileDescriptor; import java.io.IOException; import java.nio.channels.ServerSocketChannel; @@ -52,6 +55,8 @@ import sun.security.util.SecurityConstants; */ public class ServerSocket implements java.io.Closeable { + + private WispServerSocketImpl asyncImpl; /** * Various states of this socket. */ @@ -80,6 +85,9 @@ class ServerSocket implements java.io.Closeable { */ ServerSocket(SocketImpl impl) { checkPermission(); + if (WispEngine.transparentWispSwitch()) + throw new UnsupportedOperationException(); + this.impl = impl; impl.setServerSocket(this); } @@ -99,6 +107,10 @@ class ServerSocket implements java.io.Closeable { * @revised 1.4 */ public ServerSocket() throws IOException { + if (WispEngine.transparentWispSwitch()) { + asyncImpl = new WispServerSocketImpl(); + return; + } setImpl(); } @@ -242,7 +254,12 @@ class ServerSocket implements java.io.Closeable { * @since JDK1.1 */ public ServerSocket(int port, int backlog, InetAddress bindAddr) throws IOException { - setImpl(); + if (WispEngine.transparentWispSwitch()) { + asyncImpl = new WispServerSocketImpl(); + } else { + setImpl(); + } + if (port < 0 || port > 0xFFFF) throw new IllegalArgumentException( "Port value out of range: " + port); @@ -268,12 +285,18 @@ class ServerSocket implements java.io.Closeable { * @since 1.4 */ SocketImpl getImpl() throws SocketException { + if (WispEngine.transparentWispSwitch()) + throw new UnsupportedOperationException(); + if (!created) createImpl(); return impl; } private void checkOldImpl() { + if (WispEngine.transparentWispSwitch()) + throw new UnsupportedOperationException(); + if (impl == null) return; // SocketImpl.connect() is a protected method, therefore we need to use @@ -294,6 +317,9 @@ class ServerSocket implements java.io.Closeable { } private void setImpl() { + if (WispEngine.transparentWispSwitch()) + throw new UnsupportedOperationException(); + if (factory != null) { impl = factory.createSocketImpl(); checkOldImpl(); @@ -313,6 +339,9 @@ class ServerSocket implements java.io.Closeable { * @since 1.4 */ void createImpl() throws SocketException { + if (WispEngine.transparentWispSwitch()) + throw new UnsupportedOperationException(); + if (impl == null) setImpl(); try { @@ -341,6 +370,10 @@ class ServerSocket implements java.io.Closeable { * @since 1.4 */ public void bind(SocketAddress endpoint) throws IOException { + if (WispEngine.transparentWispSwitch()) { + asyncImpl.bind(endpoint); + return; + } bind(endpoint, 50); } @@ -370,6 +403,10 @@ class ServerSocket implements java.io.Closeable { * @since 1.4 */ public void bind(SocketAddress endpoint, int backlog) throws IOException { + if (WispEngine.transparentWispSwitch()) { + asyncImpl.bind(endpoint, backlog); + return; + } if (isClosed()) throw new SocketException("Socket is closed"); if (!oldImpl && isBound()) @@ -418,6 +455,9 @@ class ServerSocket implements java.io.Closeable { * @see SecurityManager#checkConnect */ public InetAddress getInetAddress() { + if (WispEngine.transparentWispSwitch()) + return asyncImpl.getInetAddress(); + if (!isBound()) return null; try { @@ -447,6 +487,9 @@ class ServerSocket implements java.io.Closeable { * -1 if the socket is not bound yet. */ public int getLocalPort() { + if (WispEngine.transparentWispSwitch()) + return asyncImpl.getLocalPort(); + if (!isBound()) return -1; try { @@ -524,6 +567,9 @@ class ServerSocket implements java.io.Closeable { throw new SocketException("Socket is closed"); if (!isBound()) throw new SocketException("Socket is not bound yet"); + if (WispEngine.transparentWispSwitch()) { + return asyncImpl.accept(); + } Socket s = new Socket((SocketImpl) null); implAccept(s); return s; @@ -546,6 +592,9 @@ class ServerSocket implements java.io.Closeable { * @spec JSR-51 */ protected final void implAccept(Socket s) throws IOException { + if (WispEngine.transparentWispSwitch()) + throw new UnsupportedOperationException(); + SocketImpl si = null; try { if (s.impl == null) @@ -593,6 +642,9 @@ class ServerSocket implements java.io.Closeable { * @spec JSR-51 */ public void close() throws IOException { + if (WispEngine.transparentWispSwitch()) + asyncImpl.close(); + synchronized(closeLock) { if (isClosed()) return; @@ -619,6 +671,9 @@ class ServerSocket implements java.io.Closeable { * @spec JSR-51 */ public ServerSocketChannel getChannel() { + if (WispEngine.transparentWispSwitch()) + return asyncImpl.getChannel(); + return null; } @@ -629,6 +684,8 @@ class ServerSocket implements java.io.Closeable { * @since 1.4 */ public boolean isBound() { + if (WispEngine.transparentWispSwitch()) + return asyncImpl.isBound(); // Before 1.3 ServerSockets were always bound during creation return bound || oldImpl; } @@ -640,6 +697,8 @@ class ServerSocket implements java.io.Closeable { * @since 1.4 */ public boolean isClosed() { + if (WispEngine.transparentWispSwitch()) + return asyncImpl.isClosed(); synchronized(closeLock) { return closed; } @@ -662,6 +721,10 @@ class ServerSocket implements java.io.Closeable { * @see #getSoTimeout() */ public synchronized void setSoTimeout(int timeout) throws SocketException { + if (WispEngine.transparentWispSwitch()) { + asyncImpl.setSoTimeout(timeout); + return; + } if (isClosed()) throw new SocketException("Socket is closed"); getImpl().setOption(SocketOptions.SO_TIMEOUT, new Integer(timeout)); @@ -676,6 +739,8 @@ class ServerSocket implements java.io.Closeable { * @see #setSoTimeout(int) */ public synchronized int getSoTimeout() throws IOException { + if (WispEngine.transparentWispSwitch()) + return asyncImpl.getSoTimeout(); if (isClosed()) throw new SocketException("Socket is closed"); Object o = getImpl().getOption(SocketOptions.SO_TIMEOUT); @@ -724,6 +789,10 @@ class ServerSocket implements java.io.Closeable { * @see #isClosed() */ public void setReuseAddress(boolean on) throws SocketException { + if (WispEngine.transparentWispSwitch()) { + asyncImpl.setReuseAddress(on); + return; + } if (isClosed()) throw new SocketException("Socket is closed"); getImpl().setOption(SocketOptions.SO_REUSEADDR, Boolean.valueOf(on)); @@ -740,6 +809,9 @@ class ServerSocket implements java.io.Closeable { * @see #setReuseAddress(boolean) */ public boolean getReuseAddress() throws SocketException { + if (WispEngine.transparentWispSwitch()) { + return asyncImpl.getReuseAddress(); + } if (isClosed()) throw new SocketException("Socket is closed"); return ((Boolean) (getImpl().getOption(SocketOptions.SO_REUSEADDR))).booleanValue(); @@ -759,6 +831,8 @@ class ServerSocket implements java.io.Closeable { * @return a string representation of this socket. */ public String toString() { + if (WispEngine.transparentWispSwitch()) + return asyncImpl.toString(); if (!isBound()) return "ServerSocket[unbound]"; InetAddress in; @@ -771,10 +845,14 @@ class ServerSocket implements java.io.Closeable { } void setBound() { + if (WispEngine.transparentWispSwitch()) + throw new UnsupportedOperationException(); bound = true; } void setCreated() { + if (WispEngine.transparentWispSwitch()) + throw new UnsupportedOperationException(); created = true; } @@ -809,6 +887,8 @@ class ServerSocket implements java.io.Closeable { * @see SecurityManager#checkSetFactory */ public static synchronized void setSocketFactory(SocketImplFactory fac) throws IOException { + if (WispEngine.transparentWispSwitch()) + throw new UnsupportedOperationException(); if (factory != null) { throw new SocketException("factory already defined"); } @@ -856,6 +936,10 @@ class ServerSocket implements java.io.Closeable { * @see #getReceiveBufferSize */ public synchronized void setReceiveBufferSize (int size) throws SocketException { + if (WispEngine.transparentWispSwitch()) { + asyncImpl.setReceiveBufferSize(size); + return; + } if (!(size > 0)) { throw new IllegalArgumentException("negative receive size"); } @@ -880,6 +964,9 @@ class ServerSocket implements java.io.Closeable { */ public synchronized int getReceiveBufferSize() throws SocketException{ + if (WispEngine.transparentWispSwitch()) + asyncImpl.getReceiveBufferSize(); + if (isClosed()) throw new SocketException("Socket is closed"); int result = 0; diff --git a/src/share/classes/java/net/Socket.java b/src/share/classes/java/net/Socket.java index eed64378b532a375a4a15a6fe941014b3c3a1992..e8e530f85bc3903322bbd859a1c83c1b36bb0cad 100644 --- a/src/share/classes/java/net/Socket.java +++ b/src/share/classes/java/net/Socket.java @@ -26,6 +26,8 @@ package java.net; import sun.security.util.SecurityConstants; +import com.alibaba.wisp.engine.WispEngine; +import sun.nio.ch.WispSocketImpl; import java.io.InputStream; import java.io.OutputStream; @@ -54,6 +56,9 @@ import java.security.PrivilegedAction; */ public class Socket implements java.io.Closeable { + + private WispSocketImpl asyncImpl; + /** * Various states of this socket. */ @@ -83,9 +88,19 @@ class Socket implements java.io.Closeable { * @revised 1.4 */ public Socket() { + if (WispEngine.transparentWispSwitch()) { + asyncImpl = new WispSocketImpl(this); + return; + } setImpl(); } + public Socket(SocketChannel sc) { + if (!WispEngine.transparentWispSwitch()) + throw new UnsupportedOperationException(); + asyncImpl = new WispSocketImpl(sc, this); + } + /** * Creates an unconnected socket, specifying the type of proxy, if any, * that should be used regardless of any other settings. @@ -115,6 +130,9 @@ class Socket implements java.io.Closeable { * @since 1.5 */ public Socket(Proxy proxy) { + if (WispEngine.transparentWispSwitch()) + throw new UnsupportedOperationException(); + // Create a copy of Proxy as a security measure if (proxy == null) { throw new IllegalArgumentException("Invalid Proxy"); @@ -171,6 +189,8 @@ class Socket implements java.io.Closeable { checkPermission(impl); this.impl = impl; if (impl != null) { + if (WispEngine.transparentWispSwitch()) + throw new UnsupportedOperationException(); checkOldImpl(); this.impl.setSocket(this); } @@ -439,14 +459,22 @@ class Socket implements java.io.Closeable { private Socket(SocketAddress address, SocketAddress localAddr, boolean stream) throws IOException { - setImpl(); + if (WispEngine.transparentWispSwitch()) { + if (!stream) + throw new UnsupportedOperationException(); + asyncImpl = new WispSocketImpl(this); + } else { + setImpl(); + } + // backward compatibility if (address == null) throw new NullPointerException(); try { - createImpl(stream); + if (!WispEngine.transparentWispSwitch()) + createImpl(stream); if (localAddr != null) bind(localAddr); connect(address); @@ -469,6 +497,9 @@ class Socket implements java.io.Closeable { * @since 1.4 */ void createImpl(boolean stream) throws SocketException { + if (WispEngine.transparentWispSwitch()) + throw new UnsupportedOperationException(); + if (impl == null) setImpl(); try { @@ -480,6 +511,9 @@ class Socket implements java.io.Closeable { } private void checkOldImpl() { + if (WispEngine.transparentWispSwitch()) + throw new UnsupportedOperationException(); + if (impl == null) return; // SocketImpl.connect() is a protected method, therefore we need to use @@ -512,6 +546,9 @@ class Socket implements java.io.Closeable { * @since 1.4 */ void setImpl() { + if (WispEngine.transparentWispSwitch()) + throw new UnsupportedOperationException(); + if (factory != null) { impl = factory.createSocketImpl(); checkOldImpl(); @@ -534,6 +571,9 @@ class Socket implements java.io.Closeable { * @since 1.4 */ SocketImpl getImpl() throws SocketException { + if (WispEngine.transparentWispSwitch()) + throw new UnsupportedOperationException(); + if (!created) createImpl(true); return impl; @@ -553,6 +593,10 @@ class Socket implements java.io.Closeable { * @spec JSR-51 */ public void connect(SocketAddress endpoint) throws IOException { + if (WispEngine.transparentWispSwitch()) { + asyncImpl.connect(endpoint); + return; + } connect(endpoint, 0); } @@ -589,6 +633,11 @@ class Socket implements java.io.Closeable { if (!(endpoint instanceof InetSocketAddress)) throw new IllegalArgumentException("Unsupported address type"); + if (WispEngine.transparentWispSwitch()) { + asyncImpl.connect(endpoint, timeout); + return; + } + InetSocketAddress epoint = (InetSocketAddress) endpoint; InetAddress addr = epoint.getAddress (); int port = epoint.getPort(); @@ -639,6 +688,11 @@ class Socket implements java.io.Closeable { * @see #isBound */ public void bind(SocketAddress bindpoint) throws IOException { + if (WispEngine.transparentWispSwitch()) { + asyncImpl.bind(bindpoint); + return; + } + if (isClosed()) throw new SocketException("Socket is closed"); if (!oldImpl && isBound()) @@ -664,6 +718,9 @@ class Socket implements java.io.Closeable { } private void checkAddress (InetAddress addr, String op) { + if (WispEngine.transparentWispSwitch()) { + throw new UnsupportedOperationException(); + } if (addr == null) { return; } @@ -676,20 +733,29 @@ class Socket implements java.io.Closeable { * set the flags after an accept() call. */ final void postAccept() { + if (WispEngine.transparentWispSwitch()) + throw new UnsupportedOperationException(); + connected = true; created = true; bound = true; } void setCreated() { + if (WispEngine.transparentWispSwitch()) + throw new UnsupportedOperationException(); created = true; } void setBound() { + if (WispEngine.transparentWispSwitch()) + throw new UnsupportedOperationException(); bound = true; } void setConnected() { + if (WispEngine.transparentWispSwitch()) + throw new UnsupportedOperationException(); connected = true; } @@ -704,6 +770,9 @@ class Socket implements java.io.Closeable { * or {@code null} if the socket is not connected. */ public InetAddress getInetAddress() { + if (WispEngine.transparentWispSwitch()) + return asyncImpl.getInetAddress(); + if (!isConnected()) return null; try { @@ -729,6 +798,9 @@ class Socket implements java.io.Closeable { * @see SecurityManager#checkConnect */ public InetAddress getLocalAddress() { + if (WispEngine.transparentWispSwitch()) + return asyncImpl.getLocalAddress(); + // This is for backward compatibility if (!isBound()) return InetAddress.anyLocalAddress(); @@ -760,6 +832,9 @@ class Socket implements java.io.Closeable { * 0 if the socket is not connected yet. */ public int getPort() { + if (WispEngine.transparentWispSwitch()) + return asyncImpl.getPort(); + if (!isConnected()) return 0; try { @@ -781,6 +856,9 @@ class Socket implements java.io.Closeable { * if the socket is not bound yet. */ public int getLocalPort() { + if (WispEngine.transparentWispSwitch()) + return asyncImpl.getLocalPort(); + if (!isBound()) return -1; try { @@ -868,6 +946,9 @@ class Socket implements java.io.Closeable { * @spec JSR-51 */ public SocketChannel getChannel() { + if (WispEngine.transparentWispSwitch()) + return asyncImpl.getChannel(); + return null; } @@ -917,6 +998,9 @@ class Socket implements java.io.Closeable { * @spec JSR-51 */ public InputStream getInputStream() throws IOException { + if (WispEngine.transparentWispSwitch()) + return asyncImpl.getInputStream(); + if (isClosed()) throw new SocketException("Socket is closed"); if (!isConnected()) @@ -957,6 +1041,9 @@ class Socket implements java.io.Closeable { * @spec JSR-51 */ public OutputStream getOutputStream() throws IOException { + if (WispEngine.transparentWispSwitch()) + return asyncImpl.getOutputStream(); + if (isClosed()) throw new SocketException("Socket is closed"); if (!isConnected()) @@ -993,6 +1080,10 @@ class Socket implements java.io.Closeable { * @see #getTcpNoDelay() */ public void setTcpNoDelay(boolean on) throws SocketException { + if (WispEngine.transparentWispSwitch()) { + asyncImpl.setTcpNoDelay(on); + return; + } if (isClosed()) throw new SocketException("Socket is closed"); getImpl().setOption(SocketOptions.TCP_NODELAY, Boolean.valueOf(on)); @@ -1009,6 +1100,9 @@ class Socket implements java.io.Closeable { * @see #setTcpNoDelay(boolean) */ public boolean getTcpNoDelay() throws SocketException { + if (WispEngine.transparentWispSwitch()) + return asyncImpl.getTcpNoDelay(); + if (isClosed()) throw new SocketException("Socket is closed"); return ((Boolean) getImpl().getOption(SocketOptions.TCP_NODELAY)).booleanValue(); @@ -1030,6 +1124,10 @@ class Socket implements java.io.Closeable { * @see #getSoLinger() */ public void setSoLinger(boolean on, int linger) throws SocketException { + if (WispEngine.transparentWispSwitch()) { + asyncImpl.setSoLinger(on, linger); + return; + } if (isClosed()) throw new SocketException("Socket is closed"); if (!on) { @@ -1058,6 +1156,9 @@ class Socket implements java.io.Closeable { * @see #setSoLinger(boolean, int) */ public int getSoLinger() throws SocketException { + if (WispEngine.transparentWispSwitch()) + return asyncImpl.getSoLinger(); + if (isClosed()) throw new SocketException("Socket is closed"); Object o = getImpl().getOption(SocketOptions.SO_LINGER); @@ -1079,6 +1180,10 @@ class Socket implements java.io.Closeable { * @since 1.4 */ public void sendUrgentData (int data) throws IOException { + if (WispEngine.transparentWispSwitch()) { + asyncImpl.sendUrgentData(data); + return; + } if (!getImpl().supportsUrgentData ()) { throw new SocketException ("Urgent data not supported"); } @@ -1111,6 +1216,10 @@ class Socket implements java.io.Closeable { * @see #getOOBInline() */ public void setOOBInline(boolean on) throws SocketException { + if (WispEngine.transparentWispSwitch()) { + asyncImpl.setOOBInline(on); + return; + } if (isClosed()) throw new SocketException("Socket is closed"); getImpl().setOption(SocketOptions.SO_OOBINLINE, Boolean.valueOf(on)); @@ -1128,6 +1237,9 @@ class Socket implements java.io.Closeable { * @see #setOOBInline(boolean) */ public boolean getOOBInline() throws SocketException { + if (WispEngine.transparentWispSwitch()) + return asyncImpl.getOOBInline(); + if (isClosed()) throw new SocketException("Socket is closed"); return ((Boolean) getImpl().getOption(SocketOptions.SO_OOBINLINE)).booleanValue(); @@ -1153,6 +1265,10 @@ class Socket implements java.io.Closeable { public synchronized void setSoTimeout(int timeout) throws SocketException { if (isClosed()) throw new SocketException("Socket is closed"); + if (WispEngine.transparentWispSwitch()) { + asyncImpl.setSoTimeout(timeout); + return; + } if (timeout < 0) throw new IllegalArgumentException("timeout can't be negative"); @@ -1173,6 +1289,8 @@ class Socket implements java.io.Closeable { public synchronized int getSoTimeout() throws SocketException { if (isClosed()) throw new SocketException("Socket is closed"); + if (WispEngine.transparentWispSwitch()) + return asyncImpl.getSoTimeout(); Object o = getImpl().getOption(SocketOptions.SO_TIMEOUT); /* extra type safety */ if (o instanceof Integer) { @@ -1207,6 +1325,10 @@ class Socket implements java.io.Closeable { */ public synchronized void setSendBufferSize(int size) throws SocketException{ + if (WispEngine.transparentWispSwitch()) { + asyncImpl.setSendBufferSize(size); + return; + } if (!(size > 0)) { throw new IllegalArgumentException("negative send size"); } @@ -1229,6 +1351,9 @@ class Socket implements java.io.Closeable { * @since 1.2 */ public synchronized int getSendBufferSize() throws SocketException { + if (WispEngine.transparentWispSwitch()) + return asyncImpl.getSendBufferSize(); + if (isClosed()) throw new SocketException("Socket is closed"); int result = 0; @@ -1281,6 +1406,10 @@ class Socket implements java.io.Closeable { */ public synchronized void setReceiveBufferSize(int size) throws SocketException{ + if (WispEngine.transparentWispSwitch()) { + asyncImpl.setReceiveBufferSize(size); + return; + } if (size <= 0) { throw new IllegalArgumentException("invalid receive size"); } @@ -1303,6 +1432,9 @@ class Socket implements java.io.Closeable { */ public synchronized int getReceiveBufferSize() throws SocketException{ + if (WispEngine.transparentWispSwitch()) + return asyncImpl.getReceiveBufferSize(); + if (isClosed()) throw new SocketException("Socket is closed"); int result = 0; @@ -1323,6 +1455,10 @@ class Socket implements java.io.Closeable { * @see #getKeepAlive() */ public void setKeepAlive(boolean on) throws SocketException { + if (WispEngine.transparentWispSwitch()) { + asyncImpl.setKeepAlive(on); + return; + } if (isClosed()) throw new SocketException("Socket is closed"); getImpl().setOption(SocketOptions.SO_KEEPALIVE, Boolean.valueOf(on)); @@ -1339,6 +1475,9 @@ class Socket implements java.io.Closeable { * @see #setKeepAlive(boolean) */ public boolean getKeepAlive() throws SocketException { + if (WispEngine.transparentWispSwitch()) + return asyncImpl.getKeepAlive(); + if (isClosed()) throw new SocketException("Socket is closed"); return ((Boolean) getImpl().getOption(SocketOptions.SO_KEEPALIVE)).booleanValue(); @@ -1391,6 +1530,10 @@ class Socket implements java.io.Closeable { * @see SocketOptions#IP_TOS */ public void setTrafficClass(int tc) throws SocketException { + if (WispEngine.transparentWispSwitch()) { + asyncImpl.setTrafficClass(tc); + return; + } if (tc < 0 || tc > 255) throw new IllegalArgumentException("tc is not in range 0 -- 255"); @@ -1423,6 +1566,9 @@ class Socket implements java.io.Closeable { * @see SocketOptions#IP_TOS */ public int getTrafficClass() throws SocketException { + if (WispEngine.transparentWispSwitch()) + return asyncImpl.getTrafficClass(); + return ((Integer) (getImpl().getOption(SocketOptions.IP_TOS))).intValue(); } @@ -1462,6 +1608,10 @@ class Socket implements java.io.Closeable { * @see #isBound() */ public void setReuseAddress(boolean on) throws SocketException { + if (WispEngine.transparentWispSwitch()) { + asyncImpl.setReuseAddress(on); + return; + } if (isClosed()) throw new SocketException("Socket is closed"); getImpl().setOption(SocketOptions.SO_REUSEADDR, Boolean.valueOf(on)); @@ -1478,6 +1628,9 @@ class Socket implements java.io.Closeable { * @see #setReuseAddress(boolean) */ public boolean getReuseAddress() throws SocketException { + if (WispEngine.transparentWispSwitch()) + return asyncImpl.getReuseAddress(); + if (isClosed()) throw new SocketException("Socket is closed"); return ((Boolean) (getImpl().getOption(SocketOptions.SO_REUSEADDR))).booleanValue(); @@ -1506,6 +1659,10 @@ class Socket implements java.io.Closeable { * @see #isClosed */ public synchronized void close() throws IOException { + if (WispEngine.transparentWispSwitch()) { + asyncImpl.close(); + return; + } synchronized(closeLock) { if (isClosed()) return; @@ -1535,6 +1692,10 @@ class Socket implements java.io.Closeable { */ public void shutdownInput() throws IOException { + if (WispEngine.transparentWispSwitch()) { + asyncImpl.shutdownInput(); + return; + } if (isClosed()) throw new SocketException("Socket is closed"); if (!isConnected()) @@ -1565,6 +1726,10 @@ class Socket implements java.io.Closeable { */ public void shutdownOutput() throws IOException { + if (WispEngine.transparentWispSwitch()) { + asyncImpl.shutdownOutput(); + return; + } if (isClosed()) throw new SocketException("Socket is closed"); if (!isConnected()) @@ -1581,6 +1746,9 @@ class Socket implements java.io.Closeable { * @return a string representation of this socket. */ public String toString() { + if (WispEngine.transparentWispSwitch()) + return asyncImpl.toString(); + try { if (isConnected()) return "Socket[addr=" + getImpl().getInetAddress() + @@ -1603,6 +1771,9 @@ class Socket implements java.io.Closeable { * @since 1.4 */ public boolean isConnected() { + if (WispEngine.transparentWispSwitch()) + return asyncImpl.isConnected(); + // Before 1.3 Sockets were always connected during creation return connected || oldImpl; } @@ -1620,6 +1791,9 @@ class Socket implements java.io.Closeable { * @see #bind */ public boolean isBound() { + if (WispEngine.transparentWispSwitch()) + return asyncImpl.isBound(); + // Before 1.3 Sockets were always bound during creation return bound || oldImpl; } @@ -1632,6 +1806,9 @@ class Socket implements java.io.Closeable { * @see #close */ public boolean isClosed() { + if (WispEngine.transparentWispSwitch()) + return asyncImpl.isClosed(); + synchronized(closeLock) { return closed; } @@ -1645,6 +1822,9 @@ class Socket implements java.io.Closeable { * @see #shutdownInput */ public boolean isInputShutdown() { + if (WispEngine.transparentWispSwitch()) + return asyncImpl.isInputShutdown(); + return shutIn; } @@ -1656,6 +1836,9 @@ class Socket implements java.io.Closeable { * @see #shutdownOutput */ public boolean isOutputShutdown() { + if (WispEngine.transparentWispSwitch()) + return asyncImpl.isOutputShutdown(); + return shutOut; } @@ -1691,6 +1874,9 @@ class Socket implements java.io.Closeable { public static synchronized void setSocketImplFactory(SocketImplFactory fac) throws IOException { + if (WispEngine.transparentWispSwitch()) + throw new UnsupportedOperationException(); + if (factory != null) { throw new SocketException("factory already defined"); } diff --git a/src/share/classes/java/nio/channels/Selector.java b/src/share/classes/java/nio/channels/Selector.java index ea72acb6400b7e748ffc6db78f75ac645d2f9073..620a46c8711c0e523c77f8d93a2de30d4657f5cf 100644 --- a/src/share/classes/java/nio/channels/Selector.java +++ b/src/share/classes/java/nio/channels/Selector.java @@ -25,6 +25,8 @@ package java.nio.channels; +import com.alibaba.wisp.engine.WispEngine; + import java.io.Closeable; import java.io.IOException; import java.nio.channels.spi.SelectorProvider; diff --git a/src/share/classes/java/util/concurrent/LinkedBlockingDeque.java b/src/share/classes/java/util/concurrent/LinkedBlockingDeque.java index 73d3da049ec22c69a41fc5cd3a379c541a72f65a..080517d9bc8fd75dc5b8df8a5973c1f028649eae 100644 --- a/src/share/classes/java/util/concurrent/LinkedBlockingDeque.java +++ b/src/share/classes/java/util/concurrent/LinkedBlockingDeque.java @@ -154,6 +154,10 @@ public class LinkedBlockingDeque /** Maximum number of items in the deque */ private final int capacity; + int getCapacity() { + return capacity; + } + /** Main lock guarding all access */ final ReentrantLock lock = new ReentrantLock(); diff --git a/src/share/classes/java/util/concurrent/LinkedBlockingQueue.java b/src/share/classes/java/util/concurrent/LinkedBlockingQueue.java index d7a1587146af4da123ea5dc5b995c9d0b7b225ec..281ead2ec9e82e9b02809f2f5e13702d7cc107ee 100644 --- a/src/share/classes/java/util/concurrent/LinkedBlockingQueue.java +++ b/src/share/classes/java/util/concurrent/LinkedBlockingQueue.java @@ -136,6 +136,10 @@ public class LinkedBlockingQueue extends AbstractQueue /** The capacity bound, or Integer.MAX_VALUE if none */ private final int capacity; + int getCapacity() { + return capacity; + } + /** Current number of elements */ private final AtomicInteger count = new AtomicInteger(); diff --git a/src/share/classes/java/util/concurrent/SynchronousQueue.java b/src/share/classes/java/util/concurrent/SynchronousQueue.java index fdc42f926eece574cc28b2126ae9b53426f37c1a..bad8e59869a11e461d95fd781161c02e407deee5 100644 --- a/src/share/classes/java/util/concurrent/SynchronousQueue.java +++ b/src/share/classes/java/util/concurrent/SynchronousQueue.java @@ -35,6 +35,10 @@ */ package java.util.concurrent; +import com.alibaba.wisp.engine.WispEngine; +import sun.misc.SharedSecrets; +import sun.misc.WispEngineAccess; + import java.util.concurrent.locks.LockSupport; import java.util.concurrent.locks.ReentrantLock; import java.util.*; @@ -85,6 +89,8 @@ public class SynchronousQueue extends AbstractQueue implements BlockingQueue, java.io.Serializable { private static final long serialVersionUID = -3223113410248163686L; + private static WispEngineAccess WEA = SharedSecrets.getWispEngineAccess(); + /* * This class implements extensions of the dual stack and dual * queue algorithms described in "Nonblocking Concurrent Objects @@ -456,7 +462,8 @@ public class SynchronousQueue extends AbstractQueue s.waiter = w; // establish waiter so can park next iter else if (!timed) LockSupport.park(this); - else if (nanos > spinForTimeoutThreshold) + else if (nanos > spinForTimeoutThreshold || + WispEngine.transparentWispSwitch() && WEA.hasMoreTasks()) LockSupport.parkNanos(this, nanos); } } @@ -466,6 +473,9 @@ public class SynchronousQueue extends AbstractQueue * fulfiller. */ boolean shouldSpin(SNode s) { + if (WispEngine.transparentWispSwitch() && WEA.hasMoreTasks()) { + return false; + } SNode h = head; return (h == s || h == null || isFulfilling(h.mode)); } @@ -760,7 +770,8 @@ public class SynchronousQueue extends AbstractQueue s.waiter = w; else if (!timed) LockSupport.park(this); - else if (nanos > spinForTimeoutThreshold) + else if (nanos > spinForTimeoutThreshold || + WispEngine.transparentWispSwitch() && WEA.hasMoreTasks()) LockSupport.parkNanos(this, nanos); } } diff --git a/src/share/classes/java/util/concurrent/locks/AbstractQueuedSynchronizer.java b/src/share/classes/java/util/concurrent/locks/AbstractQueuedSynchronizer.java index dce35765df71e053f579f7d87f9b9f61cff42aef..db2c8b1b707fd6c4e0fadab46c99e80b90544157 100644 --- a/src/share/classes/java/util/concurrent/locks/AbstractQueuedSynchronizer.java +++ b/src/share/classes/java/util/concurrent/locks/AbstractQueuedSynchronizer.java @@ -38,7 +38,11 @@ import java.util.concurrent.TimeUnit; import java.util.ArrayList; import java.util.Collection; import java.util.Date; + +import com.alibaba.wisp.engine.WispEngine; +import sun.misc.SharedSecrets; import sun.misc.Unsafe; +import sun.misc.WispEngineAccess; /** * Provides a framework for implementing blocking locks and related @@ -292,6 +296,8 @@ public abstract class AbstractQueuedSynchronizer private static final long serialVersionUID = 7373984972572414691L; + private static WispEngineAccess WA = SharedSecrets.getWispEngineAccess(); + /** * Creates a new {@code AbstractQueuedSynchronizer} instance * with initial synchronization state of zero. @@ -930,7 +936,8 @@ public abstract class AbstractQueuedSynchronizer if (nanosTimeout <= 0L) return false; if (shouldParkAfterFailedAcquire(p, node) && - nanosTimeout > spinForTimeoutThreshold) + (nanosTimeout > spinForTimeoutThreshold || + WispEngine.transparentWispSwitch() && WA.hasMoreTasks())) LockSupport.parkNanos(this, nanosTimeout); if (Thread.interrupted()) throw new InterruptedException(); @@ -1033,7 +1040,8 @@ public abstract class AbstractQueuedSynchronizer if (nanosTimeout <= 0L) return false; if (shouldParkAfterFailedAcquire(p, node) && - nanosTimeout > spinForTimeoutThreshold) + (nanosTimeout > spinForTimeoutThreshold || + WispEngine.transparentWispSwitch() && WA.hasMoreTasks())) LockSupport.parkNanos(this, nanosTimeout); if (Thread.interrupted()) throw new InterruptedException(); @@ -2074,7 +2082,8 @@ public abstract class AbstractQueuedSynchronizer transferAfterCancelledWait(node); break; } - if (nanosTimeout >= spinForTimeoutThreshold) + if (nanosTimeout >= spinForTimeoutThreshold || + WispEngine.transparentWispSwitch() && WA.hasMoreTasks()) LockSupport.parkNanos(this, nanosTimeout); if ((interruptMode = checkInterruptWhileWaiting(node)) != 0) break; @@ -2159,7 +2168,8 @@ public abstract class AbstractQueuedSynchronizer timedout = transferAfterCancelledWait(node); break; } - if (nanosTimeout >= spinForTimeoutThreshold) + if (nanosTimeout >= spinForTimeoutThreshold || + WispEngine.transparentWispSwitch() && WA.hasMoreTasks()) LockSupport.parkNanos(this, nanosTimeout); if ((interruptMode = checkInterruptWhileWaiting(node)) != 0) break; diff --git a/src/share/classes/java/util/concurrent/locks/LockSupport.java b/src/share/classes/java/util/concurrent/locks/LockSupport.java index 46a0ab597c87d17ace150c256bcdc58f1e6e3051..17f0409f2ffe3ac1213fd516521697a4c324e74b 100644 --- a/src/share/classes/java/util/concurrent/locks/LockSupport.java +++ b/src/share/classes/java/util/concurrent/locks/LockSupport.java @@ -34,6 +34,7 @@ */ package java.util.concurrent.locks; + import sun.misc.Unsafe; /** diff --git a/src/share/classes/sun/management/ManagementFactoryHelper.java b/src/share/classes/sun/management/ManagementFactoryHelper.java index 30dcc41ccc103349b0beee570bf546b87aeb5b0d..40a0d6325797a9001c4570ae1eb4f9d3cdca7a16 100644 --- a/src/share/classes/sun/management/ManagementFactoryHelper.java +++ b/src/share/classes/sun/management/ManagementFactoryHelper.java @@ -41,8 +41,10 @@ import java.security.PrivilegedExceptionAction; import com.alibaba.management.TenantContainerMXBean; import com.alibaba.management.ElasticHeapMXBean; +import com.alibaba.management.WispCounterMXBean; import com.alibaba.tenant.TenantContainerMXBeanImpl; import com.alibaba.jvm.gc.ElasticHeapMXBeanImpl; +import com.alibaba.wisp.engine.WispCounterMXBeanImpl; import sun.util.logging.LoggingSupport; import java.util.ArrayList; @@ -71,6 +73,7 @@ public class ManagementFactoryHelper { private static OperatingSystemImpl osMBean = null; private static TenantContainerMXBeanImpl tenantContainerMBean = null; private static ElasticHeapMXBeanImpl elasticHeapMXBean = null; + private static WispCounterMXBeanImpl wispCounterMBean = null; public static synchronized ClassLoadingMXBean getClassLoadingMXBean() { if (classMBean == null) { @@ -128,6 +131,13 @@ public class ManagementFactoryHelper { return elasticHeapMXBean; } + public static synchronized WispCounterMXBean getWispCounterMXBean() { + if (wispCounterMBean == null) { + wispCounterMBean = new WispCounterMXBeanImpl(); + } + return wispCounterMBean; + } + public static List getMemoryPoolMXBeans() { MemoryPoolMXBean[] pools = MemoryImpl.getMemoryPools(); List list = new ArrayList<>(pools.length); diff --git a/src/share/classes/sun/misc/JavaLangAccess.java b/src/share/classes/sun/misc/JavaLangAccess.java index 04494e668f89c6eddd084b2c5c831a222eea48a2..8f26f843d12e85a0d62c6b6a18f67e5146055cbe 100644 --- a/src/share/classes/sun/misc/JavaLangAccess.java +++ b/src/share/classes/sun/misc/JavaLangAccess.java @@ -31,6 +31,8 @@ import java.security.AccessControlContext; import java.util.Map; import com.alibaba.rcm.internal.AbstractResourceContainer; +import com.alibaba.wisp.engine.WispEngine; +import com.alibaba.wisp.engine.WispTask; import sun.reflect.ConstantPool; import sun.reflect.annotation.AnnotationType; import sun.nio.ch.Interruptible; @@ -150,4 +152,23 @@ public interface JavaLangAccess { * Get the reference to the thread's inherited {@code ResourceContainer} */ AbstractResourceContainer getInheritedResourceContainer(Thread thread); + + /** + * Returns a reference to the currently executing thread object. + */ + Thread currentThread0(); + + void yield0(); + + void setWispTask(Thread thread, WispTask task); + + WispTask getWispTask(Thread thread); + + void setWispAlive(Thread thread, boolean b); + + boolean isInSameNative(Thread thread); + + void threadExit(Thread thread); + + void wispBooted(); } diff --git a/src/share/classes/sun/misc/SharedSecrets.java b/src/share/classes/sun/misc/SharedSecrets.java index e266fbf3e7b1be8c4e172617ea862792811cdc71..2afc945ef1aabed62a8be06eac0d8a942884a200 100644 --- a/src/share/classes/sun/misc/SharedSecrets.java +++ b/src/share/classes/sun/misc/SharedSecrets.java @@ -26,6 +26,9 @@ package sun.misc; import javax.crypto.SealedObject; + +import sun.nio.ch.IOEventAccess; + import java.util.jar.JarFile; import java.io.Console; import java.io.FileDescriptor; @@ -64,6 +67,8 @@ public class SharedSecrets { private static JavaObjectInputStreamAccess javaObjectInputStreamAccess; private static TenantAccess tenantAccess; private static JavaSecuritySignatureAccess javaSecuritySignatureAccess; + private static WispEngineAccess wispEngineAccess; + private static IOEventAccess ioEventAccess; public static JavaUtilJarAccess javaUtilJarAccess() { if (javaUtilJarAccess == null) { @@ -257,4 +262,27 @@ public class SharedSecrets { public static TenantAccess getTenantAccess() { return tenantAccess; } + + public static WispEngineAccess getWispEngineAccess() { + return wispEngineAccess; + } + + public static void setWispEngineAccess(WispEngineAccess wispEngineAccess) { + SharedSecrets.wispEngineAccess = wispEngineAccess; + } + + public static UnsafeAccess getUnsafeAccess() { + return Unsafe.access; + } + + public static IOEventAccess getIOEventAccess() { + if (ioEventAccess == null) { + IOEventAccess.initializeEvent(); + } + return ioEventAccess; + } + + public static void setIOEventAccess(IOEventAccess ioEventAccess) { + SharedSecrets.ioEventAccess = ioEventAccess; + } } diff --git a/src/share/classes/sun/misc/Unsafe.java b/src/share/classes/sun/misc/Unsafe.java index 99e4658029c3d23c0d38bb7248ad55d5539d3da5..fd6bc7f43c66742e241ac2dfbbc5f069494d8afe 100644 --- a/src/share/classes/sun/misc/Unsafe.java +++ b/src/share/classes/sun/misc/Unsafe.java @@ -28,6 +28,7 @@ package sun.misc; import java.security.*; import java.lang.reflect.*; +import com.alibaba.wisp.engine.WispEngine; import sun.reflect.CallerSensitive; import sun.reflect.Reflection; @@ -53,6 +54,18 @@ public final class Unsafe { private static final Unsafe theUnsafe = new Unsafe(); + static final UnsafeAccess access = new UnsafeAccess() { + @Override + public void unpark0(Object thread) { + theUnsafe.unpark0(thread); + } + + @Override + public void park0(boolean isAbsolute, long time) { + theUnsafe.park0(isAbsolute, time); + } + }; + /** * Provides the caller with the capability of performing unsafe * operations. @@ -971,7 +984,7 @@ public final class Unsafe { public native void putOrderedLong(Object o, long offset, long x); /** - * Unblock the given thread blocked on park, or, if it is + * Unblock the given thread(or coroutine) blocked on park, or, if it is * not blocked, cause the subsequent call to park not to * block. Note: this operation is "unsafe" solely because the * caller must somehow ensure that the thread has not been @@ -982,10 +995,25 @@ public final class Unsafe { * @param thread the thread to unpark. * */ - public native void unpark(Object thread); + public void unpark(Object thread) { + if (WispEngine.transparentWispSwitch()) { + if (thread instanceof Thread) { + SharedSecrets.getWispEngineAccess().unpark( + SharedSecrets.getJavaLangAccess().getWispTask((Thread) thread)); + } + return; + } + unpark0(thread); + } /** - * Block current thread, returning when a balancing + * Unblock the given thread. Always use the thread semantic. + * @param thread + */ + private native void unpark0(Object thread); + + /** + * Block current thread(or coroutine), returning when a balancing * unpark occurs, or a balancing unpark has * already occurred, or the thread is interrupted, or, if not * absolute and time is not zero, the given time nanoseconds have @@ -995,7 +1023,25 @@ public final class Unsafe { * because unpark is, so it would be strange to place it * elsewhere. */ - public native void park(boolean isAbsolute, long time); + public void park(boolean isAbsolute, long time) { + if (WispEngine.transparentWispSwitch()) { + if (time <= 0) { // non-timeouted park + SharedSecrets.getWispEngineAccess().park(0); + } + long timeout = !isAbsolute ? time: + java.util.concurrent.TimeUnit.MILLISECONDS.toNanos(time - System.currentTimeMillis()); + if (timeout > 0) { + SharedSecrets.getWispEngineAccess().park(timeout); + } + return; + } + park0(isAbsolute, time); + } + + /** + * Block current thread. Always use the thread semantic. + */ + private native void park0(boolean isAbsolute, long time); /** * Gets the load average in the system run queue assigned diff --git a/src/share/classes/sun/misc/UnsafeAccess.java b/src/share/classes/sun/misc/UnsafeAccess.java new file mode 100644 index 0000000000000000000000000000000000000000..ca66a18b60671bf6ec41556b91c13784a3872761 --- /dev/null +++ b/src/share/classes/sun/misc/UnsafeAccess.java @@ -0,0 +1,7 @@ +package sun.misc; + +public interface UnsafeAccess { + void unpark0(Object thread); + + void park0(boolean isAbsolute, long time); +} diff --git a/src/share/classes/sun/misc/VM.java b/src/share/classes/sun/misc/VM.java index 2e2c1cc492f86cdfbef90fa545c8f970fe98c2f1..5b5fbd98d763094a995e6519fc05b644739fbf84 100644 --- a/src/share/classes/sun/misc/VM.java +++ b/src/share/classes/sun/misc/VM.java @@ -410,6 +410,7 @@ public class VM { private final static int JVMTI_THREAD_STATE_WAITING_INDEFINITELY = 0x0010; private final static int JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT = 0x0020; + /* * Returns first non-privileged class loader on the stack (excluding * reflection generated frames) or the extension class loader if only diff --git a/src/share/classes/sun/misc/WispEngineAccess.java b/src/share/classes/sun/misc/WispEngineAccess.java new file mode 100644 index 0000000000000000000000000000000000000000..41fbb5ac46d35d40b022d41cf52022d50525b3e2 --- /dev/null +++ b/src/share/classes/sun/misc/WispEngineAccess.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package sun.misc; + + +import com.alibaba.wisp.engine.WispTask; + +import java.io.IOException; +import java.nio.channels.SelectableChannel; +import java.util.concurrent.atomic.AtomicReference; + +public interface WispEngineAccess { + + WispTask getCurrentTask(); + + void registerEvent(SelectableChannel ch, int events) throws IOException; + + void unregisterEvent(); + + int epollWait(int epfd, long pollArray, int arraySize, long timeout, + AtomicReference status, final Object INTERRUPTED) throws IOException; + + void interruptEpoll(AtomicReference status, Object INTERRUPTED, int interruptFD); + + void addTimer(long deadlineNano); + + void cancelTimer(); + + void sleep(long ms); + + void yield(); + + boolean isThreadTask(WispTask task); + + boolean isTimeout(); + + void park(long timeoutNanos); + + void unpark(WispTask task); + + void destroy(); + + boolean isAlive(WispTask task); + + void interrupt(WispTask task); + + boolean testInterruptedAndClear(WispTask task, boolean clear); + + boolean hasMoreTasks(); + + boolean runningAsCoroutine(Thread t); + + boolean usingWispEpoll(); + + boolean isAllThreadAsWisp(); + + boolean tryStartThreadAsWisp(Thread thread, Runnable target); + + boolean useDirectSelectorWakeup(); + + boolean enableSocketLock(); + + StackTraceElement[] getStackTrace(WispTask task); +} diff --git a/src/share/classes/sun/nio/ch/IOEventAccess.java b/src/share/classes/sun/nio/ch/IOEventAccess.java new file mode 100644 index 0000000000000000000000000000000000000000..6f7358c04670cbccc5c14537aa75de459c21b7b0 --- /dev/null +++ b/src/share/classes/sun/nio/ch/IOEventAccess.java @@ -0,0 +1,47 @@ +package sun.nio.ch; + +import sun.misc.Unsafe; + +import java.io.IOException; + +public interface IOEventAccess { + + int eventCtlAdd(); + + int eventCtlDel(); + + int eventCtlMod(); + + int eventOneShot(); + + int noEvent(); + + long allocatePollArray(int count); + + void freePollArray(long address); + + long getEvent(long address, int i); + + int getDescriptor(long eventAddress); + + int getEvents(long eventAddress); + + int eventCreate() throws IOException; + + int eventCtl(int epfd, int opcode, int fd, int events); + + int eventWait(int epfd, long pollAddress, int numfds, int timeout) throws IOException; + + void socketpair(int[] sv) throws IOException; + + void interrupt(int fd) throws IOException; + + void drain(int fd) throws IOException; + + void close(int fd); + + static void initializeEvent() { + Unsafe.getUnsafe().ensureClassInitialized(IOEvent.eventClass()); + } + +} \ No newline at end of file diff --git a/src/share/classes/sun/nio/ch/SelectionKeyImpl.java b/src/share/classes/sun/nio/ch/SelectionKeyImpl.java index 28102d78fcf07ac37df49b8fd39ee9fb446c3361..8e6c2e1caec18ca67eac1188b9495ce6ff93aeda 100644 --- a/src/share/classes/sun/nio/ch/SelectionKeyImpl.java +++ b/src/share/classes/sun/nio/ch/SelectionKeyImpl.java @@ -46,6 +46,7 @@ public class SelectionKeyImpl private volatile int interestOps; private int readyOps; + public int wispOps; // pass argument, access by WispSelector SelectionKeyImpl(SelChImpl ch, SelectorImpl sel) { channel = ch; @@ -102,6 +103,7 @@ public class SelectionKeyImpl public SelectionKey nioInterestOps(int ops) { if ((ops & ~channel().validOps()) != 0) throw new IllegalArgumentException(); + wispOps = ops; // read by WispSelector.putEventOps channel.translateAndSetInterestOps(ops, this); interestOps = ops; return this; diff --git a/src/share/classes/sun/nio/ch/SelectorImpl.java b/src/share/classes/sun/nio/ch/SelectorImpl.java index db8ce1fd0e1d0aadf479e5ce2fd0a8ea42e4cdea..1bb692f9f0749e47153dee35bead78e56f6cae7b 100644 --- a/src/share/classes/sun/nio/ch/SelectorImpl.java +++ b/src/share/classes/sun/nio/ch/SelectorImpl.java @@ -141,17 +141,28 @@ public abstract class SelectorImpl // Precondition: Synchronized on this, keys, and selectedKeys Set cks = cancelledKeys(); synchronized (cks) { + if (cks.isEmpty()) { + return; + } + cks = new HashSet<>(cks); + cancelledKeys().clear(); + } + try { // now cks is a thread local copy + Iterator i = cks.iterator(); + while (i.hasNext()) { + SelectionKeyImpl ski = (SelectionKeyImpl)i.next(); + try { + implDereg(ski); + } catch (SocketException se) { + throw new IOException("Error deregistering key", se); + } finally { + i.remove(); + } + } + } finally { if (!cks.isEmpty()) { - Iterator i = cks.iterator(); - while (i.hasNext()) { - SelectionKeyImpl ski = (SelectionKeyImpl)i.next(); - try { - implDereg(ski); - } catch (SocketException se) { - throw new IOException("Error deregistering key", se); - } finally { - i.remove(); - } + synchronized (cancelledKeys()) { + cancelledKeys().addAll(cks); } } } diff --git a/src/share/classes/sun/nio/ch/ServerSocketChannelImpl.java b/src/share/classes/sun/nio/ch/ServerSocketChannelImpl.java index 563f948cf924bc804e248c06a1182cb7b0f66138..292cbfb6db4153e2304b23b35fdca15b7deec6d6 100644 --- a/src/share/classes/sun/nio/ch/ServerSocketChannelImpl.java +++ b/src/share/classes/sun/nio/ch/ServerSocketChannelImpl.java @@ -31,6 +31,10 @@ import java.net.*; import java.nio.channels.*; import java.nio.channels.spi.*; import java.util.*; + +import com.alibaba.wisp.engine.WispEngine; +import sun.misc.SharedSecrets; +import sun.misc.WispEngineAccess; import sun.net.NetHooks; @@ -42,6 +46,7 @@ class ServerSocketChannelImpl extends ServerSocketChannel implements SelChImpl { + private static final WispEngineAccess WEA = SharedSecrets.getWispEngineAccess(); // Used to make native close and configure calls private static NativeDispatcher nd; @@ -238,18 +243,31 @@ class ServerSocketChannelImpl FileDescriptor newfd = new FileDescriptor(); InetSocketAddress[] isaa = new InetSocketAddress[1]; + final boolean wispAndBlocking = WispEngine.transparentWispSwitch() && isBlocking() && + WEA.usingWispEpoll(); try { begin(); if (!isOpen()) return null; thread = NativeThread.current(); + if (wispAndBlocking) { + IOUtil.configureBlocking(fd, false); + } for (;;) { n = accept(this.fd, newfd, isaa); if ((n == IOStatus.INTERRUPTED) && isOpen()) continue; + if (wispAndBlocking && n < 0) { + WEA.registerEvent(this, SelectionKey.OP_ACCEPT); + WEA.park(-1); + continue; + } break; } } finally { + if (wispAndBlocking) { + IOUtil.configureBlocking(fd, true); + } thread = 0; end(n > 0); assert IOStatus.check(n); diff --git a/src/share/demo/jvmti/java_crw_demo/java_crw_demo.c b/src/share/demo/jvmti/java_crw_demo/java_crw_demo.c index eaa271e9e189b09cd61055a2e4dc1e25f2e1d02f..27cee0fabe41efd027b5a3b0b2ed5d9cf7dd3f4b 100644 --- a/src/share/demo/jvmti/java_crw_demo/java_crw_demo.c +++ b/src/share/demo/jvmti/java_crw_demo/java_crw_demo.c @@ -2032,6 +2032,14 @@ skip_method(CrwClassImage *ci, const char *name, unsigned access_flags, ByteOffset code_len, int system_class, jboolean *pskip_call_return_sites) { + if (strcmp(name,"currentThread0") == 0 || + strcmp(name,"getJavaLangAccess") == 0) { + /* + * bypass here otherwise the jdk/test/demo/jvmti/hprof/ tests will fail + * caused by the change to the Thread.currentThread() in co-routine feature + */ + return JNI_TRUE; + } *pskip_call_return_sites = JNI_FALSE; if ( system_class ) { if ( code_len == 1 && is_init_method(name) ) { diff --git a/src/share/javavm/export/jvm.h b/src/share/javavm/export/jvm.h index cfcc5d509a093655f9b88fd9fab5da87160be4a9..27dd064b6c100d66f5fa4f4e8e4f7de303c0ca2d 100644 --- a/src/share/javavm/export/jvm.h +++ b/src/share/javavm/export/jvm.h @@ -268,6 +268,9 @@ JVM_Interrupt(JNIEnv *env, jobject thread); JNIEXPORT jboolean JNICALL JVM_IsInterrupted(JNIEnv *env, jobject thread, jboolean clearInterrupted); +JNIEXPORT jboolean JNICALL +JVM_CheckAndClearNativeInterruptForWisp(JNIEnv* env, jobject task, jobject jthread); + JNIEXPORT jboolean JNICALL JVM_HoldsLock(JNIEnv *env, jclass threadClass, jobject obj); @@ -280,6 +283,9 @@ JVM_GetAllThreads(JNIEnv *env, jclass dummy); JNIEXPORT void JNICALL JVM_SetNativeThreadName(JNIEnv *env, jobject jthread, jstring name); +JNIEXPORT jboolean JNICALL +JVM_IsInSameNative(JNIEnv *env, jobject thread); + /* getStackTrace() and getAllStackTraces() method */ JNIEXPORT jobjectArray JNICALL JVM_DumpThreads(JNIEnv *env, jclass threadClass, jobjectArray threads); @@ -342,6 +348,15 @@ JVM_CheckJWarmUpCompilationIsComplete(JNIEnv *env, jclass clz); JNIEXPORT void JNICALL JVM_NotifyJVMDeoptWarmUpMethods(JNIEnv* env, jclass clz); +JNIEXPORT void JNICALL +JVM_SetWispTask(JNIEnv* env, jclass clz, jlong coroutinePtr, jint task_id, jobject task, jobject engine); + +JNIEXPORT jint JNICALL +JVM_GetProxyUnpark(JNIEnv* env, jclass clz, jintArray res); + +JNIEXPORT void JNICALL +JVM_MarkPreempted(JNIEnv* env, jclass clz, jobject thread); + /* * com.alibaba.management.ElasticHeapMXBean */ diff --git a/src/share/native/java/dyn/Coroutine.c b/src/share/native/java/dyn/Coroutine.c new file mode 100644 index 0000000000000000000000000000000000000000..7aa8bc880d614fe96c99022e7a22e226720e3150 --- /dev/null +++ b/src/share/native/java/dyn/Coroutine.c @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "jni.h" +#include "jvm.h" +#include "java_dyn_Coroutine.h" + +#define ARRAY_LENGTH(a) (sizeof(a)/sizeof(a[0])) + +#define LANG "Ljava/lang/" +#define OBJ LANG"Object;" + +static JNINativeMethod methods[] = { + {"setWispTask","(JI"OBJ OBJ")V", (void *)&JVM_SetWispTask}, +}; + +JNIEXPORT void JNICALL +Java_java_dyn_Coroutine_registerNatives(JNIEnv *env, jclass cls) +{ + (*env)->RegisterNatives(env, cls, methods, ARRAY_LENGTH(methods)); +} diff --git a/src/share/native/java/lang/Thread.c b/src/share/native/java/lang/Thread.c index 05be07e7724f303fd2da242071d69f671e00c8da..5d7dab917091145e41b5f40067ff2b509efc2bca 100644 --- a/src/share/native/java/lang/Thread.c +++ b/src/share/native/java/lang/Thread.c @@ -43,13 +43,13 @@ static JNINativeMethod methods[] = { {"start0", "()V", (void *)&JVM_StartThread}, {"stop0", "(" OBJ ")V", (void *)&JVM_StopThread}, - {"isAlive", "()Z", (void *)&JVM_IsThreadAlive}, + {"isAlive0", "()Z", (void *)&JVM_IsThreadAlive}, {"suspend0", "()V", (void *)&JVM_SuspendThread}, {"resume0", "()V", (void *)&JVM_ResumeThread}, {"setPriority0", "(I)V", (void *)&JVM_SetThreadPriority}, - {"yield", "()V", (void *)&JVM_Yield}, - {"sleep", "(J)V", (void *)&JVM_Sleep}, - {"currentThread", "()" THD, (void *)&JVM_CurrentThread}, + {"yield0", "()V", (void *)&JVM_Yield}, + {"sleep0", "(J)V", (void *)&JVM_Sleep}, + {"currentThread0", "()" THD, (void *)&JVM_CurrentThread}, {"countStackFrames", "()I", (void *)&JVM_CountStackFrames}, {"interrupt0", "()V", (void *)&JVM_Interrupt}, {"isInterrupted", "(Z)Z", (void *)&JVM_IsInterrupted}, @@ -57,6 +57,7 @@ static JNINativeMethod methods[] = { {"getThreads", "()[" THD, (void *)&JVM_GetAllThreads}, {"dumpThreads", "([" THD ")[[" STE, (void *)&JVM_DumpThreads}, {"setNativeName", "(" STR ")V", (void *)&JVM_SetNativeThreadName}, + {"isInSameNative", "()Z", (void *)&JVM_IsInSameNative}, }; #undef THD diff --git a/src/solaris/classes/sun/nio/ch/EPoll.java b/src/solaris/classes/sun/nio/ch/EPoll.java index bb2b9e5171fb9416eb98b1d9ef9ec0773f789345..321b1ad307434b7cfd27f40613174f8ae2a1be99 100644 --- a/src/solaris/classes/sun/nio/ch/EPoll.java +++ b/src/solaris/classes/sun/nio/ch/EPoll.java @@ -26,6 +26,8 @@ package sun.nio.ch; import java.io.IOException; + +import sun.misc.SharedSecrets; import sun.misc.Unsafe; /** @@ -62,6 +64,8 @@ class EPoll { // flags static final int EPOLLONESHOT = (1 << 30); + static final int ENOENT; + /** * Allocates a poll array to handle up to {@code count} events. */ @@ -109,10 +113,17 @@ class EPoll { static native int epollCtl(int epfd, int opcode, int fd, int events); - static native int epollWait(int epfd, long pollAddress, int numfds) + static int epollWait(int epfd, long pollAddress, int numfds) throws IOException { + return epollWait(epfd, pollAddress, numfds, -1); + } + + static native int epollWait(int epfd, long pollAddress, int numfds, int timeout) throws IOException; + static native int errnoENOENT(); + static { IOUtil.load(); + ENOENT = errnoENOENT(); } } diff --git a/src/solaris/classes/sun/nio/ch/EPollArrayWrapper.java b/src/solaris/classes/sun/nio/ch/EPollArrayWrapper.java index e6a2b584d960a2cc1eb0d19ad79485cd0af4ec63..7c82f344e8864f8337dd245cfef2b38703c33b4a 100644 --- a/src/solaris/classes/sun/nio/ch/EPollArrayWrapper.java +++ b/src/solaris/classes/sun/nio/ch/EPollArrayWrapper.java @@ -30,6 +30,13 @@ import java.security.AccessController; import java.util.BitSet; import java.util.HashMap; import java.util.Map; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; + +import com.alibaba.wisp.engine.WispEngine; +import com.alibaba.wisp.engine.WispTask; +import sun.misc.SharedSecrets; +import sun.misc.WispEngineAccess; import sun.security.action.GetIntegerAction; /** @@ -57,6 +64,8 @@ import sun.security.action.GetIntegerAction; */ class EPollArrayWrapper { + private static final WispEngineAccess WEA = SharedSecrets.getWispEngineAccess(); + // EPOLL_EVENTS private static final int EPOLLIN = 0x001; @@ -266,7 +275,9 @@ class EPollArrayWrapper { int poll(long timeout) throws IOException { updateRegistrations(); - updated = epollWait(pollArrayAddress, NUM_EPOLLEVENTS, timeout, epfd); + updated = WispEngine.transparentWispSwitch() ? + handleEPollWithWisp(timeout) : + epollWait(pollArrayAddress, NUM_EPOLLEVENTS, timeout, epfd); for (int i=0; i status = new AtomicReference<>(); + // null: initial status + // INTERRUPTED: interrupted by wakeup() + // other: task blocking on this selector + + private int handleEPollWithWisp(long timeout) throws IOException { + int updated = WEA.epollWait(epfd, pollArrayAddress, NUM_EPOLLEVENTS, timeout, status, INTERRUPTED); + if (WEA.useDirectSelectorWakeup() && status.get() == INTERRUPTED) { + interrupted = true; + } + return updated; + } + /** * Update the pending registrations. */ @@ -314,7 +339,11 @@ class EPollArrayWrapper { private boolean interrupted = false; public void interrupt() { - interrupt(outgoingInterruptFD); + if (WispEngine.transparentWispSwitch() && WEA.useDirectSelectorWakeup()) { + WEA.interruptEpoll(status, INTERRUPTED, outgoingInterruptFD); + } else { + interrupt(outgoingInterruptFD); + } } public int interruptedIndex() { @@ -327,6 +356,10 @@ class EPollArrayWrapper { void clearInterrupted() { interrupted = false; + if (WispEngine.transparentWispSwitch() && WEA.useDirectSelectorWakeup()) { + assert status.get() == INTERRUPTED; + status.lazySet(null); + } } static { diff --git a/src/solaris/classes/sun/nio/ch/EPollPort.java b/src/solaris/classes/sun/nio/ch/EPollPort.java index 651457e98df2ed7ea27efd86edc7967b62ffa203..c184593fc32c8355f1f1b7ce5179a27672f26e3e 100644 --- a/src/solaris/classes/sun/nio/ch/EPollPort.java +++ b/src/solaris/classes/sun/nio/ch/EPollPort.java @@ -25,6 +25,8 @@ package sun.nio.ch; +import sun.misc.SharedSecrets; + import java.nio.channels.spi.AsynchronousChannelProvider; import java.io.IOException; import java.util.concurrent.ArrayBlockingQueue; @@ -319,5 +321,91 @@ final class EPollPort static { IOUtil.load(); + SharedSecrets.setIOEventAccess(new IOEventAccess() { + @Override + public int eventCtlAdd() { + return EPoll.EPOLL_CTL_ADD; + } + + @Override + public int eventCtlDel() { + return EPoll.EPOLL_CTL_DEL; + } + + @Override + public int eventCtlMod() { + return EPoll.EPOLL_CTL_MOD; + } + + @Override + public int eventOneShot() { + return EPoll.EPOLLONESHOT; + } + + @Override + public int noEvent() { + return EPoll.ENOENT; + } + + @Override + public long allocatePollArray(int count) { + return EPoll.allocatePollArray(count); + } + + @Override + public void freePollArray(long address) { + EPoll.freePollArray(address); + } + + @Override + public long getEvent(long address, int i) { + return EPoll.getEvent(address, i); + } + + @Override + public int getDescriptor(long eventAddress) { + return EPoll.getDescriptor(eventAddress); + } + + @Override + public int getEvents(long eventAddress) { + return EPoll.getEvents(eventAddress); + } + + @Override + public int eventCreate() throws IOException { + return EPoll.epollCreate(); + } + + @Override + public int eventCtl(int epfd, int opcode, int fd, int events) { + return EPoll.epollCtl(epfd, opcode, fd, events); + } + + @Override + public int eventWait(int epfd, long pollAddress, int numfds, int timeout) throws IOException { + return EPoll.epollWait(epfd, pollAddress, numfds, timeout); + } + + @Override + public void socketpair(int[] sv) throws IOException { + EPollPort.socketpair(sv); + } + + @Override + public void interrupt(int fd) throws IOException { + EPollPort.interrupt(fd); + } + + @Override + public void drain(int fd) throws IOException { + EPollPort.drain1(fd); + } + + @Override + public void close(int fd) { + EPollPort.close0(fd); + } + }); } } diff --git a/src/solaris/classes/sun/nio/ch/EPollSelectorImpl.java b/src/solaris/classes/sun/nio/ch/EPollSelectorImpl.java index 1528b9e16b8182864bf113235f336f6882243096..8cb800acd7a732a5bac322b6452e7fb20fb2ccbe 100644 --- a/src/solaris/classes/sun/nio/ch/EPollSelectorImpl.java +++ b/src/solaris/classes/sun/nio/ch/EPollSelectorImpl.java @@ -29,6 +29,8 @@ import java.io.IOException; import java.nio.channels.*; import java.nio.channels.spi.*; import java.util.*; + +import com.alibaba.wisp.engine.WispEngine; import sun.misc.*; /** @@ -38,6 +40,7 @@ import sun.misc.*; class EPollSelectorImpl extends SelectorImpl { + private static final WispEngineAccess WEA = SharedSecrets.getWispEngineAccess(); // File descriptors used for interrupt protected int fd0; @@ -101,7 +104,12 @@ class EPollSelectorImpl pollWrapper.putEventOps(pollWrapper.interruptedIndex(), 0); synchronized (interruptLock) { pollWrapper.clearInterrupted(); - IOUtil.drain(fd0); + if (WispEngine.transparentWispSwitch() + && WEA.useDirectSelectorWakeup() && WEA.isAllThreadAsWisp()) { + // skip + } else { + IOUtil.drain(fd0); + } interruptTriggered = false; } } diff --git a/src/solaris/native/sun/nio/ch/EPoll.c b/src/solaris/native/sun/nio/ch/EPoll.c index ea9cdf4dd039a52330cc00b838c645eb13972918..7b1936c94a0e61ae6181706d6e37196607e8f741 100644 --- a/src/solaris/native/sun/nio/ch/EPoll.c +++ b/src/solaris/native/sun/nio/ch/EPoll.c @@ -84,12 +84,12 @@ Java_sun_nio_ch_EPoll_epollCtl(JNIEnv *env, jclass c, jint epfd, JNIEXPORT jint JNICALL Java_sun_nio_ch_EPoll_epollWait(JNIEnv *env, jclass c, - jint epfd, jlong address, jint numfds) + jint epfd, jlong address, jint numfds, jint timeout) { struct epoll_event *events = jlong_to_ptr(address); int res; - RESTARTABLE(epoll_wait(epfd, events, numfds, -1), res); + RESTARTABLE(epoll_wait(epfd, events, numfds, timeout), res); if (res < 0) { JNU_ThrowIOExceptionWithLastError(env, "epoll_wait failed"); } @@ -101,3 +101,8 @@ Java_sun_nio_ch_EPoll_close0(JNIEnv *env, jclass c, jint epfd) { int res; RESTARTABLE(close(epfd), res); } + +JNIEXPORT jint JNICALL +Java_sun_nio_ch_EPoll_errnoENOENT(JNIEnv *env, jclass this) { + return (jint)ENOENT; +} diff --git a/src/windows/classes/com/alibaba/wisp/engine/WispCounter.java b/src/windows/classes/com/alibaba/wisp/engine/WispCounter.java new file mode 100644 index 0000000000000000000000000000000000000000..0975f673773484378f4e1eec88d918d2983a5c9e --- /dev/null +++ b/src/windows/classes/com/alibaba/wisp/engine/WispCounter.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.alibaba.wisp.engine; + +final public class WispCounter { + +} diff --git a/src/windows/classes/com/alibaba/wisp/engine/WispCounterMXBeanImpl.java b/src/windows/classes/com/alibaba/wisp/engine/WispCounterMXBeanImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..6438cc340fa97601eec5dfe5b8bbb484889b4104 --- /dev/null +++ b/src/windows/classes/com/alibaba/wisp/engine/WispCounterMXBeanImpl.java @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.alibaba.wisp.engine; + +import com.alibaba.management.WispCounterMXBean; + +import javax.management.ObjectName; +import java.util.List; + +public class WispCounterMXBeanImpl implements WispCounterMXBean { + + @Override + public List getRunningStates() { + throw new UnsupportedOperationException(); + } + + @Override + public List getSwitchCount() { + throw new UnsupportedOperationException(); + } + + @Override + public List getWaitTimeTotal() { + throw new UnsupportedOperationException(); + } + + @Override + public List getRunningTimeTotal() { + throw new UnsupportedOperationException(); + } + + @Override + public List getCompleteTaskCount() { + throw new UnsupportedOperationException(); + } + + @Override + public List getCreateTaskCount() { + throw new UnsupportedOperationException(); + } + + @Override + public List getParkCount() { + throw new UnsupportedOperationException(); + } + + @Override + public List getUnparkCount() { + throw new UnsupportedOperationException(); + } + + @Override + public List getLazyUnparkCount() { + throw new UnsupportedOperationException(); + } + + @Override + public List getUnparkInterruptSelectorCount() { + throw new UnsupportedOperationException(); + } + + @Override + public List getSelectableIOCount() { + throw new UnsupportedOperationException(); + } + + @Override + public List getTimeOutCount() { + throw new UnsupportedOperationException(); + } + + @Override + public List getEventLoopCount() { + throw new UnsupportedOperationException(); + } + + @Override + public List getQueueLength() { + throw new UnsupportedOperationException(); + } + + @Override + public List getNumberOfRunningTasks() { + throw new UnsupportedOperationException(); + } + + @Override + public List getTotalEnqueueTime() { + throw new UnsupportedOperationException(); + } + + @Override + public List getEnqueueCount() { + throw new UnsupportedOperationException(); + } + + @Override + public List getTotalExecutionTime() { + throw new UnsupportedOperationException(); + } + + @Override + public List getExecutionCount() { + throw new UnsupportedOperationException(); + } + + @Override + public List getTotalWaitSocketIOTime() { + throw new UnsupportedOperationException(); + } + + @Override + public List getWaitSocketIOCount() { + throw new UnsupportedOperationException(); + } + + @Override + public List getTotalBlockingTime() { + throw new UnsupportedOperationException(); + } + + @Override + public WispCounter getWispCounter(long id) { + throw new UnsupportedOperationException(); + } + + @Override + public ObjectName getObjectName() { + throw new UnsupportedOperationException(); + } +} diff --git a/src/windows/classes/com/alibaba/wisp/engine/WispEngine.java b/src/windows/classes/com/alibaba/wisp/engine/WispEngine.java new file mode 100644 index 0000000000000000000000000000000000000000..fd5a9354ba97ca23d891e20e8b7004f224036c27 --- /dev/null +++ b/src/windows/classes/com/alibaba/wisp/engine/WispEngine.java @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.alibaba.wisp.engine; + +import sun.misc.SharedSecrets; +import sun.misc.WispEngineAccess; + +import java.io.IOException; +import java.nio.channels.SelectableChannel; +import java.util.concurrent.atomic.AtomicReference; + +public class WispEngine { + + public static boolean transparentWispSwitch() { + return false; + } + + public static boolean enableThreadAsWisp() { + return false; + } + + private static void setWispEngineAccess() { + SharedSecrets.setWispEngineAccess(new WispEngineAccess() { + + @Override + public WispTask getCurrentTask() { + throw new UnsupportedOperationException(); + } + + @Override + public void registerEvent(SelectableChannel ch, int events) throws IOException { + throw new UnsupportedOperationException(); + } + + @Override + public void unregisterEvent() { + throw new UnsupportedOperationException(); + } + + @Override + public int epollWait(int epfd, long pollArray, int arraySize, long timeout, + AtomicReference status, Object INTERRUPTED) throws IOException { + throw new UnsupportedOperationException(); + } + + @Override + public void interruptEpoll(AtomicReference status, Object INTERRUPTED, int interruptFd) { + throw new UnsupportedOperationException(); + } + + @Override + public void addTimer(long deadlineNano) { + throw new UnsupportedOperationException(); + } + + @Override + public void cancelTimer() { + throw new UnsupportedOperationException(); + } + + @Override + public void sleep(long ms) { + throw new UnsupportedOperationException(); + } + + @Override + public void yield() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isThreadTask(WispTask task) { + return false; + } + + @Override + public boolean isTimeout() { + return false; + } + + @Override + public void park(long timeoutNano) { + throw new UnsupportedOperationException(); + } + + @Override + public void unpark(WispTask task) { + throw new UnsupportedOperationException(); + } + + @Override + public void destroy() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean hasMoreTasks() { + return false; + } + + @Override + public boolean runningAsCoroutine(Thread t) { + return false; + } + + @Override + public boolean usingWispEpoll() { + return false; + } + + public boolean isAlive(WispTask task) { + return false; + } + + @Override + public void interrupt(WispTask task) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean testInterruptedAndClear(WispTask task, boolean clear) { + return false; + } + + @Override + public boolean tryStartThreadAsWisp(Thread thread, Runnable target) { + return false; + } + + @Override + public boolean isAllThreadAsWisp() { + return false; + } + + @Override + public boolean useDirectSelectorWakeup() { + return false; + } + + @Override + public boolean enableSocketLock() { + return false; + } + + @Override + public StackTraceElement[] getStackTrace(WispTask task) { + throw new UnsupportedOperationException(); + } + }); + } + +} diff --git a/src/windows/classes/com/alibaba/wisp/engine/WispTask.java b/src/windows/classes/com/alibaba/wisp/engine/WispTask.java new file mode 100644 index 0000000000000000000000000000000000000000..40ef3fc58213f8897097718b48eebe978e613cc1 --- /dev/null +++ b/src/windows/classes/com/alibaba/wisp/engine/WispTask.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.alibaba.wisp.engine; + + +public class WispTask { + + public Thread getThreadWrapper() { + throw new UnsupportedOperationException(); + } + +} diff --git a/src/windows/classes/com/alibaba/wisp/engine/WispThreadWrapper.java b/src/windows/classes/com/alibaba/wisp/engine/WispThreadWrapper.java new file mode 100644 index 0000000000000000000000000000000000000000..67999b115507afb9e8a02f1ebc7bbee356fc8677 --- /dev/null +++ b/src/windows/classes/com/alibaba/wisp/engine/WispThreadWrapper.java @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.alibaba.wisp.engine; + + +import sun.reflect.CallerSensitive; + +import java.dyn.CoroutineSupport; + +class WispThreadWrapper extends Thread { + + @Override + public CoroutineSupport getCoroutineSupport() { + throw new UnsupportedOperationException(); + } + + @Override + public void start() { + throw new UnsupportedOperationException(); + } + + + @Override + @Deprecated + public void destroy() { + throw new UnsupportedOperationException(); + } + + @Override + @Deprecated + public int countStackFrames() { + throw new UnsupportedOperationException(); + } + + @Override + @CallerSensitive + public ClassLoader getContextClassLoader() { + throw new UnsupportedOperationException(); + } + + @Override + public void setContextClassLoader(ClassLoader cl) { + throw new UnsupportedOperationException(); + } + + @Override + public StackTraceElement[] getStackTrace() { + throw new UnsupportedOperationException(); + } + + @Override + public long getId() { + throw new UnsupportedOperationException(); + } + + @Override + public State getState() { + throw new UnsupportedOperationException(); + } + + @Override + public UncaughtExceptionHandler getUncaughtExceptionHandler() { + throw new UnsupportedOperationException(); + } + + @Override + public void setUncaughtExceptionHandler(UncaughtExceptionHandler eh) { + throw new UnsupportedOperationException(); + } + + @Override + public String toString() { + throw new UnsupportedOperationException(); + } + +} diff --git a/src/windows/classes/sun/nio/ch/IOEvent.java b/src/windows/classes/sun/nio/ch/IOEvent.java new file mode 100644 index 0000000000000000000000000000000000000000..51db2db85782dbb1f5e11edff72073c70edb7de8 --- /dev/null +++ b/src/windows/classes/sun/nio/ch/IOEvent.java @@ -0,0 +1,8 @@ +package sun.nio.ch; + +public class IOEvent { + + public static Class eventClass() { + return IOEventRegister.class; + } +} \ No newline at end of file diff --git a/src/windows/classes/sun/nio/ch/IOEventRegister.java b/src/windows/classes/sun/nio/ch/IOEventRegister.java new file mode 100644 index 0000000000000000000000000000000000000000..06f724db98d04d20d991d0d51ad6fe9afebf6b83 --- /dev/null +++ b/src/windows/classes/sun/nio/ch/IOEventRegister.java @@ -0,0 +1,99 @@ +package sun.nio.ch; + +import sun.misc.SharedSecrets; + +import java.io.IOException; + +public class IOEventRegister { + + static { + SharedSecrets.setIOEventAccess(new IOEventAccess() { + + @Override + public int eventCtlAdd() { + throw new UnsupportedOperationException(); + } + + @Override + public int eventCtlDel() { + throw new UnsupportedOperationException(); + } + + @Override + public int eventCtlMod() { + throw new UnsupportedOperationException(); + } + + @Override + public int eventOneShot() { + throw new UnsupportedOperationException(); + } + + @Override + public int noEvent() { + throw new UnsupportedOperationException(); + } + + @Override + public long allocatePollArray(int count) { + throw new UnsupportedOperationException(); + } + + @Override + public void freePollArray(long address) { + throw new UnsupportedOperationException(); + } + + @Override + public long getEvent(long address, int i) { + throw new UnsupportedOperationException(); + } + + @Override + public int getDescriptor(long eventAddress) { + throw new UnsupportedOperationException(); + } + + @Override + public int getEvents(long eventAddress) { + throw new UnsupportedOperationException(); + } + + @Override + public int eventCreate() throws IOException { + throw new UnsupportedOperationException(); + } + + @Override + public int eventCtl(int epfd, int opcode, int fd, int events) { + throw new UnsupportedOperationException(); + } + + @Override + public int eventWait(int epfd, long pollAddress, int numfds, int timeout) throws IOException { + throw new UnsupportedOperationException(); + } + + @Override + public void socketpair(int[] sv) throws IOException { + throw new UnsupportedOperationException(); + } + + @Override + public void interrupt(int fd) throws IOException { + throw new UnsupportedOperationException(); + } + + @Override + public void drain(int fd) throws IOException { + throw new UnsupportedOperationException(); + } + + @Override + public void close(int fd) { + throw new UnsupportedOperationException(); + } + }); + } + +} \ No newline at end of file diff --git a/src/windows/classes/sun/nio/ch/WispServerSocketImpl.java b/src/windows/classes/sun/nio/ch/WispServerSocketImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..b6fdd26984c6197a8bcc47b30f0ba5423e88b47a --- /dev/null +++ b/src/windows/classes/sun/nio/ch/WispServerSocketImpl.java @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package sun.nio.ch; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.Socket; +import java.net.SocketAddress; +import java.net.SocketException; +import java.nio.channels.ServerSocketChannel; + + +public class WispServerSocketImpl +{ + + public WispServerSocketImpl() { + } + + public void bind(SocketAddress local) throws IOException { + throw new UnsupportedOperationException(); + } + + public void bind(SocketAddress local, int backlog) throws IOException { + throw new UnsupportedOperationException(); + } + + public InetAddress getInetAddress() { + throw new UnsupportedOperationException(); + } + + public int getLocalPort() { + throw new UnsupportedOperationException(); + } + + public Socket accept() throws IOException { + throw new UnsupportedOperationException(); + } + + public void close() throws IOException { + throw new UnsupportedOperationException(); + } + + public ServerSocketChannel getChannel() { + throw new UnsupportedOperationException(); + } + + public boolean isBound() { + throw new UnsupportedOperationException(); + } + + public boolean isClosed() { + throw new UnsupportedOperationException(); + } + + public void setSoTimeout(int timeout) throws SocketException { + throw new UnsupportedOperationException(); + } + + public int getSoTimeout() throws IOException { + throw new UnsupportedOperationException(); + } + + public void setReuseAddress(boolean on) throws SocketException { + throw new UnsupportedOperationException(); + } + + public boolean getReuseAddress() throws SocketException { + throw new UnsupportedOperationException(); + } + + public String toString() { + throw new UnsupportedOperationException(); + } + + public void setReceiveBufferSize(int size) throws SocketException { + throw new UnsupportedOperationException(); + } + + public int getReceiveBufferSize() throws SocketException { + throw new UnsupportedOperationException(); + } + +} diff --git a/src/windows/classes/sun/nio/ch/WispSocketImpl.java b/src/windows/classes/sun/nio/ch/WispSocketImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..318ad6436fe376a472c9c2dc4283a41e60c72f56 --- /dev/null +++ b/src/windows/classes/sun/nio/ch/WispSocketImpl.java @@ -0,0 +1,197 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package sun.nio.ch; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.InetAddress; +import java.net.Socket; +import java.net.SocketAddress; +import java.net.SocketException; +import java.nio.channels.SocketChannel; + + +public class WispSocketImpl +{ + + public WispSocketImpl(Socket so) { + throw new UnsupportedOperationException(); + } + + public WispSocketImpl(SocketChannel sc, Socket so) { + throw new UnsupportedOperationException(); + } + + public SocketChannel getChannel() { + throw new UnsupportedOperationException(); + } + + public void connect(SocketAddress remote) throws IOException { + throw new UnsupportedOperationException(); + } + + public void connect(SocketAddress remote, int timeout) throws IOException { + throw new UnsupportedOperationException(); + } + + public void bind(SocketAddress local) throws IOException { + throw new UnsupportedOperationException(); + } + + public InetAddress getInetAddress() { + throw new UnsupportedOperationException(); + } + + public InetAddress getLocalAddress() { + throw new UnsupportedOperationException(); + } + + public int getPort() { + throw new UnsupportedOperationException(); + } + + public int getLocalPort() { + throw new UnsupportedOperationException(); + } + + public InputStream getInputStream() throws IOException { + throw new UnsupportedOperationException(); + } + + public OutputStream getOutputStream() throws IOException { + throw new UnsupportedOperationException(); + } + + public void setTcpNoDelay(boolean on) throws SocketException { + throw new UnsupportedOperationException(); + } + + public boolean getTcpNoDelay() throws SocketException { + throw new UnsupportedOperationException(); + } + + public void setSoLinger(boolean on, int linger) throws SocketException { + throw new UnsupportedOperationException(); + } + + public int getSoLinger() throws SocketException { + throw new UnsupportedOperationException(); + } + + public void sendUrgentData(int data) throws IOException { + throw new UnsupportedOperationException(); + } + + public void setOOBInline(boolean on) throws SocketException { + throw new UnsupportedOperationException(); + } + + public boolean getOOBInline() throws SocketException { + throw new UnsupportedOperationException(); + } + + public void setSoTimeout(int timeout) throws SocketException { + throw new UnsupportedOperationException(); + } + + public int getSoTimeout() throws SocketException { + throw new UnsupportedOperationException(); + } + + public void setSendBufferSize(int size) throws SocketException { + throw new UnsupportedOperationException(); + } + + public int getSendBufferSize() throws SocketException { + throw new UnsupportedOperationException(); + } + + public void setReceiveBufferSize(int size) throws SocketException { + throw new UnsupportedOperationException(); + } + + public int getReceiveBufferSize() throws SocketException { + throw new UnsupportedOperationException(); + } + + public void setKeepAlive(boolean on) throws SocketException { + throw new UnsupportedOperationException(); + } + + public boolean getKeepAlive() throws SocketException { + throw new UnsupportedOperationException(); + } + + public void setTrafficClass(int tc) throws SocketException { + throw new UnsupportedOperationException(); + } + + public int getTrafficClass() throws SocketException { + throw new UnsupportedOperationException(); + } + + public void setReuseAddress(boolean on) throws SocketException { + throw new UnsupportedOperationException(); + } + + public boolean getReuseAddress() throws SocketException { + throw new UnsupportedOperationException(); + } + + public void close() throws IOException { + throw new UnsupportedOperationException(); + } + + public void shutdownInput() throws IOException { + throw new UnsupportedOperationException(); + } + + public void shutdownOutput() throws IOException { + throw new UnsupportedOperationException(); + } + + public String toString() { + throw new UnsupportedOperationException(); + } + + public boolean isConnected() { + throw new UnsupportedOperationException(); + } + + public boolean isBound() { + throw new UnsupportedOperationException(); + } + + public boolean isClosed() { + throw new UnsupportedOperationException(); + } + + public boolean isInputShutdown() { + throw new UnsupportedOperationException(); + } + + public boolean isOutputShutdown() { + throw new UnsupportedOperationException(); + } + +} diff --git a/src/windows/classes/sun/nio/ch/WispSocketLockSupport.java b/src/windows/classes/sun/nio/ch/WispSocketLockSupport.java new file mode 100644 index 0000000000000000000000000000000000000000..62373f0c88135b99343803d7189288fc920bb27e --- /dev/null +++ b/src/windows/classes/sun/nio/ch/WispSocketLockSupport.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package sun.nio.ch; + +class WispSocketLockSupport { + +} diff --git a/src/windows/classes/sun/nio/ch/WispUdpSocketImpl.java b/src/windows/classes/sun/nio/ch/WispUdpSocketImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..e3d5f79fd9d5b1ba4f7b460eab9729fb2c2b0f5e --- /dev/null +++ b/src/windows/classes/sun/nio/ch/WispUdpSocketImpl.java @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package sun.nio.ch; + +import java.io.IOException; +import java.net.*; +import java.nio.ByteBuffer; +import java.nio.channels.DatagramChannel; + + +// Make a udp socket channel look like a datagram socket. + +public class WispUdpSocketImpl { + + public WispUdpSocketImpl(DatagramSocket so) { + throw new UnsupportedOperationException(); + } + + public void bind(SocketAddress local) throws SocketException { + throw new UnsupportedOperationException(); + } + + public void connect(InetAddress address, int port) { + throw new UnsupportedOperationException(); + } + + public void connect(SocketAddress remote) throws SocketException { + throw new UnsupportedOperationException(); + } + + public void disconnect() { + throw new UnsupportedOperationException(); + } + + public boolean isBound() { + throw new UnsupportedOperationException(); + } + + public boolean isConnected() { + throw new UnsupportedOperationException(); + } + + public InetAddress getInetAddress() { + throw new UnsupportedOperationException(); + } + + public int getPort() { + throw new UnsupportedOperationException(); + } + + public void send(DatagramPacket p) throws IOException { + throw new UnsupportedOperationException(); + } + + private SocketAddress receive(ByteBuffer bb) throws IOException { + throw new UnsupportedOperationException(); + } + + public int receive(DatagramPacket p, int bufLen) throws IOException { + throw new UnsupportedOperationException(); + } + + public InetAddress getLocalAddress() { + throw new UnsupportedOperationException(); + } + + public int getLocalPort() { + throw new UnsupportedOperationException(); + } + + public void setSoTimeout(int timeout) throws SocketException { + throw new UnsupportedOperationException(); + } + + public int getSoTimeout() throws SocketException { + throw new UnsupportedOperationException(); + } + + public void setSendBufferSize(int size) throws SocketException { + throw new UnsupportedOperationException(); + } + + public int getSendBufferSize() throws SocketException { + throw new UnsupportedOperationException(); + } + + public void setReceiveBufferSize(int size) throws SocketException { + throw new UnsupportedOperationException(); + } + + public int getReceiveBufferSize() throws SocketException { + throw new UnsupportedOperationException(); + } + + public void setReuseAddress(boolean on) throws SocketException { + throw new UnsupportedOperationException(); + } + + public boolean getReuseAddress() throws SocketException { + throw new UnsupportedOperationException(); + } + + public void setBroadcast(boolean on) throws SocketException { + throw new UnsupportedOperationException(); + } + + public boolean getBroadcast() throws SocketException { + throw new UnsupportedOperationException(); + } + + public void setTrafficClass(int tc) throws SocketException { + throw new UnsupportedOperationException(); + } + + public int getTrafficClass() throws SocketException { + throw new UnsupportedOperationException(); + } + + public void close() { + throw new UnsupportedOperationException(); + } + + public boolean isClosed() { + throw new UnsupportedOperationException(); + } + + public DatagramChannel getChannel() { + throw new UnsupportedOperationException(); + } + +} diff --git a/test/com/alibaba/management/WispCounterMXBean/TestWispCounter.java b/test/com/alibaba/management/WispCounterMXBean/TestWispCounter.java new file mode 100644 index 0000000000000000000000000000000000000000..1828b6756029f6091dc51e2dd5d76f590d341d5d --- /dev/null +++ b/test/com/alibaba/management/WispCounterMXBean/TestWispCounter.java @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +import com.alibaba.management.WispCounterMXBean; + +import javax.management.MBeanServer; +import java.io.*; +import java.lang.management.ManagementFactory; +import java.lang.management.RuntimeMXBean; +import java.lang.reflect.Method; +import java.net.ServerSocket; +import java.net.Socket; +import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +import static jdk.testlibrary.Asserts.assertTrue; + +/* @test + * @summary WispCounterMXBean unit test + * @requires os.family == "linux" + * @library /lib/testlibrary + * @run main/othervm/timeout=2000 -XX:+UseWisp2 -Dcom.alibaba.wisp.config=/tmp/wisp.config TestWispCounter + */ + +public class TestWispCounter { + + public static void main(String[] args) throws Exception { + startNetServer(); + MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); + WispCounterMXBean mbean = null; + try { + + mbean = ManagementFactory.newPlatformMXBeanProxy(mbs, + "com.alibaba.management:type=WispCounter", WispCounterMXBean.class); + } catch (IOException e) { + e.printStackTrace(); + } + + ExecutorService es = Executors.newFixedThreadPool(20); + int taskTotal = 40; + for (int i = 0; i < taskTotal; i++) { + // submit task + es.submit(() -> { + // do park/unpark + synchronized (TestWispCounter.class) { + // do sleep + try { + Thread.sleep(10L); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + // do net IO + doNetIO(); + }); + } + + System.out.println(mbean.getRunningStates()); + System.out.println(mbean.getQueueLength()); + // wait task complete + Thread.sleep(1_000L); + System.out.println(mbean.getSwitchCount()); + assertTrue(mbean.getSwitchCount().stream().mapToLong(Long::longValue).sum() >= taskTotal); + System.out.println(mbean.getSelectableIOCount()); + assertTrue(mbean.getSwitchCount().stream().mapToLong(Long::longValue).sum() > 0); + System.out.println(mbean.getParkCount()); + assertTrue(mbean.getParkCount().stream().mapToLong(Long::longValue).sum() > 0); + } + + private static ServerSocket ss; + private static final int PORT = 23000; + private static final int BUFFER_SIZE = 1024; + + private static void startNetServer() throws IOException { + ss = new ServerSocket(PORT); + Thread t = new Thread(() -> { + try { + while (true) { + Socket cs = ss.accept(); + InputStream is = cs.getInputStream(); + int r = is.read(new byte[BUFFER_SIZE]); + is.close(); + cs.close(); + } + } catch (IOException e) { + e.printStackTrace(); + } + }); + t.setDaemon(true); + t.start(); + } + + private static void doNetIO() { + try { + Socket so = new Socket("localhost", PORT); + OutputStream os = so.getOutputStream(); + os.write(new byte[BUFFER_SIZE]); + os.flush(); + os.close(); + } catch (IOException e) { + e.printStackTrace(); + throw new Error(e); + } + } +} diff --git a/test/com/alibaba/wisp/ConfigurationCompatibilityCheckTest.java b/test/com/alibaba/wisp/ConfigurationCompatibilityCheckTest.java new file mode 100644 index 0000000000000000000000000000000000000000..4ffe1b391c755919a1bb7e39e71f8ab08d1fe926 --- /dev/null +++ b/test/com/alibaba/wisp/ConfigurationCompatibilityCheckTest.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * @test + * @summary Test the config compatibility in different wisp version + * @requires os.family == "linux" + * @library /lib/testlibrary + * @run main ConfigurationCompatibilityCheckTest + */ + +import java.util.ArrayList; +import java.util.Arrays; + +public class ConfigurationCompatibilityCheckTest { + public static void main(String[] args) throws Exception { + incompatibility("-Dcom.alibaba.wisp.enableThreadAsWisp=true"); + incompatibility("-Dcom.alibaba.wisp.enableThreadAsWisp=true", "-Dcom.alibaba.wisp.transparentWispSwitch=false"); + incompatibility("-Dcom.alibaba.wisp.allThreadAsWisp=true"); + incompatibility("-Dcom.alibaba.wisp.allThreadAsWisp=true", "-Dcom.alibaba.wisp.enableThreadAsWisp=false"); + incompatibility("-Dcom.alibaba.wisp.useCarrierAsPoller=true", "-Dcom.alibaba.wisp.allThreadAsWisp=false"); + } + + private static void incompatibility(String... args) throws Exception { + ArrayList list = new ArrayList<>(); + list.add("-XX:+EnableCoroutine"); + list.addAll(Arrays.asList(args)); + list.add("-version"); + ProcessBuilder pb = jdk.testlibrary.ProcessTools.createJavaProcessBuilder(list.toArray(new String[0])); + jdk.testlibrary.OutputAnalyzer output = new jdk.testlibrary.OutputAnalyzer(pb.start()); + output.shouldContain("IllegalArgumentException"); + } +} diff --git a/test/com/alibaba/wisp/CoroutineTest.java b/test/com/alibaba/wisp/CoroutineTest.java new file mode 100644 index 0000000000000000000000000000000000000000..2fa273d144407367c3a9b7fece4dcf63bf0cee51 --- /dev/null +++ b/test/com/alibaba/wisp/CoroutineTest.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * @test + * @summary Test low level coroutine implement + * @requires os.family == "linux" + * @run main/othervm -XX:+EnableCoroutine CoroutineTest + */ + +import java.dyn.Coroutine; +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; + +/** + * test low level coroutine implement + * 2 coroutine switch to each other and see the sequence + */ +public class CoroutineTest { + static int i = 0; + static Coroutine co1, co2; + + public static void main(String[] args) { + try { + + co1 = new Coroutine(() -> { + try { + if (i++ != 0) + throw new RuntimeException("co1 wrong sequence, expect 0"); + Coroutine.yieldTo(co2); + if (i++ != 2) + throw new RuntimeException("co1 wrong sequence, expect 2"); + Coroutine.yieldTo(co2); + } catch (Exception e) { + throw new RuntimeException(e); + } + }); + co2 = new Coroutine(() -> { + + try { + if (i++ != 1) + throw new RuntimeException("co2 wrong sequence, expect 1"); + Coroutine.yieldTo(co1); + if (i++ != 3) + throw new RuntimeException("co2 wrong sequence, expect 3"); + } catch (Exception e) { + throw new RuntimeException(e); + } + }); + + Coroutine.yieldTo(co1); + + + } catch (Exception e) { + throw new RuntimeException(); + } + } +} diff --git a/test/com/alibaba/wisp/ExecutionTest.java b/test/com/alibaba/wisp/ExecutionTest.java new file mode 100644 index 0000000000000000000000000000000000000000..5d0878bd7364a5b51d4b9702e063988ba634db0d --- /dev/null +++ b/test/com/alibaba/wisp/ExecutionTest.java @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * @test + * @summary Test WispCarrier's multi-task schedule + * @requires os.family == "linux" + * @run main/othervm -XX:+EnableCoroutine -Dcom.alibaba.wisp.transparentWispSwitch=true ExecutionTest + */ + + +import com.alibaba.wisp.engine.WispEngine; +import com.alibaba.wisp.engine.WispTask; +import sun.misc.SharedSecrets; + +import java.util.concurrent.Callable; +import java.util.concurrent.FutureTask; + +/** + * test the coroutine execute engine + */ +public class ExecutionTest { + + static int finishCnt = 0; + + public static void main(String[] args) { + Callable handler = () -> { + /** + * transform from: + * if ((nodeA() + nodeB()).equals("A:done\nB1:done\nB2:done\n")) + * throw new Error("result error"); + */ + FutureTask futureA = new FutureTask<>(ExecutionTest::nodeA); + FutureTask futureB = new FutureTask<>(ExecutionTest::nodeB); + WispEngine.current().execute(futureA); + WispEngine.current().execute(futureB); + String result; + try { + result = futureA.get() + futureB.get(); + } catch (Exception e) { + throw new Error("exception during task execution"); + } + + if (!result.equals("A:done\nB1:done\nB2:done\n")) + throw new Error("result error"); + + finishCnt++; + return null; + }; + + /** + *
+         * WispCarrier.local().createTask(() -> {
+         *     while (client = accept()) {
+         *         WispCarrier.local().createTask(()->handler(client), "client handler");
+         *     }
+         * }, "accepter");
+         *
+         * a web server can using a accepter {@link WispTask}  create handler for every request
+         * we don't have request, create 3 handler manually
+         *
+         * every handler is a tree root
+         *         handler         handler        handler
+         *         /    \           /   \          /   \
+         *        A     B          A    B         A    B
+         *             / \             / \            / \
+         *            B1 B2           B1 B2          B1 B2
+         * 
+ * + * look into a particular tree: + * B1 and B2 is created when nodeB is executed after nodeA blocked + * business code drive the node create .. + * + * then B1 block; B2 executed and block + * then the engine block on pump about 100ms.. + * + * + * 3 tree execute concurrently + * + */ + for (int i = 0; i < 3; i++) { + WispEngine.current().execute(() -> { + try { + handler.call(); + } catch (Exception e) { + e.printStackTrace(); + } + }); + } + + SharedSecrets.getWispEngineAccess().sleep(200); + // the 3 tree should finish after 100ms+, but the switch need warm up, give more time.. + if (finishCnt != 3) throw new Error("not finished"); + } + + static String nodeA() { + // node A could also be a function call after B created + return slowReq("A"); + } + + static String nodeB() { + /* + transform from: + return slowReq("B1") + slowReq("B2"); + */ + FutureTask future1 = new FutureTask<>(() -> slowReq("B1")); + FutureTask future2 = new FutureTask<>(() -> slowReq("B2")); + WispEngine.current().execute(future1); + WispEngine.current().execute(future2); + + try { + return future1.get() + future2.get(); + } catch (Exception e) { + throw new Error("exception during task execution"); + } + } + + // mimic an IO call + static String slowReq(String arg) { + SharedSecrets.getWispEngineAccess().sleep(100); // only block current coroutine + return arg + ":done\n"; + } +} diff --git a/test/com/alibaba/wisp/IoTest.java b/test/com/alibaba/wisp/IoTest.java new file mode 100644 index 0000000000000000000000000000000000000000..1c982687f811aa66a8bf2eb1b4f64aa77764bac7 --- /dev/null +++ b/test/com/alibaba/wisp/IoTest.java @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * @test + * @summary Test Wisp engine's NIO support + * @requires os.family == "linux" + * @run main/othervm -XX:+EnableCoroutine -Dcom.alibaba.wisp.transparentWispSwitch=true IoTest + */ + +import sun.misc.SharedSecrets; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.nio.ByteBuffer; +import java.nio.channels.SelectionKey; +import java.nio.channels.SocketChannel; +import java.util.Properties; + +public class IoTest { + + static Properties p; + static String socketAddr; + static { + p = java.security.AccessController.doPrivileged( + new java.security.PrivilegedAction() { + public Properties run() { + return System.getProperties(); + } + } + ); + socketAddr = (String)p.get("test.wisp.socketAddress"); + if (socketAddr == null) { + socketAddr = "www.example.com"; + } + } + + public static void main(String[] args) { + String result = http(); + + System.out.println(result); + + if (!result.startsWith("HTTP") || + !result.contains("Content-") || + !result.contains("Server:")) + throw new Error("protocol error"); + } + + static String http() { + String host = socketAddr; + + try { + SocketChannel ch = SocketChannel.open(); + ch.configureBlocking(false); + + connect(ch, InetAddress.getByName(host).getHostAddress()); + ByteBuffer bb = ByteBuffer.allocate(1000); + String request = "HEAD / HTTP/1.0\r\nHost:" + host + "\r\n\r\n"; + bb.put(request.getBytes()); + bb.flip(); + write(ch, bb); + bb.clear(); + read(ch, bb); + + return new String(bb.array(), 0, bb.remaining()); + + } catch (IOException e) { + throw new Error(e); + } + } + + static int read(SocketChannel ch, ByteBuffer bb) throws IOException { + int n, retry = 0; + while ((n = ch.read(bb)) == 0) { + if (retry++ > 3) + throw new Error("busy loop"); // make sure we're not in a busy loop + SharedSecrets.getWispEngineAccess().registerEvent(ch, SelectionKey.OP_READ); + SharedSecrets.getWispEngineAccess().park(-1); + } + return n; + } + + static int write(SocketChannel ch, ByteBuffer bb) throws IOException { + int n, retry = 0; + while ((n = ch.write(bb)) == 0) { + if (retry++ > 3) throw new Error("busy loop"); + SharedSecrets.getWispEngineAccess().registerEvent(ch, SelectionKey.OP_WRITE); + SharedSecrets.getWispEngineAccess().park(-1); + } + return n; + } + + static void connect(SocketChannel ch, String ip) throws IOException { + ch.connect(new InetSocketAddress(ip, 80)); + int retry = 0; + while (!ch.finishConnect()) { + if (retry++ > 3) throw new Error("busy loop"); + SharedSecrets.getWispEngineAccess().registerEvent(ch, SelectionKey.OP_CONNECT); + SharedSecrets.getWispEngineAccess().park(-1); + } + } +} diff --git a/test/com/alibaba/wisp/LoadClassInnerWispGuard.java b/test/com/alibaba/wisp/LoadClassInnerWispGuard.java new file mode 100644 index 0000000000000000000000000000000000000000..f1a4f6dd05bd1df900ef39dec1062f270aa1426b --- /dev/null +++ b/test/com/alibaba/wisp/LoadClassInnerWispGuard.java @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * @test + * @library /lib/testlibrary + * @summary verify there's no wisp class loaded after wisp initialize finished + * @requires os.family == "linux" + * @run main LoadClassInnerWispGuard + */ + +import com.alibaba.wisp.engine.WispEngine; + +import java.util.concurrent.*; + +import static jdk.testlibrary.Asserts.assertTrue; +import static jdk.testlibrary.Asserts.fail; + +public class LoadClassInnerWispGuard { + private static final String START = "LoadClassInnerWispGuard_START"; + private static final String END = "LoadClassInnerWispGuard_END"; + + public static void main(String[] args) throws Exception { + if (args.length == 0) { + driver(); + } else { + doSomeWispOperation(); + } + } + + /* + Here is a sample output from OutputAnalyzer: + + [Loaded java.util.concurrent.ArrayBlockingQueue from: ...images/j2sdk-image/jre/lib/rt.jar] + LoadClassInnerWispGuard_START + [Loaded LoadClassInnerWispGuard$PingPong from file:...classes/com/alibaba/wisp/] + LoadClassInnerWispGuard_END + + if class loading is happened during the test, + [Loaded com.alibaba.wisp.engine.WispEngine*...] will exist between + LoadClassInnerWispGuard_START and LoadClassInnerWispGuard_END + and we'll fail + */ + private static void driver() throws Exception { + ProcessBuilder pb = jdk.testlibrary.ProcessTools.createJavaProcessBuilder( + "-XX:+UseWisp2", "-Dcom.alibaba.wisp.allThreadAsWisp=false", "-verbose:class", + "-cp", System.getProperty("java.class.path"), + LoadClassInnerWispGuard.class.getName(), "1"); + jdk.testlibrary.OutputAnalyzer output = new jdk.testlibrary.OutputAnalyzer(pb.start()); + String out = output.getStdout(); + int off = out.indexOf(START); + for (String line : out.substring(off + START.length() + 1).split("\n")) { + if (line.startsWith(END)) break; + if (!line.startsWith("[Loaded")) continue; + if (line.contains(LoadClassInnerWispGuard.class.getName())) continue; + fail(line + "\n" + "wisp class loaded during runtime"); + } + } + + private static class TF implements ThreadFactory { + @Override + public Thread newThread(Runnable r) { + Thread thread = new Thread(r, "T"); + thread.setDaemon(true); + return thread; + } + } + + private static void doSomeWispOperation() throws Exception { + assertTrue(true); // preload jdk.testlibrary.Assert + Runnable r = () -> { /**/}; + Executors.newCachedThreadPool(new TF()).submit(r).get(); + WispEngine g = WispEngine.current(); + BlockingQueue q1 = new ArrayBlockingQueue<>(1); + BlockingQueue q2 = new ArrayBlockingQueue<>(1); + CountDownLatch latch = new CountDownLatch(2); + System.out.println(START); + // 1. simple case + g.submit(r).get(); + // 2. work stealing + g.submit(new PingPong(q1, q2, latch)); + g.submit(new PingPong(q2, q1, latch)); + q1.offer(1); // start + latch.await(); + System.out.println(END); + } + + static class PingPong implements Runnable { + final BlockingQueue pingQ; + final BlockingQueue pongQ; + final CountDownLatch latch; + + public PingPong(BlockingQueue pingQ, BlockingQueue pongQ, CountDownLatch latch) { + this.pingQ = pingQ; + this.pongQ = pongQ; + this.latch = latch; + } + + @Override + public void run() { + try { + for (int i = 0; i < 100000; i++) { + pingQ.poll(1, TimeUnit.HOURS); + pongQ.offer(1); + } + latch.countDown(); + } catch (Exception e) { + } + } + } +} diff --git a/test/com/alibaba/wisp/NormalExitTest.java b/test/com/alibaba/wisp/NormalExitTest.java new file mode 100644 index 0000000000000000000000000000000000000000..486c591af7d2cb6f010117532d026f19f266f140 --- /dev/null +++ b/test/com/alibaba/wisp/NormalExitTest.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * @test + * @summary Test the exit without exception + * @requires os.family == "linux" + * @library /lib/testlibrary + * @run main NormalExitTest + */ + +public class NormalExitTest { + public static void main(String[] args) throws Exception { + ProcessBuilder pb = jdk.testlibrary.ProcessTools.createJavaProcessBuilder("-XX:+UseWisp2", "-version"); + jdk.testlibrary.OutputAnalyzer output = new jdk.testlibrary.OutputAnalyzer(pb.start()); + output.shouldNotContain("IllegalArgumentException"); + } +} diff --git a/test/com/alibaba/wisp/ParkTest.java b/test/com/alibaba/wisp/ParkTest.java new file mode 100644 index 0000000000000000000000000000000000000000..eaa74296fe1c09603bcb9eeb6d1b479660b1936b --- /dev/null +++ b/test/com/alibaba/wisp/ParkTest.java @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * @test + * @summary Test Wisp engine park / unpark + * @requires os.family == "linux" + * @library /lib/testlibrary + * @run main/othervm -XX:+UseWisp2 ParkTest + */ + + +import com.alibaba.wisp.engine.WispEngine; +import com.alibaba.wisp.engine.WispTask; +import sun.misc.SharedSecrets; +import sun.misc.WispEngineAccess; + +import java.util.concurrent.FutureTask; +import java.util.concurrent.TimeUnit; + +import static jdk.testlibrary.Asserts.assertTrue; + +public class ParkTest { + public static void main(String[] args) throws Exception { + WispEngineAccess access = SharedSecrets.getWispEngineAccess(); + + WispTask[] task = new WispTask[1]; + + FutureTask ft = new FutureTask<>(() -> { + task[0] = access.getCurrentTask(); + long start, diff; + + start = System.currentTimeMillis(); + access.park(0); + + diff = System.currentTimeMillis() - start; + if (diff < 200 || diff > 220) + throw new Error("error test unpark by other thread"); + + + start = start + diff; + access.park(TimeUnit.MILLISECONDS.toNanos(200)); + diff = System.currentTimeMillis() - start; + + if (diff < 200 || diff > 220) + throw new Error("error test timed park"); + + + start = start + diff; + access.unpark(access.getCurrentTask()); + access.park(0); + diff = System.currentTimeMillis() - start; + if (diff > 20) + throw new Error("error test permitted park"); + + return true; + }); + + WispEngine.current().execute(ft); + + Thread unparkThread = new Thread(() -> { + access.sleep(200); + access.unpark(task[0]); + }); + unparkThread.start(); + + assertTrue(ft.get(5, TimeUnit.SECONDS)); + } +} diff --git a/test/com/alibaba/wisp/TestWispDetailCounter.java b/test/com/alibaba/wisp/TestWispDetailCounter.java new file mode 100644 index 0000000000000000000000000000000000000000..0e642ed42544505998cad9a34f7f81b42f025c23 --- /dev/null +++ b/test/com/alibaba/wisp/TestWispDetailCounter.java @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +import com.alibaba.management.WispCounterMXBean; + +import javax.management.MBeanServer; +import java.io.*; +import java.lang.management.ManagementFactory; +import java.lang.reflect.Method; +import java.net.ServerSocket; +import java.net.Socket; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +import static jdk.testlibrary.Asserts.assertTrue; + +/* @test + * @summary WispCounterMXBean unit test for Detail profile data + * @requires os.family == "linux" + * @library /lib/testlibrary + * @run main/othervm/timeout=2000 -XX:+UseWisp2 -Dcom.alibaba.wisp.config=/tmp/wisp.config -Dcom.alibaba.wisp.profile=true -Dcom.alibaba.wisp.enableProfileLog=true -Dcom.alibaba.wisp.logTimeInternalMillis=3000 TestWispDetailCounter + */ + +public class TestWispDetailCounter { + + public static void main(String[] args) throws Exception { + startNetServer(); + MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); + WispCounterMXBean mbean = null; + try { + mbean = ManagementFactory.newPlatformMXBeanProxy(mbs, + "com.alibaba.management:type=WispCounter", WispCounterMXBean.class); + } catch (IOException e) { + e.printStackTrace(); + } + + ExecutorService es = Executors.newFixedThreadPool(20); + int taskTotal = 40; + for (int i = 0; i < taskTotal; i++) { + // submit task + es.submit(() -> { + // do park/unpark + synchronized (TestWispDetailCounter.class) { + // do sleep + try { + Thread.sleep(10L); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + // do net IO + doNetIO(); + }); + } + + System.out.println(mbean.getRunningStates()); + System.out.println(mbean.getQueueLength()); + System.out.println(mbean.getNumberOfRunningTasks()); + // wait task complete + Thread.sleep(1_000L); + System.out.println(mbean.getCreateTaskCount()); + System.out.println(mbean.getCompleteTaskCount()); + System.out.println(mbean.getUnparkCount()); + assertTrue(mbean.getUnparkCount().stream().mapToLong(Long::longValue).sum() > 0); + System.out.println(mbean.getTotalBlockingTime()); + assertTrue(mbean.getTotalBlockingTime().stream().mapToLong(Long::longValue).sum() > 0); + System.out.println(mbean.getWaitSocketIOCount()); + System.out.println(mbean.getTotalWaitSocketIOTime()); + System.out.println(mbean.getEnqueueCount()); + assertTrue(mbean.getEnqueueCount().stream().mapToLong(Long::longValue).sum() > 0); + System.out.println(mbean.getTotalEnqueueTime()); + assertTrue(mbean.getTotalEnqueueTime().stream().mapToLong(Long::longValue).sum() > 0); + System.out.println(mbean.getExecutionCount()); + assertTrue(mbean.getExecutionCount().stream().mapToLong(Long::longValue).sum() > 0); + System.out.println(mbean.getTotalExecutionTime()); + assertTrue(mbean.getTotalExecutionTime().stream().mapToLong(Long::longValue).sum() > 0); + + es.submit(() -> { + try { + Thread.sleep(1000); + } catch (Exception e) { + } + }); + System.out.println(mbean.getNumberOfRunningTasks()); + assertTrue(mbean.getNumberOfRunningTasks().stream().mapToLong(Long::longValue).sum() > 0); + + // check log file exist + File file = new File("wisplog0.log"); + if (!file.exists()) { + assertTrue(false, "log file isn't generated"); + } + } + + private static ServerSocket ss; + private static final int PORT = 23000; + private static final int BUFFER_SIZE = 1024; + + private static void startNetServer() throws IOException { + ss = new ServerSocket(PORT); + Thread t = new Thread(() -> { + try { + while (true) { + Socket cs = ss.accept(); + InputStream is = cs.getInputStream(); + int r = is.read(new byte[BUFFER_SIZE]); + is.close(); + cs.close(); + } + } catch (IOException e) { + e.printStackTrace(); + } + }); + t.setDaemon(true); + t.start(); + } + + private static void doNetIO() { + try { + Socket so = new Socket("localhost", PORT); + OutputStream os = so.getOutputStream(); + os.write(new byte[BUFFER_SIZE]); + os.flush(); + os.close(); + } catch (IOException e) { + e.printStackTrace(); + throw new Error(e); + } + } +} diff --git a/test/com/alibaba/wisp/WispMonitorDataTest.java b/test/com/alibaba/wisp/WispMonitorDataTest.java new file mode 100644 index 0000000000000000000000000000000000000000..262cf2d87d887784e35eb8cfa061c098688506c7 --- /dev/null +++ b/test/com/alibaba/wisp/WispMonitorDataTest.java @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +import com.alibaba.management.WispCounterMXBean; +import com.alibaba.wisp.engine.WispCounter; + +import javax.management.MBeanServer; +import java.io.*; +import java.lang.management.ManagementFactory; +import java.net.ServerSocket; +import java.net.Socket; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.*; + +import sun.misc.JavaLangAccess; +import sun.misc.SharedSecrets; + +import java.lang.reflect.Field; + +import static jdk.testlibrary.Asserts.assertTrue; + +/* @test + * @summary WispCounterMXBean unit test for Detail profile data using the API with the specified WispCarrier + * @requires os.family == "linux" + * @library /lib/testlibrary + * @run main/othervm/timeout=2000 -XX:+UseWisp2 -Dcom.alibaba.wisp.config=/tmp/wisp.config -Dcom.alibaba.wisp.profile=true WispMonitorDataTest + */ + +public class WispMonitorDataTest { + + public static void main(String[] args) throws Exception { + startNetServer(); + MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); + WispCounterMXBean mbean = null; + try { + mbean = ManagementFactory.newPlatformMXBeanProxy(mbs, + "com.alibaba.management:type=WispCounter", WispCounterMXBean.class); + } catch (IOException e) { + e.printStackTrace(); + } + + ExecutorService es = Executors.newFixedThreadPool(20); + int taskTotal = 40; + for (int i = 0; i < taskTotal; i++) { + // submit task + es.submit(() -> { + // do park/unpark + synchronized (WispMonitorDataTest.class) { + // do sleep + try { + Thread.sleep(10L); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + // do net IO + doNetIO(); + }); + } + Thread.sleep(1_000L); + + Class clazz = Class.forName("com.alibaba.wisp.engine.WispEngine"); + Field field = clazz.getDeclaredField("carrierThreads"); + field.setAccessible(true); + Set set = (Set) field.get(null); + JavaLangAccess JLA = SharedSecrets.getJavaLangAccess(); + + for (Thread thread : set) { + WispCounter wispdata = mbean.getWispCounter(thread.getId()); + if (wispdata != null) { + System.out.println("WispTask is " + thread.getId()); + System.out.println(wispdata.getCompletedTaskCount()); + System.out.println(wispdata.getTotalExecutionTime()); + System.out.println(wispdata.getExecutionCount()); + System.out.println(wispdata.getTotalEnqueueTime()); + System.out.println(wispdata.getEnqueueCount()); + } + } + } + + private static ServerSocket ss; + private static final int PORT = 23000; + private static final int BUFFER_SIZE = 1024; + + private static void startNetServer() throws IOException { + ss = new ServerSocket(PORT); + Thread t = new Thread(() -> { + try { + while (true) { + Socket cs = ss.accept(); + InputStream is = cs.getInputStream(); + int r = is.read(new byte[BUFFER_SIZE]); + is.close(); + cs.close(); + } + } catch (IOException e) { + e.printStackTrace(); + } + }); + t.setDaemon(true); + t.start(); + } + + private static void doNetIO() { + try { + Socket so = new Socket("localhost", PORT); + OutputStream os = so.getOutputStream(); + os.write(new byte[BUFFER_SIZE]); + os.flush(); + os.close(); + } catch (IOException e) { + e.printStackTrace(); + throw new Error(e); + } + } +} diff --git a/test/com/alibaba/wisp/boot/UnsafeDependencyBugTest.java b/test/com/alibaba/wisp/boot/UnsafeDependencyBugTest.java new file mode 100644 index 0000000000000000000000000000000000000000..bd62715b5b9ee8d0423e10c7a6e9eb9720424ede --- /dev/null +++ b/test/com/alibaba/wisp/boot/UnsafeDependencyBugTest.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * @test + * @library /lib/testlibrary + * @summary test bug fix of SharedSecrets and Unsafe class initializer circular dependency + * @requires os.family == "linux" + * @run main UnsafeDependencyBugTest 10 + */ + + +import java.io.File; +import java.util.concurrent.TimeUnit; + +import static jdk.testlibrary.Asserts.assertTrue; + +/** + * We need thousand times to reproduce the DEADLOCK. Don't spend too much time here.. + * We already add svt test ajdk_svt/wispTest to ensure the DEADLOCK already solved. + */ +public class UnsafeDependencyBugTest { + + public static void main(String[] args) throws Exception { + long start = System.currentTimeMillis(); + int i; + for (i = 0; System.currentTimeMillis() - start < Integer.valueOf(args[0]) * 1000; i++) { + runLauncherWithWisp(); + } + System.out.println("tested " + i + " times"); + } + + private static void runLauncherWithWisp() throws Exception { + Process p = new ProcessBuilder(System.getProperty("java.home") + "/bin/java", "-XX:+EnableCoroutine") + .redirectErrorStream(true) + .redirectOutput(new File("/dev/null")) + .start(); + + assertTrue(p.waitFor(4, TimeUnit.SECONDS)); + } +} diff --git a/test/com/alibaba/wisp/bug/CancelTimerAndSleepTest.java b/test/com/alibaba/wisp/bug/CancelTimerAndSleepTest.java new file mode 100644 index 0000000000000000000000000000000000000000..854a4fb1322d1208218ae6965151d6c10ec06ebd --- /dev/null +++ b/test/com/alibaba/wisp/bug/CancelTimerAndSleepTest.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * @test + * @summary Test sleep + * @requires os.family == "linux" + * @run main/othervm -XX:+EnableCoroutine -Dcom.alibaba.wisp.transparentWispSwitch=true CancelTimerAndSleepTest + */ + +import sun.misc.SharedSecrets; +import sun.misc.WispEngineAccess; + +import java.util.concurrent.TimeUnit; + +public class CancelTimerAndSleepTest { + + public static void main(String[] args) throws Exception { + WispEngineAccess access = SharedSecrets.getWispEngineAccess(); + + access.addTimer(System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(10)); + access.cancelTimer(); + + long now = System.currentTimeMillis(); + Thread.sleep(200); + long elapsed = System.currentTimeMillis() - now; + if (Math.abs(elapsed - 200) > 10) + throw new Error("elapsed = " + elapsed); + } +} diff --git a/test/com/alibaba/wisp/bug/Id2TaskMapLeakTest.java b/test/com/alibaba/wisp/bug/Id2TaskMapLeakTest.java new file mode 100644 index 0000000000000000000000000000000000000000..7d3aaeb7acb92ad8507bf7ab9d3d109564770863 --- /dev/null +++ b/test/com/alibaba/wisp/bug/Id2TaskMapLeakTest.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * @test + * @library /lib/testlibrary + * @summary Test for thread WispTask leak + * @requires os.family == "linux" + * @run main/othervm -XX:+EnableCoroutine -Dcom.alibaba.wisp.transparentWispSwitch=true Id2TaskMapLeakTest + */ + +import com.alibaba.wisp.engine.WispEngine; +import com.alibaba.wisp.engine.WispTask; + +import java.lang.reflect.Field; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; + +import static jdk.testlibrary.Asserts.assertEQ; + +public class Id2TaskMapLeakTest { + public static void main(String[] args) throws Exception { + Field f = WispTask.class.getDeclaredField("id2Task"); + f.setAccessible(true); + Map map = (Map) f.get(null); + + int size0 = map.size(); + + AtomicInteger sizeHolder = new AtomicInteger(); + + new Thread(() -> { + WispEngine.current(); + sizeHolder.set(map.size()); + }).start(); + Thread.sleep(20); // ensure thread exit; + + assertEQ(size0 + 1, sizeHolder.get()); + assertEQ(size0, map.size()); + } +} diff --git a/test/com/alibaba/wisp/bug/ReleaseWispSocketAndExitTest.java b/test/com/alibaba/wisp/bug/ReleaseWispSocketAndExitTest.java new file mode 100644 index 0000000000000000000000000000000000000000..79c5ebd4808b24de89ecbd7b182b2b2af22c95e6 --- /dev/null +++ b/test/com/alibaba/wisp/bug/ReleaseWispSocketAndExitTest.java @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * @test + * @summary handle this scenario: + * 1. task A fetch a socket S and release it. + * 2. task B get the socket S and block on IO. + * 3. task A exit and clean S's event, now B waiting forever... + * @requires os.family == "linux" + * @run main/othervm -XX:+EnableCoroutine -Dcom.alibaba.wisp.transparentWispSwitch=true ReleaseWispSocketAndExitTest + */ + + +import com.alibaba.wisp.engine.WispEngine; +import sun.misc.SharedSecrets; + +import java.io.InputStream; +import java.io.OutputStream; +import java.net.ServerSocket; +import java.net.Socket; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +public class ReleaseWispSocketAndExitTest { + static byte buf[] = new byte[1000]; + + public static void main(String[] args) throws Exception { + CountDownLatch latch = new CountDownLatch(1); + CountDownLatch listen = new CountDownLatch(1); + CountDownLatch finish = new CountDownLatch(3); + + WispEngine.dispatch(() -> { + try { + ServerSocket sso = new ServerSocket(51243); + listen.countDown(); + Socket so; + so = sso.accept(); + InputStream is = so.getInputStream(); + OutputStream os = so.getOutputStream(); + int n; + while ((n = is.read(buf, 0, 4)) == 4) { + long l = Long.valueOf(new String(buf, 0, n)); + System.out.println("l = " + l); + SharedSecrets.getWispEngineAccess().sleep(l); + os.write(buf, 0, n); + } + } catch (Exception e) { + throw new Error(); + } + finish.countDown(); + }); + + listen.await(); + Socket so = new Socket("127.0.0.1", 51243); + + WispEngine.dispatch(() -> { + try { + InputStream is = so.getInputStream(); + OutputStream os = so.getOutputStream(); + os.write("0000".getBytes()); + System.out.println("======"); + latch.await(); + System.out.println("======2"); + } catch (Exception e) { + throw new Error(); + } + finish.countDown(); + }); + + WispEngine.dispatch(() -> { + try { + InputStream is = so.getInputStream(); + OutputStream os = so.getOutputStream(); + + os.write("0100".getBytes()); + System.out.println("------"); + latch.countDown(); + is.read(buf); + is.read(buf);// blocked + System.out.println("------2"); + + so.close(); + } catch (Exception e) { + throw new Error(); + } + finish.countDown(); + }); + + finish.await(5, TimeUnit.SECONDS); + System.out.println("passed"); + } + +} diff --git a/test/com/alibaba/wisp/bug/ResetTaskCancelTimerBugTest.java b/test/com/alibaba/wisp/bug/ResetTaskCancelTimerBugTest.java new file mode 100644 index 0000000000000000000000000000000000000000..ad048e205a801b8b576ac3b09840b89d76815419 --- /dev/null +++ b/test/com/alibaba/wisp/bug/ResetTaskCancelTimerBugTest.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * @test + * @library /lib/testlibrary + * @summary test reset task doesn't cancel the current task's timer unexpectedly. + * @requires os.family == "linux" + * @run main/othervm -XX:+EnableCoroutine -Dcom.alibaba.wisp.transparentWispSwitch=true ResetTaskCancelTimerBugTest + */ + + + +import com.alibaba.wisp.engine.WispEngine; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; + +import static jdk.testlibrary.Asserts.assertTrue; + + +public class ResetTaskCancelTimerBugTest { + public static void main(String[] args) throws Exception { + AtomicReference executor = new AtomicReference<>(); + AtomicBoolean submitDone = new AtomicBoolean(); + CountDownLatch latch = new CountDownLatch(1); + + new Thread(() -> { + executor.set(WispEngine.current()); + while (!submitDone.get()) {} + // 4. go sleep, cause there's a pending external WispTask create operation, + // we should create the task fist. + // WispCarrier.runTaskInternal() -> WispTask.reset() -> WispCarrier.cancelTimer() + // will remove current task's timer, the sleep() could never be wakened. + try { + Thread.sleep(500); + } catch (InterruptedException e) { + e.printStackTrace(); + } + latch.countDown(); + }).start(); + while (executor.get() == null) {} + + // 1. create a thread(also created WispCarrier A) + executor.get().execute(() -> {}); + // 2. request create WispTask in the WispCarrier A + submitDone.set(true); + // 3. telling WispCarrier A's it's time to sleep + + // 5. wait sleep done. If latch.countDown() is not invoked inner 10 seconds, TEST FAILURE. + assertTrue(latch.await(10, TimeUnit.SECONDS)); + } +} diff --git a/test/com/alibaba/wisp/bug/SelectorInitCriticalTest.java b/test/com/alibaba/wisp/bug/SelectorInitCriticalTest.java new file mode 100644 index 0000000000000000000000000000000000000000..3d1cc8a4c9bf5f3b35fea4678ffe03e241b7043d --- /dev/null +++ b/test/com/alibaba/wisp/bug/SelectorInitCriticalTest.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * @test + * @library /lib/testlibrary + * @summary Test the fix to NPE issue caused by unexpected co-routine yielding on synchronized(lock) in SelectorProvider.provider() during initialization of WispCarrier + * @requires os.family == "linux" + * @run main/othervm -XX:+EnableCoroutine -Dcom.alibaba.wisp.transparentWispSwitch=true -XX:+UseWispMonitor SelectorInitCriticalTest + */ + + +import java.lang.reflect.Field; +import java.nio.channels.spi.SelectorProvider; +import java.util.concurrent.CountDownLatch; + +public class SelectorInitCriticalTest { + public static void main(String[] args) throws Exception { + Field f = SelectorProvider.class.getDeclaredField("lock"); + f.setAccessible(true); + Object selectorProviderLock = f.get(null); + CountDownLatch latch = new CountDownLatch(1); + + Thread t = new Thread(latch::countDown); + + synchronized (selectorProviderLock) { + t.start(); + // Holding selectorProviderLock for a while which will eventually blocks the initialization of t' WispCarrier + Thread.sleep(100); + } + + latch.await(); + + } +} diff --git a/test/com/alibaba/wisp/bug/TestThreadStackTrace.sh b/test/com/alibaba/wisp/bug/TestThreadStackTrace.sh new file mode 100644 index 0000000000000000000000000000000000000000..21797fb33da791cc8ceac8596f33dac02df72e67 --- /dev/null +++ b/test/com/alibaba/wisp/bug/TestThreadStackTrace.sh @@ -0,0 +1,149 @@ +#!/usr/bin/env bash + +# +# Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. Alibaba designates this +# particular file as subject to the "Classpath" exception as provided +# by Oracle in the LICENSE file that accompanied this code. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# + +# +# @test +# @summary test Thread.getStackTrace() in wisp transparentAsync model +# @requires os.family == "linux" +# @run shell TestThreadStackTrace.sh +# + +if [ "${TESTSRC}" = "" ] +then + TESTSRC=${PWD} + echo "TESTSRC not set. Using "${TESTSRC}" as default" +fi +echo "TESTSRC=${TESTSRC}" +# determine platform dependant variables +OS=`uname -s` +case ${OS} in + Linux) + FS=/ + ;; + *) + exit 1 + ;; +esac + +JAVA=${TESTJAVA}${FS}bin${FS}java +JAVAC=${TESTJAVA}${FS}bin${FS}javac +TEST_CLASS=TmpThreadStackTrace +TEST_SOURCE=${TEST_CLASS}.java + +################################################################################### + + +cat > ${TESTCLASSES}${FS}$TEST_SOURCE << EOF +import com.alibaba.wisp.engine.WispEngine; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.atomic.AtomicBoolean; +import sun.misc.SharedSecrets; +import java.util.concurrent.Executors; +import java.util.concurrent.*; + +public class TmpThreadStackTrace { + + public static Thread runningCoroutine; + + public static void foo() throws Exception { + ExecutorService executor = Executors.newCachedThreadPool(); + executor.execute(() -> { + try { + runningCoroutine = Thread.currentThread(); + Thread.sleep(2_000L); + } catch (InterruptedException e) { + e.printStackTrace(); + } + }); + AtomicBoolean result = new AtomicBoolean(true); + CountDownLatch done = new CountDownLatch(1); + Thread mainThread = Thread.currentThread(); + Thread[] ts = new Thread[1]; + ts[0] = new Thread(() -> { + System.out.println("in managed!"); + try { + if (ts[0].getStackTrace().length == 0 || mainThread.getStackTrace().length == 0 + || Thread.currentThread().getStackTrace().length == 0) { + result.set(false); + } + runningCoroutine.getStackTrace(); + result.set(false); + } catch (Exception e) { + if (!(e instanceof UnsupportedOperationException)) { + result.set(false); + } + } finally { + done.countDown(); + } + }, "test-thread"); + ts[0].start(); + done.await(); + System.out.println("in main"); + executor.shutdown(); + if (!result.get()) { + throw new Error("test failure"); + } + } + + public static void main(String[] args) throws Exception { + final CountDownLatch latch = new CountDownLatch(1); + WispEngine.current().execute(() -> { + for (int i = 0; i < 5; i++) { + System.out.println(i); + Thread.currentThread().getStackTrace(); + } + latch.countDown(); + }); + latch.await(); + foo(); + System.out.println("done"); + System.exit(0); + } +} +EOF + +# Do compilation +${JAVAC} -cp ${TESTCLASSES} -d ${TESTCLASSES} ${TESTCLASSES}${FS}$TEST_SOURCE >> /dev/null 2>&1 +if [ $? != '0' ] +then + printf "Failed to compile ${TESTCLASSES}${FS}${TEST_SOURCE}" + exit 1 +fi + +#run +${JAVA} -XX:+PrintSafepointStatistics -XX:PrintSafepointStatisticsCount=1 -XX:+UseWisp2 -Dcom.alibaba.wisp.carrierEngines=1 -cp ${TESTCLASSES} ${TEST_CLASS} > output.txt 2>&1 +cat output.txt + +function assert() +{ + line=`cat output.txt | grep ThreadDump | wc -l` + echo $line + if [[ $line -eq "2" ]]; then + echo "success" + else + echo "failure" + exit -1 + fi +} + +assert diff --git a/test/com/alibaba/wisp/bug/ThreadLockTest.java b/test/com/alibaba/wisp/bug/ThreadLockTest.java new file mode 100644 index 0000000000000000000000000000000000000000..308fe69c162724b99030239739dfe2ffc6b482da --- /dev/null +++ b/test/com/alibaba/wisp/bug/ThreadLockTest.java @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * @test + * @summary Test fix of WispCarrier block on Thread.class lock + * @requires os.family == "linux" + * @run main/othervm -XX:-UseBiasedLocking -XX:+EnableCoroutine -XX:+UseWispMonitor -XX:SyncKnobs="ReportSettings=1:QMode=1" -Dcom.alibaba.wisp.transparentWispSwitch=true ThreadLockTest + */ + +import com.alibaba.wisp.engine.WispEngine; + +import java.lang.reflect.Method; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.atomic.AtomicReference; + +public class ThreadLockTest { + public static void main(String[] args) throws Exception { + + CountDownLatch done = new CountDownLatch(1); + AtomicReference es = new AtomicReference<>(null); + + synchronized (Thread.class) { + new Thread(() -> { + es.set(WispEngine.current()); + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + }, "ThreadIdGeneratorLockTest").start(); + + Thread.sleep(100); // make sure "ThreadIdGeneratorLockTest" already sleeping + + /* + if ((SyncFlags & 16) == 0 && nxt == NULL && _EntryList == NULL) { + // Try to assume the role of responsible thread for the monitor. + // CONSIDER: ST vs CAS vs { if (Responsible==null) Responsible=Self } + Atomic::cmpxchg_ptr (Self, &_Responsible, NULL) ; + } + ---------------- + if (_Responsible == Self || (SyncFlags & 1)) { + use timeout park + } + add a waiter, let _Responsible != Self + */ + new Thread(() -> { + try { + Method m = Thread.class.getDeclaredMethod("nextThreadNum"); + m.setAccessible(true); + m.invoke(null); + } catch (ReflectiveOperationException e) { + throw new Error(e); + } + }, "waiter").start(); + Thread.sleep(100); // make sure adding waiter is done + + + es.get().execute(() -> { // give an name "A" + Thread.currentThread(); // before fix, we'll hang here + done.countDown(); + }); + + // objectMonitor::EnterI generally puts waiter to head, now _cxq is: + // "A" (os_park) --> "A" (wisp_park) --> "waiter" + // set QMode=1 to reverse the queue + // _Entry_list is: + // "waiter" --> "A" (wisp_park) --> "A" (os_park) + Thread.sleep(100); // still hold lock 100 ms, make sure the lsat wisp blocked + } + // release: + // 1. wisp unpark "waiter" + // 2. wisp unpark "A" (wisp_park) (just after waiter ends) + // now "ThreadIdGeneratorLockTest" Thread is on os_park, test failed! + + done.await(); + } +} diff --git a/test/com/alibaba/wisp/bug/ThreadPoolFastShutdownBugTest.java b/test/com/alibaba/wisp/bug/ThreadPoolFastShutdownBugTest.java new file mode 100644 index 0000000000000000000000000000000000000000..35442a45977cce5c4cbf4af083f12dd06b20fee5 --- /dev/null +++ b/test/com/alibaba/wisp/bug/ThreadPoolFastShutdownBugTest.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * @test + * @summary test shutdown a thread pool which contains non-fully-started thread + * @requires os.family == "linux" + * @run main/othervm -XX:+EnableCoroutine -Dcom.alibaba.wisp.transparentWispSwitch=true ThreadPoolFastShutdownBugTest + */ + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +public class ThreadPoolFastShutdownBugTest { + public static void main(String[] args) { + ExecutorService executorService = Executors.newSingleThreadExecutor(); + executorService.submit(new InitThread()); + executorService.shutdown(); + } + + static class InitThread implements Runnable { + + @Override + public void run() { + + } + } +} diff --git a/test/com/alibaba/wisp/bug/WispSelectorReadyOpsTest.java b/test/com/alibaba/wisp/bug/WispSelectorReadyOpsTest.java new file mode 100644 index 0000000000000000000000000000000000000000..4d45a33845fd3b72b7d61836a86ccf2b6273e7ac --- /dev/null +++ b/test/com/alibaba/wisp/bug/WispSelectorReadyOpsTest.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * @test + * @summary ensure nio program call SelectionKey.is{}able() and got correct result. + * @requires os.family == "linux" + * @run main/othervm -XX:+EnableCoroutine -Dcom.alibaba.wisp.transparentWispSwitch=true WispSelectorReadyOpsTest + */ + +import java.net.InetSocketAddress; +import java.net.Socket; +import java.nio.channels.SelectionKey; +import java.nio.channels.Selector; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; + +public class WispSelectorReadyOpsTest { + public static void main(String[] args) throws Exception { + Selector selector = Selector.open(); + ServerSocketChannel sch = ServerSocketChannel.open(); + sch.configureBlocking(false); + sch.bind(new InetSocketAddress(54442)); + sch.register(selector, sch.validOps()); + + Socket so = new Socket("127.0.0.1", 54442); + + selector.selectNow(); + SelectionKey sk = (SelectionKey) selector.selectedKeys().toArray()[0]; + if (!sk.isAcceptable()) { + throw new Error("fail"); + } + + SocketChannel sc = sch.accept(); + sc.configureBlocking(false); + sc.register(selector, sc.validOps()); + + so.getOutputStream().write("123".getBytes()); + + selector.selectedKeys().clear(); + selector.selectNow(); + sk = (SelectionKey) selector.selectedKeys().toArray()[0]; + if (!sk.isReadable()) { + throw new Error("fail"); + } + } + +} diff --git a/test/com/alibaba/wisp/bug/WispSocketLeakWhenConnectTimeoutTest.java b/test/com/alibaba/wisp/bug/WispSocketLeakWhenConnectTimeoutTest.java new file mode 100644 index 0000000000000000000000000000000000000000..4a5d6c61499a7f9b35fbd40ceb4aef62af96a319 --- /dev/null +++ b/test/com/alibaba/wisp/bug/WispSocketLeakWhenConnectTimeoutTest.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * @test + * @library /lib/testlibrary + * @summary test the fix to fd leakage when socket connect timeout + * @requires os.family == "linux" + * @run main/othervm -XX:+EnableCoroutine -Dcom.alibaba.wisp.transparentWispSwitch=true WispSocketLeakWhenConnectTimeoutTest + */ + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.net.SocketTimeoutException; + +import static jdk.testlibrary.Asserts.assertTrue; + +public class WispSocketLeakWhenConnectTimeoutTest { + public static void main(String[] args) throws IOException { + Socket so = new Socket(); + boolean timeout = false; + try { + so.connect(new InetSocketAddress("www.example.com", 80), 5); + } catch (SocketTimeoutException e) { + assertTrue(so.isClosed()); + timeout = true; + } + + assertTrue(timeout, "SocketTimeoutException should been thrown"); + } +} diff --git a/test/com/alibaba/wisp/close/WispDestroyTest.java b/test/com/alibaba/wisp/close/WispDestroyTest.java new file mode 100644 index 0000000000000000000000000000000000000000..352f819a3422643bd4448135a6c499bd8db37807 --- /dev/null +++ b/test/com/alibaba/wisp/close/WispDestroyTest.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * @test + * @summary Test WispCarrier's destroy + * @requires os.family == "linux" + * @run main/othervm -XX:+EnableCoroutine -Dcom.alibaba.wisp.transparentWispSwitch=true WispDestroyTest + */ + +import com.alibaba.wisp.engine.WispTask; +import sun.misc.SharedSecrets; + +import java.lang.reflect.Field; +import java.util.concurrent.CountDownLatch; + +public class WispDestroyTest { + + static Object e; + + public static void main(String[] args) throws Exception { + CountDownLatch l = new CountDownLatch(1); + + new Thread(() -> { + try { + Field f = WispTask.class.getDeclaredField("carrier"); + f.setAccessible(true); + e = f.get(SharedSecrets.getJavaLangAccess().getWispTask(Thread.currentThread())); + l.countDown(); + } catch (Exception e) { + e.printStackTrace(); + } + }).start(); + + l.await(); + + Thread.sleep(1000); // ensure Thread.exit() executed + + + Field terminated = e.getClass().getDeclaredField("terminated"); + terminated.setAccessible(true); + if (!terminated.getBoolean(e)) throw new Error("resource leak!"); + } +} diff --git a/test/com/alibaba/wisp/env/CtxClassLoaderIsolateTest.java b/test/com/alibaba/wisp/env/CtxClassLoaderIsolateTest.java new file mode 100644 index 0000000000000000000000000000000000000000..683ca5622c61d5ee588e9c28725c00a5d49bf5c5 --- /dev/null +++ b/test/com/alibaba/wisp/env/CtxClassLoaderIsolateTest.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * @test + * @library /lib/testlibrary + * @summary Verify the context class loader isolation per co-routine + * @requires os.family == "linux" + * @run main/othervm -XX:+EnableCoroutine -Dcom.alibaba.wisp.transparentWispSwitch=true CtxClassLoaderIsolateTest + */ + + +import com.alibaba.wisp.engine.WispEngine; + +import static jdk.testlibrary.Asserts.assertTrue; + +public class CtxClassLoaderIsolateTest { + public static void main(String[] args) throws Exception { + ClassLoader loader0 = Thread.currentThread().getContextClassLoader(); + WispEngine.dispatch(() -> Thread.currentThread().setContextClassLoader(new ClassLoader() { + @Override + public Class loadClass(String name) throws ClassNotFoundException { + return super.loadClass(name); + } + })); + + assertTrue(Thread.currentThread().getContextClassLoader() == loader0); + } +} diff --git a/test/com/alibaba/wisp/io/BlockingAccept2Test.java b/test/com/alibaba/wisp/io/BlockingAccept2Test.java new file mode 100644 index 0000000000000000000000000000000000000000..05adaabcb37ea01728fc45f7f1436d1cc0d2d8d2 --- /dev/null +++ b/test/com/alibaba/wisp/io/BlockingAccept2Test.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * @test + * @library /lib/testlibrary + * @summary test blocking accept + * @requires os.family == "linux" + * @run main/othervm -Dcom.alibaba.wisp.carrierEngines=1 -XX:+UseWisp2 BlockingAccept2Test + */ + +import java.net.InetSocketAddress; +import java.net.Socket; +import java.nio.channels.ServerSocketChannel; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +public class BlockingAccept2Test { + public static void main(String[] args) throws Exception { + CountDownLatch latch = new CountDownLatch(1); + CountDownLatch latch2 = new CountDownLatch(1); + Thread t = new Thread(() -> { + try { + latch.await(1, TimeUnit.SECONDS); + Thread.sleep(200); + Socket s = new Socket(); + s.connect(new InetSocketAddress(12388)); + latch2.await(1, TimeUnit.SECONDS); + s.close(); + Thread.sleep(200); + s = new Socket(); + s.connect(new InetSocketAddress(12388)); + } catch (Exception e) { + } + }); + t.start(); + ServerSocketChannel ssc = ServerSocketChannel.open(); + ssc.bind(new InetSocketAddress(12388)); + latch.countDown(); + ssc.accept(); + latch2.countDown(); + ssc.accept(); + } +} diff --git a/test/com/alibaba/wisp/io/BlockingAcceptTest.java b/test/com/alibaba/wisp/io/BlockingAcceptTest.java new file mode 100644 index 0000000000000000000000000000000000000000..c6291ff9efb33054c9a93932a57b5deecd18a58c --- /dev/null +++ b/test/com/alibaba/wisp/io/BlockingAcceptTest.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * @test + * @library /lib/testlibrary + * @summary test blocking accept + * @requires os.family == "linux" + * @run main/othervm -XX:ActiveProcessorCount=4 -XX:+EnableCoroutine -Dcom.alibaba.wisp.transparentWispSwitch=true -XX:+UseWispMonitor BlockingAcceptTest + * @run main/othervm -XX:ActiveProcessorCount=4 -XX:+UseWisp2 BlockingAcceptTest + */ + +import java.net.InetSocketAddress; +import java.net.Socket; +import java.nio.channels.ServerSocketChannel; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +public class BlockingAcceptTest { + public static void main(String[] args) throws Exception { + CountDownLatch latch = new CountDownLatch(1); + Thread t = new Thread(() -> { + try { + latch.await(1, TimeUnit.SECONDS); + Thread.sleep(200); + Socket s = new Socket(); + s.connect(new InetSocketAddress(12388)); + } catch (Exception e) { + } + }); + t.start(); + ServerSocketChannel ssc = ServerSocketChannel.open(); + ssc.bind(new InetSocketAddress(12388)); + latch.countDown(); + ssc.accept(); + } +} diff --git a/test/com/alibaba/wisp/io/CreateFdOnDemandTest.java b/test/com/alibaba/wisp/io/CreateFdOnDemandTest.java new file mode 100644 index 0000000000000000000000000000000000000000..29f6239a35af18b8a3b7e67c50ff142224d5e0a8 --- /dev/null +++ b/test/com/alibaba/wisp/io/CreateFdOnDemandTest.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * @test + * @library /lib/testlibrary + * @summary Test fix of unconnected Socket fd leak. + * @requires os.family == "linux" + * @run main/othervm -XX:+EnableCoroutine -Dcom.alibaba.wisp.transparentWispSwitch=true CreateFdOnDemandTest + */ + +import java.io.File; +import java.net.DatagramSocket; +import java.net.ServerSocket; +import java.net.Socket; + +import com.alibaba.wisp.engine.WispEngine; + +import static jdk.testlibrary.Asserts.assertEQ; +import static jdk.testlibrary.Asserts.assertTrue; + +public class CreateFdOnDemandTest { + public static void main(String[] args) throws Exception { + + assertEQ(countFd(), countFd()); + Socket so = new Socket(); + so.setReuseAddress(true); + so.close(); + ServerSocket sso = new ServerSocket(); + sso.setReuseAddress(true); + sso.close(); + DatagramSocket ds = new DatagramSocket(null); + ds.setReuseAddress(true); + ds.close(); + + + final int nfd0 = countFd(); + so = new Socket(); + assertEQ(countFd(), nfd0); + sso = new ServerSocket(); + assertEQ(countFd(), nfd0); + ds = new DatagramSocket(null); + assertTrue(WispEngine.transparentWispSwitch() && countFd() == nfd0); // if -Dcom.alibaba.wisp.transparentWispSwitch=false, fail + + so.setReuseAddress(true); + assertEQ(countFd(), nfd0 + 1); + sso.setReuseAddress(true); + assertEQ(countFd(), nfd0 + 2); + ds.setReuseAddress(true); + assertEQ(countFd(), nfd0 + 3); + + so.close(); + assertEQ(countFd(), nfd0 + 2); + sso.close(); + assertEQ(countFd(), nfd0 + 1); + ds.close(); + assertEQ(countFd(), nfd0); + } + + private static int countFd() { + File f = new File("/proc/self/fd"); + return f.list().length; + } +} diff --git a/test/com/alibaba/wisp/io/DatagramSocketTest.java b/test/com/alibaba/wisp/io/DatagramSocketTest.java new file mode 100644 index 0000000000000000000000000000000000000000..572ca8a79d84bcc207ee571e2c0a349fc54f8771 --- /dev/null +++ b/test/com/alibaba/wisp/io/DatagramSocketTest.java @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * @test + * @library /lib/testlibrary + * @summary Test WispCarrier's DatagramSocket, InitialDirContext use dup socket to query dns. + * @requires os.family == "linux" + * @run main/othervm -XX:+EnableCoroutine -Dcom.alibaba.wisp.transparentWispSwitch=true DatagramSocketTest + */ + + +import com.alibaba.wisp.engine.WispEngine; + +import javax.naming.directory.InitialDirContext; +import java.io.IOException; +import java.net.*; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import static jdk.testlibrary.Asserts.*; + +public class DatagramSocketTest { + + static CountDownLatch latch = new CountDownLatch(10); + + public static void main(String[] args) throws Exception { + for (int i = 0; i < 10; i++) { + WispEngine.current().execute(DatagramSocketTest::foo); + } + + latch.await(5, TimeUnit.SECONDS); + testSendAndReceive(); + } + + static public void foo() { + try { + InitialDirContext dirCtx = new InitialDirContext(); + System.out.println(dirCtx.getAttributes("dns:/www.tmall.com")); + } catch (Throwable e) { + throw new Error("query dns error"); + } + latch.countDown(); + } + + static public void testSendAndReceive() throws Exception { + CountDownLatch count = new CountDownLatch(1); + InetAddress host = InetAddress.getByName("localhost"); + int port = 9527; + DatagramSocket so = new DatagramSocket(port); + so.setSoTimeout(1_000); + final int loop = 1024; + Thread receiver = new Thread(() -> { + int received = 0; + try { + byte[] buf = new byte[1024 * 32]; + DatagramPacket dp = new DatagramPacket(buf, buf.length); + count.countDown(); + for (int i = 0; i < loop; i++) { + received++; + so.receive(dp); + } + } catch (SocketTimeoutException e) { + e.printStackTrace(); + System.out.println("received packets: " + received); + // We at least received 64 packets before timeout + assertTrue(received > 64); + } catch (IOException e) { + throw new Error(e); + } + }); + receiver.start(); + count.await(); + for (int i = 0; i < loop; i++) { + byte[] buf = new byte[1024 * 32]; + DatagramPacket dp = new DatagramPacket(buf, buf.length, host, port); + so.send(dp); + } + receiver.join(); + } +} diff --git a/test/com/alibaba/wisp/io/GlobalPollerTest.java b/test/com/alibaba/wisp/io/GlobalPollerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..443d7284b7745d60d32fbd6adf807e008e73fc98 --- /dev/null +++ b/test/com/alibaba/wisp/io/GlobalPollerTest.java @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * @test + * @library /lib/testlibrary + * @summary Test for Global Poller + * @requires os.family == "linux" + * @run main/othervm -XX:+EnableCoroutine -Dcom.alibaba.transparentAsync=true GlobalPollerTest +*/ + +import com.alibaba.wisp.engine.WispTask; +import sun.misc.SharedSecrets; +import sun.misc.WispEngineAccess; +import sun.nio.ch.SelChImpl; + +import java.lang.reflect.Field; +import java.net.Socket; +import java.nio.channels.SelectionKey; +import java.nio.channels.SocketChannel; +import java.util.Properties; + +import static jdk.testlibrary.Asserts.assertNotNull; +import static jdk.testlibrary.Asserts.assertTrue; + +public class GlobalPollerTest { + private static WispEngineAccess access = SharedSecrets.getWispEngineAccess(); + + static Properties p; + static String socketAddr; + static { + p = java.security.AccessController.doPrivileged( + new java.security.PrivilegedAction() { + public Properties run() { + return System.getProperties(); + } + } + ); + socketAddr = (String)p.get("test.wisp.socketAddress"); + if (socketAddr == null) { + socketAddr = "www.example.com"; + } + } + + public static void main(String[] args) throws Exception { + + + Socket so = new Socket(socketAddr, 80); + so.getOutputStream().write("NOP\n\r\n\r".getBytes()); + // now server returns the data.. + // so is readable + // current task is interested in read event. + SocketChannel ch = so.getChannel(); + access.registerEvent(ch, SelectionKey.OP_READ); + + Class clazz = Class.forName("com.alibaba.wisp.engine.WispEventPump$Pool"); + Field pumps = clazz.getDeclaredField("pumps"); + pumps.setAccessible(true); + Object[] a = (Object[]) pumps.get(clazz.getEnumConstants()[0]); + WispTask[] fd2TaskLow = null; + int fd = ((SelChImpl) ch).getFDVal(); + for (Object pump : a) { + Field f = Class.forName("com.alibaba.wisp.engine.WispEventPump").getDeclaredField("fd2ReadTaskLow"); + f.setAccessible(true); + WispTask[] map = (WispTask[]) f.get(pump); + if (map[fd] != null) { + fd2TaskLow = map; + } + } + assertNotNull(fd2TaskLow); + + access.park(-1); + + assertTrue(fd2TaskLow[fd] == null); + + so.close(); + } +} diff --git a/test/com/alibaba/wisp/io/ReuseUdpSocektBufTest.java b/test/com/alibaba/wisp/io/ReuseUdpSocektBufTest.java new file mode 100644 index 0000000000000000000000000000000000000000..136870fb96afe787f18d38d5bf9a8de53e987e45 --- /dev/null +++ b/test/com/alibaba/wisp/io/ReuseUdpSocektBufTest.java @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * @test + * @library /lib/testlibrary + * @summary test reuse WispUdpSocket buffer + * @requires os.family == "linux" + * @run main/othervm -XX:+EnableCoroutine -Dcom.alibaba.wisp.transparentWispSwitch=true ReuseUdpSocektBufTest + */ + +import java.net.DatagramPacket; +import java.net.DatagramSocket; +import java.net.InetAddress; + +import static jdk.testlibrary.Asserts.assertTrue; + + +public class ReuseUdpSocektBufTest { + + static String msgs[] = {"Hello World", "Java", "Good Bye"}; + static int port; + + static boolean success = true; + + static class ServerThread extends Thread{ + DatagramSocket ds; + public ServerThread() { + try { + ds = new DatagramSocket(); + port = ds.getLocalPort(); + } catch (Exception e) { + throw new RuntimeException(e.getMessage()); + } + } + + public void run() { + byte b[] = new byte[100]; + DatagramPacket dp = new DatagramPacket(b,b.length); + while (true) { + try { + ds.receive(dp); + String reply = new String(dp.getData(), dp.getOffset(), dp.getLength()); + ds.send(new DatagramPacket(reply.getBytes(),reply.length(), + dp.getAddress(),dp.getPort())); + if (reply.equals(msgs[msgs.length-1])) { + break; + } + } catch (Exception e) { + success = false; + } + } + ds.close(); + } + } + + public static void main(String args[]) throws Exception { + ServerThread st = new ServerThread(); + st.start(); + DatagramSocket ds = new DatagramSocket(); + byte b[] = new byte[100]; + DatagramPacket dp = new DatagramPacket(b,b.length); + for (int i = 0; i < msgs.length; i++) { + ds.send(new DatagramPacket(msgs[i].getBytes(),msgs[i].length(), + InetAddress.getLocalHost(), + port)); + ds.receive(dp); + if (!msgs[i].equals(new String(dp.getData(), dp.getOffset(), dp.getLength()))) { + success = false; + } + } + ds.close(); + assertTrue(success); + System.out.println("Test Passed!!!"); + } +} diff --git a/test/com/alibaba/wisp/io/ShareFdTest.java b/test/com/alibaba/wisp/io/ShareFdTest.java new file mode 100644 index 0000000000000000000000000000000000000000..4afb2d6091f2d0f14ea1205c24f0bae740918b1f --- /dev/null +++ b/test/com/alibaba/wisp/io/ShareFdTest.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * @test + * @library /lib/testlibrary + * @summary test support fd use acorss coroutines + * @requires os.family == "linux" + * @run main/othervm -XX:+EnableCoroutine -Dcom.alibaba.wisp.transparentWispSwitch=true ShareFdTest + */ + +import sun.misc.SharedSecrets; +import sun.misc.WispEngineAccess; +import java.net.InetSocketAddress; +import java.net.ServerSocket; +import java.net.Socket; + + +import static jdk.testlibrary.Asserts.assertTrue; + +public class ShareFdTest { + private static boolean success = true; + private static ServerSocket serverSocket = null; + + public static void main(String[] args) { + WispEngineAccess wispEngineAccess = SharedSecrets.getWispEngineAccess(); + assert (wispEngineAccess.enableSocketLock()); + ShareFdTest shareFdTest = new ShareFdTest(); + shareFdTest.testAccept(); + assert success; + } + + void testAccept() { + try { + serverSocket = new ServerSocket(); + serverSocket.bind(new InetSocketAddress(6402)); + + Thread t1 = new TestThread(); + Thread t2 = new TestThread(); + t1.start(); + t2.start(); + + Socket s = new Socket(); + s.connect(new InetSocketAddress(6402)); + + Socket s1 = new Socket(); + s1.connect(new InetSocketAddress(6402)); + t1.join(); + t2.join(); + + } catch (Exception e) { + e.printStackTrace(); + success = false; + } + assertTrue(success); + } + + class TestThread extends Thread { + @Override + public void run() { + blockOnAccept(serverSocket); + } + } + + static void blockOnAccept(ServerSocket serverSocket) { + try { + serverSocket.accept(); + } catch (Exception e) { + e.printStackTrace(); + success = false; + } + } +} diff --git a/test/com/alibaba/wisp/io/SocketTest.java b/test/com/alibaba/wisp/io/SocketTest.java new file mode 100644 index 0000000000000000000000000000000000000000..4e4fd1991f9e0be3e5c01a62e6404ef759f7e68d --- /dev/null +++ b/test/com/alibaba/wisp/io/SocketTest.java @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * @test + * @summary Test WispEngine's Socket + * @requires os.family == "linux" + * @library /lib/testlibrary + * @run main/othervm -XX:+EnableCoroutine -Dcom.alibaba.wisp.transparentWispSwitch=true SocketTest + */ + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.*; +import java.nio.ByteBuffer; +import java.util.Arrays; +import java.util.List; +import static jdk.testlibrary.Asserts.assertTrue; + + +public class SocketTest { + + public static void main(String[] args) throws IOException { + System.out.println(mkSocketPair()); // test accept() and connect() + testData(); + testBlock(); + testCreation(); + } + + static private void testCreation() throws IOException { + int expectionCnt = 0; + try { + new Socket((Proxy) null); + } catch (Exception e) { + expectionCnt++; + } + try { + new Socket(Proxy.NO_PROXY); + } catch (Exception e) { + expectionCnt++; + } + assertTrue(expectionCnt == 2); + new SocketWrapper(); + assertTrue(expectionCnt == 2); + } + + static class SocketWrapper extends Socket { + SocketWrapper() throws SocketException { + super((SocketImpl)null); + } + } + + static private void testBlock() throws IOException { + List sop = mkSocketPair(); + new Thread(() -> { + try { + Socket so = sop.get(0); + Thread.sleep(100); + so.getOutputStream().write(new byte[2]); + + so.close(); + } catch (Exception e) { + throw new Error(e); + } + }).start(); + + Socket so = sop.get(1); + + long now = System.currentTimeMillis(); + if (2 != so.getInputStream().read(new byte[10])) { + throw new Error("read error"); + } + if (Math.abs(System.currentTimeMillis() - now - 100) > 5) + throw new Error("not wake as expected"); + } + + static private void testData() throws IOException { + List sop = mkSocketPair(); + + new Thread(() -> { + try { + Socket so = sop.get(0); + OutputStream os = so.getOutputStream(); + byte buf[] = new byte[4]; + ByteBuffer bb = ByteBuffer.wrap(buf); + for (int i = 0; i < 10; i++) { + bb.clear(); + bb.putInt(i); + os.write(buf); + } + so.close(); + } catch (Exception e) { + throw new Error(e); + } + }).start(); + Socket so = sop.get(1); + InputStream is = so.getInputStream(); + byte buf[] = new byte[4]; + ByteBuffer bb = ByteBuffer.wrap(buf); + for (int i = 0; true; i++) { + bb.clear(); + if (4 != is.read(buf)) { + if (i == 10) { + so.close(); + break; // ok here + } else { + throw new Error("read error"); + } + } + if (bb.getInt() != i) + throw new Error("data error"); + } + + } + + static private List mkSocketPair() throws IOException { + ServerSocket ss = new ServerSocket(13000); + Socket so = new Socket("localhost", 13000); + Socket so1 = ss.accept(); + ss.close(); + + return Arrays.asList(so, so1); + } +} diff --git a/test/com/alibaba/wisp/io/WispSocketCloseTest.java b/test/com/alibaba/wisp/io/WispSocketCloseTest.java new file mode 100644 index 0000000000000000000000000000000000000000..115857d14f91af8c2eb0819cda18fd81129ef8a2 --- /dev/null +++ b/test/com/alibaba/wisp/io/WispSocketCloseTest.java @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * @test + * @library /lib/testlibrary + * @summary test close will wake up blocking wispTask + * @requires os.family == "linux" + * @run main/othervm -XX:+EnableCoroutine -Dcom.alibaba.wisp.transparentWispSwitch=true WispSocketCloseTest + */ + +import java.io.IOException; +import java.net.*; + +import static jdk.testlibrary.Asserts.assertTrue; + + +public class WispSocketCloseTest { + + class Reaper extends Thread { + Socket s; + int timeout; + + Reaper(Socket s, int timeout) { + this.s = s; + this.timeout = timeout; + } + + public void run() { + try { + Thread.sleep(timeout); + s.close(); + } catch (Exception e) { + } + } + } + + WispSocketCloseTest() throws Exception { + ServerSocket ss = new ServerSocket(0); + ss.setSoTimeout(1000); + + InetAddress ia = InetAddress.getLocalHost(); + InetSocketAddress isa = + new InetSocketAddress(ia, ss.getLocalPort()); + + // client establishes the connection + Socket s1 = new Socket(); + s1.connect(isa); + + // receive the connection + Socket s2 = ss.accept(); + + // schedule reaper to close the socket in 5 seconds + Reaper r = new Reaper(s2, 5000); + r.start(); + + boolean readTimedOut = false; + try { + s2.getInputStream().read(); + } catch (IOException e) { + assertTrue (e instanceof SocketException); + assertTrue ("Socket is closed".equals(e.getMessage())); + } + + s1.close(); + ss.close(); + + if (readTimedOut) { + throw new Exception("Unexpected SocketTimeoutException throw!"); + } + } + + public static void main(String args[]) throws Exception { + new WispSocketCloseTest(); + } +} diff --git a/test/com/alibaba/wisp/lock/AQSTest.java b/test/com/alibaba/wisp/lock/AQSTest.java new file mode 100644 index 0000000000000000000000000000000000000000..13c25c5b4f906ca7828fe50974f10941efcb17b6 --- /dev/null +++ b/test/com/alibaba/wisp/lock/AQSTest.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * @test + * @summary Test AQS: CountDownLatch is implement by AQS + * @requires os.family == "linux" + * @run main/othervm -XX:+EnableCoroutine -Dcom.alibaba.wisp.transparentWispSwitch=true AQSTest + */ + + +import com.alibaba.wisp.engine.WispEngine; +import sun.misc.SharedSecrets; + +import java.util.concurrent.CountDownLatch; + +public class AQSTest { + static CountDownLatch cd = new CountDownLatch(1); + static CountDownLatch cd2 = new CountDownLatch(1); + public static void main(String[] args) { + WispEngine.dispatch(() -> { + long start = System.currentTimeMillis(); + try { + cd.await(); + assertInterval(start, 100, 5); + } catch (InterruptedException e) { + throw new Error(); + } + }); + WispEngine.dispatch(() -> { + long start = System.currentTimeMillis(); + try { + cd2.await(); + assertInterval(start, 200, 5); + } catch (InterruptedException e) { + throw new Error(); + } + }); + + + SharedSecrets.getWispEngineAccess().sleep(100); + cd.countDown(); + SharedSecrets.getWispEngineAccess().sleep(100); + cd2.countDown(); + SharedSecrets.getWispEngineAccess().sleep(5); + } + + public static void assertInterval(long start, int diff, int bias) { + if (Math.abs(System.currentTimeMillis() - start - diff) > bias) + throw new Error("not wakeup expected"); + } +} diff --git a/test/com/alibaba/wisp/lock/ElisionSpinTest.java b/test/com/alibaba/wisp/lock/ElisionSpinTest.java new file mode 100644 index 0000000000000000000000000000000000000000..335f1b74fe7629d25d4f9524695a6aeccd4702c9 --- /dev/null +++ b/test/com/alibaba/wisp/lock/ElisionSpinTest.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * @test + * @summary Test elision spin + * @requires os.family == "linux" + * @library /lib/testlibrary + * @run main/othervm -XX:+UseWisp2 -Dcom.alibaba.wisp.carrierEngines=1 ElisionSpinTest + */ + +import com.alibaba.wisp.engine.WispEngine; +import sun.misc.SharedSecrets; + +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.ReentrantLock; + +import static jdk.testlibrary.Asserts.assertFalse; +import static jdk.testlibrary.Asserts.assertTrue; + +public class ElisionSpinTest { + public static void main(String[] args) { + assertFalse(SharedSecrets.getWispEngineAccess().hasMoreTasks()); + + ReentrantLock lock = new ReentrantLock(); + Condition cond = lock.newCondition(); + + WispEngine.dispatch(() -> { + lock.lock(); + try { + cond.awaitUninterruptibly(); + } finally { + lock.unlock(); + } + }); + + lock.lock(); + try { + cond.signal(); + } finally { + lock.unlock(); + } + + assertTrue(SharedSecrets.getWispEngineAccess().hasMoreTasks()); + } +} diff --git a/test/com/alibaba/wisp/lock/LockTest.java b/test/com/alibaba/wisp/lock/LockTest.java new file mode 100644 index 0000000000000000000000000000000000000000..23f9652558808c60339773ed7138691cdf637eff --- /dev/null +++ b/test/com/alibaba/wisp/lock/LockTest.java @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * @test + * @summary Test ReentrantLock in coroutine environment + * @requires os.family == "linux" + * @run main/othervm -XX:+EnableCoroutine -Dcom.alibaba.wisp.transparentWispSwitch=true LockTest + */ + + +import com.alibaba.wisp.engine.WispEngine; +import sun.misc.SharedSecrets; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +public class LockTest { + static Lock lock = new ReentrantLock(); + static Condition cond = lock.newCondition(); + + static CountDownLatch latch = new CountDownLatch(2); + + public static void main(String[] args) throws Exception { + WispEngine.dispatch(LockTest::p1); + WispEngine.dispatch(LockTest::p2); + + latch.await(2, TimeUnit.SECONDS); + } + + static void assertInterval(long start, int diff, int bias) { + if (Math.abs(System.currentTimeMillis() - start - diff) > bias) + throw new Error("not wakeup expected"); + } + + private static void p1() { + lock.lock(); + SharedSecrets.getWispEngineAccess().sleep(100); + try { + long start = System.currentTimeMillis(); + cond.await(); + assertInterval(start, 100, 5); + + } catch (InterruptedException e) { + throw new Error(); + } finally { + lock.unlock(); + } + latch.countDown(); + } + + private static void p2() { + long start = System.currentTimeMillis(); + lock.lock(); + try { + assertInterval(start, 100, 5); + SharedSecrets.getWispEngineAccess().sleep(100); + cond.signal(); + } finally { + lock.unlock(); + } + latch.countDown(); + } + + +} diff --git a/test/com/alibaba/wisp/lock/ParkNanoTest.java b/test/com/alibaba/wisp/lock/ParkNanoTest.java new file mode 100644 index 0000000000000000000000000000000000000000..9495b8b260afa3afabad2f5cfa393ee09d53621d --- /dev/null +++ b/test/com/alibaba/wisp/lock/ParkNanoTest.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * @test + * @summary Test park nanos + * @requires os.family == "linux" + * @run main/othervm -XX:+EnableCoroutine -Dcom.alibaba.wisp.transparentWispSwitch=true ParkNanoTest + */ + + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +public class ParkNanoTest { + public static void main(String[] args) throws Exception { + doTest(400_000, 300_000); + doTest(800_000, 500_000); + } + + private static void doTest(long wait, long expected) throws Exception { + CountDownLatch l = new CountDownLatch(1); + long start = System.nanoTime(); + l.await(wait, TimeUnit.NANOSECONDS); + long diff = System.nanoTime() - start; + + if (diff < expected) + throw new Error("wake up too early!"); + } +} diff --git a/test/com/alibaba/wisp/lock/UnsafeParkTest.java b/test/com/alibaba/wisp/lock/UnsafeParkTest.java new file mode 100644 index 0000000000000000000000000000000000000000..527865fa33362c9f5fad024006960d954c5a20fe --- /dev/null +++ b/test/com/alibaba/wisp/lock/UnsafeParkTest.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * @test + * @library /lib/testlibrary + * @summary Test to verify we can do proper wisp scheduling while calling on Unsafe.park() + * @requires os.family == "linux" + * @run main/othervm -XX:+EnableCoroutine -Dcom.alibaba.wisp.transparentWispSwitch=true UnsafeParkTest + */ + +import com.alibaba.wisp.engine.WispEngine; +import sun.misc.Unsafe; + +import java.lang.reflect.Field; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; + +import static jdk.testlibrary.Asserts.assertTrue; + + +public class UnsafeParkTest { + public static void main(String[] args) throws Exception { + Field f = Unsafe.class.getDeclaredField("theUnsafe"); + f.setAccessible(true); + Unsafe unsafe = (Unsafe) f.get(null); + + AtomicLong awake = new AtomicLong(); + + WispEngine.dispatch(() -> { + try { + Thread.sleep(300); + } catch (InterruptedException e) { + e.printStackTrace(); + } + awake.set(System.currentTimeMillis()); + }); + + long start = System.currentTimeMillis(); + + unsafe.park(false, 1); + unsafe.park(false, TimeUnit.MILLISECONDS.toNanos(500)); + + assertTrue(Math.abs(awake.get() - start - 300) < 100, + "awake should be set before unsafe.park expired " + awake.get() + " " + start); + } +} diff --git a/test/com/alibaba/wisp/monitor/C2SyncMethodTest.java b/test/com/alibaba/wisp/monitor/C2SyncMethodTest.java new file mode 100644 index 0000000000000000000000000000000000000000..129fe75df5cde017704770e218ec3e24f4f8bc55 --- /dev/null +++ b/test/com/alibaba/wisp/monitor/C2SyncMethodTest.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * @test + * @summary test to run a compiled/synchronized method with wisp enabled. + * @requires os.family == "linux" + * @run main/othervm -XX:-UseBiasedLocking -XX:+EnableCoroutine -XX:+UseWispMonitor -Dcom.alibaba.wisp.transparentWispSwitch=true C2SyncMethodTest + */ +public class C2SyncMethodTest { + public synchronized static void main(String[] args) { + for (int i = 0; i < 1000000; i++) { + foo(); + } + } + + static volatile int n = 0; + + synchronized static void foo() { + n++; + } +} diff --git a/test/com/alibaba/wisp/monitor/FinalizerTest.java b/test/com/alibaba/wisp/monitor/FinalizerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..e6fd784f9a6d2df34f0eb9746fe334aee1679579 --- /dev/null +++ b/test/com/alibaba/wisp/monitor/FinalizerTest.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * @test + * @summary Test unpark in a finalizer thread. + * @requires os.family == "linux" + * @run main/othervm -XX:-UseBiasedLocking -XX:+EnableCoroutine -XX:+UseWispMonitor -Dcom.alibaba.wisp.transparentWispSwitch=true FinalizerTest + */ + +public class FinalizerTest { + static final Foo lock = new Foo(); + public static void main(String[] args) throws Exception { + new Foo(); + new Thread(() -> { + try { + Thread.sleep(10); + } catch (InterruptedException e) { + e.printStackTrace(); + } + System.gc(); + System.runFinalization(); + }, "call-System.gc()").start(); + synchronized (lock) { + lock.wait(); + } + System.out.println(Thread.currentThread().getName() + ": wait done"); + } + + static class Foo { + @Override + protected void finalize() throws Throwable { + synchronized (lock) { + lock.notify(); + } + } + } +} diff --git a/test/com/alibaba/wisp/monitor/JNICriticalTest.java b/test/com/alibaba/wisp/monitor/JNICriticalTest.java new file mode 100644 index 0000000000000000000000000000000000000000..5bbbde339d818a5d7eadf6991f313e215e94ea67 --- /dev/null +++ b/test/com/alibaba/wisp/monitor/JNICriticalTest.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * @test + * @summary Test unpark in JNI critical case + * @requires os.family == "linux" + * @run main/othervm -XX:-UseBiasedLocking -XX:+EnableCoroutine -XX:+UseWispMonitor -Dcom.alibaba.wisp.transparentWispSwitch=true JNICriticalTest + */ + + +import com.alibaba.wisp.engine.WispEngine; +import sun.misc.SharedSecrets; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.IntStream; +import java.util.zip.Deflater; + +public class JNICriticalTest { + public static void main(String[] args) throws Exception { + CountDownLatch latch = new CountDownLatch(16); + AtomicInteger id = new AtomicInteger(); + ExecutorService es = Executors.newCachedThreadPool(r -> { + Thread t = new Thread(r); + t.setName("JNICriticalTest" + id.getAndIncrement()); + return t; + }); + IntStream.range(0, 4).forEach(ign -> es.execute(() -> { + Deflater d = new Deflater(); + AtomicInteger n = new AtomicInteger(); + for (int i = 0; i < 4; i++) { + WispEngine.dispatch(() -> { + while (n.get() < 1_000_000) { + jniCritical(d); + if (n.incrementAndGet() % 100000 == 0) { + System.out.println(SharedSecrets.getJavaLangAccess().currentThread0().getName() + "/" + n.get() / 1000 +"k"); + } + } + latch.countDown(); + }); + } + })); + latch.await(); + } + + + private static void jniCritical(Deflater d) { + d.reset(); + d.setInput(bs); + d.finish(); + byte[] out = new byte[4096 * 4]; + + d.deflate(out); // Enter the JNI critical block here. + } + + static byte[] bs = new byte[12]; +} diff --git a/test/com/alibaba/wisp/monitor/LazyUnparkBugTest.java b/test/com/alibaba/wisp/monitor/LazyUnparkBugTest.java new file mode 100644 index 0000000000000000000000000000000000000000..7173946a5a4eb4095aff6b4382d6d897c9bd165a --- /dev/null +++ b/test/com/alibaba/wisp/monitor/LazyUnparkBugTest.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * @test + * @summary T12212948 + * @requires os.family == "linux" + * @library /lib/testlibrary + * @run main/othervm -XX:+EnableCoroutine -XX:+UseWispMonitor -Dcom.alibaba.wisp.transparentWispSwitch=true LazyUnparkBugTest + */ + +public class LazyUnparkBugTest { + private static volatile Object thunk = null; + + public static void test() throws Exception { + thunk = null; + Thread t1 = new Thread(() -> { + try { + synchronized (LazyUnparkBugTest.class) { + Thread.sleep(1_000L); + } + Object o; + do { + o = thunk; + } while (o == null); + } catch (Exception e) { + e.printStackTrace(); + } + }); + + Thread t2 = new Thread(() -> { + try { + Thread.sleep(5_0L); + synchronized (LazyUnparkBugTest.class) { + System.out.println("in t2"); + } + } catch (Exception e) { + e.printStackTrace(); + } + thunk = new Object(); + }); + t1.start(); + t2.start(); + t1.join(); + t2.join(); + } + + public static void main(String[] args) throws Exception { + long begin = System.currentTimeMillis(); + test(); + long end = System.currentTimeMillis(); + System.out.println("cost : " + (end - begin) + " ms"); + if ((end - begin) > 2000) { + throw new Error("this is bug " + (end - begin)); + } + } + +} diff --git a/test/com/alibaba/wisp/monitor/PassTokenTest.java b/test/com/alibaba/wisp/monitor/PassTokenTest.java new file mode 100644 index 0000000000000000000000000000000000000000..4257627557916b121a11a054f4d66b531309d286 --- /dev/null +++ b/test/com/alibaba/wisp/monitor/PassTokenTest.java @@ -0,0 +1,195 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * @test + * @library /lib/testlibrary + * @summary Test object lock with coroutine + * @requires os.family == "linux" + * @run main/othervm -XX:-UseBiasedLocking -XX:+EnableCoroutine -XX:+UseWispMonitor -Dcom.alibaba.wisp.transparentWispSwitch=true -Dcom.alibaba.wisp.schedule.policy=PULL PassTokenTest + * @run main/othervm -XX:-UseBiasedLocking -XX:+EnableCoroutine -XX:+UseWispMonitor -Dcom.alibaba.wisp.transparentWispSwitch=true -Dcom.alibaba.wisp.schedule.policy=PUSH PassTokenTest + * @run main/othervm -XX:-UseBiasedLocking -XX:+EnableCoroutine -XX:+UseWispMonitor -Dcom.alibaba.wisp.transparentWispSwitch=true -DcheckStealEnable=true PassTokenTest + */ + + +import com.alibaba.wisp.engine.WispEngine; +import com.alibaba.wisp.engine.WispTask; +import sun.misc.SharedSecrets; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +import static jdk.testlibrary.Asserts.assertTrue; + +public class PassTokenTest { + private final static boolean needCheckStealEnable = Boolean.getBoolean("checkStealEnable"); + final static Runner[] runners = new Runner[8]; + static boolean JUC; + static final int N = 40000; + + public static void main(String[] args) throws Exception { + JUC = true; + System.out.println("JUC"); + doTest(); + JUC = false; + System.out.println("ObjectMonitor"); + doTest(); + } + + private static void doTest() { + for (int i = 0; i < runners.length; i++) { + runners[i] = new Runner(i); + } + List plans = new ArrayList<>(Arrays.asList( + () -> { // only coroutine + for (Runner runner1 : runners) { + WispEngine.dispatch(runner1); + } + }, + () -> { // only thread + int n = 0; + for (Runner runner : runners) { + new Thread(runner, "MP-THREAD-RUNNER-" + n++).start(); + } + }, + () -> { //mixed + int n = 0; + for (int i = 0; i < runners.length; i += 2) { + final int ci = i; + new Thread(() -> { + for (int j = ci; j < ci + 2 && j < runners.length; j++) { + WispEngine.dispatch(runners[j]); + } + }, "MP-MIX-RUNNER-" + n++).start(); + } + })); + Collections.shuffle(plans); + + plans.forEach(plan -> { + finishLatch = new CountDownLatch(runners.length); + current = 0; + long start = System.currentTimeMillis(); + plan.run(); // create runners + try { + finishLatch.await(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + System.out.println("elapsed = " + (System.currentTimeMillis() - start) + "ms"); + }); + + System.out.println("-----------"); + } + + + static int current = 0; + static final Lock lock = new ReentrantLock(); + static final Condition cond = lock.newCondition(); + static CountDownLatch finishLatch; + + static class Runner implements Runnable { + + private final int ord; + + public Runner(int ord) { + this.ord = ord; + } + + @Override + public void run() { + WispTask task = SharedSecrets.getWispEngineAccess().getCurrentTask(); + + while (current < N) { + if (JUC) { + lock.lock(); + try { + while (current % runners.length != ord) { + try { + cond.await(); + checkStealEnable(task); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + if (++current % 10000 == 0) // pass the token + System.out.println(SharedSecrets.getJavaLangAccess().currentThread0().getName() + "\t" + current); + } finally { + lock.unlock(); + } + } else { + synchronized (lock) { + while (current % runners.length != ord) { + try { + lock.wait(); + checkStealEnable(task); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + if (++current % 10000 == 0) // pass the token + System.out.println(SharedSecrets.getJavaLangAccess().currentThread0().getName() + "\t" + current); + } + } + + if (JUC) { + lock.lock(); + try { + cond.signalAll(); + } finally { + lock.unlock(); + } + } else { + synchronized (lock) { + lock.notifyAll(); + } + } + } + + finishLatch.countDown(); + } + + static void checkStealEnable(WispTask task) { + if (!needCheckStealEnable) { + return; + } + try { + Field resumeEntryField = task.getClass().getDeclaredField("resumeEntry"); + resumeEntryField.setAccessible(true); + final Object resumeEntry = resumeEntryField.get(task); + if (resumeEntry == null) { + return; + } + Field stealEnableField = resumeEntry.getClass().getDeclaredField("stealEnable"); + stealEnableField.setAccessible(true); + assertTrue(stealEnableField.getBoolean(resumeEntry)); + } catch (ReflectiveOperationException e) { + throw new Error(e); + } + } + } +} diff --git a/test/com/alibaba/wisp/monitor/SynchronizedTest.java b/test/com/alibaba/wisp/monitor/SynchronizedTest.java new file mode 100644 index 0000000000000000000000000000000000000000..a542011cac1d54b7518b758bf58c08c1c72b0bcb --- /dev/null +++ b/test/com/alibaba/wisp/monitor/SynchronizedTest.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * @test + * @summary Basic test for java primitive lock(synchronized) + * @requires os.family == "linux" + * @run main/othervm -XX:-UseBiasedLocking -XX:+EnableCoroutine -XX:+UseWispMonitor -Dcom.alibaba.wisp.transparentWispSwitch=true SynchronizedTest + */ + +import com.alibaba.wisp.engine.WispEngine; + +public class SynchronizedTest { + public static void main(String[] args) { + + SynchronizedTest s = new SynchronizedTest(); + WispEngine.dispatch(s::foo); + WispEngine.dispatch(s::bar); + } + + private synchronized void foo() { + try { + Thread.sleep(10); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + private synchronized void bar() { + } +} diff --git a/test/com/alibaba/wisp/monitor/WispExitTest.java b/test/com/alibaba/wisp/monitor/WispExitTest.java new file mode 100644 index 0000000000000000000000000000000000000000..3adfcadc84cdc9d90335ef3c646f18b9403c57bb --- /dev/null +++ b/test/com/alibaba/wisp/monitor/WispExitTest.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * @test + * @summary Ensure we can exit vm when -XX:+UseWispMonitor + * @requires os.family == "linux" + * @run main/othervm -XX:-UseBiasedLocking -XX:+EnableCoroutine -XX:+UseWispMonitor -Dcom.alibaba.wisp.transparentWispSwitch=true WispExitTest + */ +public class WispExitTest { + public static void main(String[] args) throws Exception { + + } +} diff --git a/test/com/alibaba/wisp/thread/DaemonTest.java b/test/com/alibaba/wisp/thread/DaemonTest.java new file mode 100644 index 0000000000000000000000000000000000000000..a326c7684d1fd74894aa117be77939b191a04729 --- /dev/null +++ b/test/com/alibaba/wisp/thread/DaemonTest.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +import static jdk.testlibrary.Asserts.*; + +/* + * @test + * @library /lib/testlibrary + * @summary test thread as wisp still keep the daemon semantic + * @requires os.family == "linux" + * @run main DaemonTest + */ +public class DaemonTest { + + private static final String SHUTDOWN_MSG = "[Run ShutdownHook]"; + + public static void main(String[] args) throws Exception { + if (args.length == 0) { + driver(true); // test daemon will not prevent shutdown + driver(false); // test non-daemon will prevent shutdown + } else { + assert Thread.currentThread().getName().equals("main"); + boolean daemon = Boolean.valueOf(args[0]); + + // start a non-daemon to cover the `--nonDaemonCount == 0` branch + Thread thread = new Thread(() -> {/**/}); + thread.setDaemon(false); + thread.start(); + + Runtime.getRuntime().addShutdownHook(new Thread(() -> System.out.println(SHUTDOWN_MSG))); + thread = new Thread(() -> { + System.out.println("thread started.."); + if (!daemon) { + try { + Thread.sleep(2000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + }); + thread.setDaemon(daemon); + thread.start(); + Thread.sleep(1000); + } + } + + private static void driver(boolean daemon) throws Exception { + // we can not use jdk.testlibrary.ProcessTools here, because we need to analyse stdout of a unfinished process + Process process = new ProcessBuilder(System.getProperty("java.home") + "/bin/java", + "-XX:+UseWisp2", "-cp", System.getProperty("java.class.path"), DaemonTest.class.getName(), Boolean.toString(daemon)).start(); + Thread.sleep(2000); + byte[] buffer = new byte[1024]; + int n = process.getInputStream().read(buffer); + String s = new String(buffer, 0, n); + assertEQ(daemon, s.contains(SHUTDOWN_MSG)); + } +} diff --git a/test/com/alibaba/wisp/thread/DaemonThreadGroupTest.java b/test/com/alibaba/wisp/thread/DaemonThreadGroupTest.java new file mode 100644 index 0000000000000000000000000000000000000000..cfbe390b6731c4742d6a4e4e9dac9ab044599027 --- /dev/null +++ b/test/com/alibaba/wisp/thread/DaemonThreadGroupTest.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * @test + * @library /lib/testlibrary + * @summary Test Daemon Thread Group implementation + * @requires os.family == "linux" + * @run main/othervm -XX:+EnableCoroutine -Dcom.alibaba.wisp.transparentWispSwitch=true -Dcom.alibaba.wisp.useCarrierAsPoller=false DaemonThreadGroupTest +*/ + + +import java.lang.reflect.Field; + +import static jdk.testlibrary.Asserts.assertTrue; + + +/** + * test the Daemon Thread Group implementation + */ +public class DaemonThreadGroupTest { + public static void main(String... arg) throws Exception { + Field f = Class.forName("com.alibaba.wisp.engine.WispEngine").getDeclaredField("unparkDispatcher"); + f.setAccessible(true); + Thread t = (Thread) f.get(null); + + f = Class.forName("com.alibaba.wisp.engine.WispEngine").getDeclaredField("DAEMON_THREAD_GROUP"); + f.setAccessible(true); + ThreadGroup threadGroup = (ThreadGroup) f.get(null); + + System.out.println(threadGroup.getName()); + + assertTrue(t.getThreadGroup() == threadGroup, "the thread isn't in daemonThreadGroup"); + } +} diff --git a/test/com/alibaba/wisp/thread/DisableThreadAsWispAtRuntimeTest.java b/test/com/alibaba/wisp/thread/DisableThreadAsWispAtRuntimeTest.java new file mode 100644 index 0000000000000000000000000000000000000000..91f40479465364b41f6cd4b8d3336952ff921e4d --- /dev/null +++ b/test/com/alibaba/wisp/thread/DisableThreadAsWispAtRuntimeTest.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * @test + * @summary this feature is not supported in wisp2, just check compatibility + * @requires os.family == "linux" + * @library /lib/testlibrary + * @run main/othervm -XX:+EnableCoroutine -XX:+UseWispMonitor -Dcom.alibaba.wisp.transparentWispSwitch=true DisableThreadAsWispAtRuntimeTest + */ + +import com.alibaba.wisp.engine.WispEngine; + +import java.lang.reflect.Field; + +import static jdk.testlibrary.Asserts.assertEQ; + +public class DisableThreadAsWispAtRuntimeTest { + + + public static void main(String[] args) throws Exception { + switchShift(true); + switchShift(false); + } + + private static void switchShift(boolean val) { + try { + Class wispClazz = Class.forName("com.alibaba.wisp.engine.WispEngine"); + Field field = wispClazz.getDeclaredField("shiftThreadModel"); + field.setAccessible(true); + field.setBoolean(null /*static field*/, val); + } catch (ReflectiveOperationException e) { + throw new Error(e); + } + assertEQ(WispEngine.enableThreadAsWisp(), val); + } +} diff --git a/test/com/alibaba/wisp/thread/EngineExecutorTest.java b/test/com/alibaba/wisp/thread/EngineExecutorTest.java new file mode 100644 index 0000000000000000000000000000000000000000..fd4442ce96e68ed534197821402f18ee1a2a330d --- /dev/null +++ b/test/com/alibaba/wisp/thread/EngineExecutorTest.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * @test + * @library /lib/testlibrary + * @summary test submit task to engine. + * @requires os.family == "linux" + * @run main/othervm -XX:+EnableCoroutine -Dcom.alibaba.wisp.transparentWispSwitch=true EngineExecutorTest + */ + +import com.alibaba.wisp.engine.WispEngine; +import sun.misc.SharedSecrets; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executor; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; + +import static jdk.testlibrary.Asserts.assertTrue; + +public class EngineExecutorTest { + public static void main(String[] args) throws Exception { + testExecutor(); + } + + static AtomicReference engine = new AtomicReference<>(); + private static void testExecutor() throws Exception { + CountDownLatch latch = new CountDownLatch(1); + Thread t = new Thread(() -> { + engine.set(WispEngine.current()); + latch.countDown(); + while (true) { + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + }); + + t.start(); + latch.await(); + WispEngine e = engine.get(); + CountDownLatch latch1 = new CountDownLatch(100); + for (int i = 0; i < 100; i++) { + e.execute(latch1::countDown); + } + assertTrue(latch1.await(1, TimeUnit.SECONDS)); + } +} diff --git a/test/com/alibaba/wisp/thread/InterruptTest.java b/test/com/alibaba/wisp/thread/InterruptTest.java new file mode 100644 index 0000000000000000000000000000000000000000..16ed42b6891b06a495a929dfc62fe04d6f0eaa52 --- /dev/null +++ b/test/com/alibaba/wisp/thread/InterruptTest.java @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * @test + * @summary test thread.interrupt() of wispTask + * @requires os.family == "linux" + * @run main/othervm -XX:+EnableCoroutine -Dcom.alibaba.wisp.transparentWispSwitch=true InterruptTest + */ + +import com.alibaba.wisp.engine.WispEngine; + +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; +import java.util.concurrent.CountDownLatch; + +public class InterruptTest { + public static void main(String[] args) throws Exception { + Lock lock = new ReentrantLock(); + Condition dummy = lock.newCondition(); + Condition finish = lock.newCondition(); + Condition threadSet = lock.newCondition(); + CountDownLatch finishLatch = new CountDownLatch(1); + + AtomicBoolean interrupted = new AtomicBoolean(false); + AtomicReference thrd = new AtomicReference<>(); + + WispEngine.dispatch(() -> { + thrd.set(Thread.currentThread()); + lock.lock(); + finishLatch.countDown(); + threadSet.signal(); + try { + try { + dummy.await(1, TimeUnit.SECONDS); + throw new Error("Exception not happened"); + } catch (InterruptedException e) { + interrupted.set(true); + } + } finally { + finish.signal(); + lock.unlock(); + } + }); + + finishLatch.await(); + lock.lock(); + try { + if (thrd.get() == null && !threadSet.await(1, TimeUnit.SECONDS)) + throw new Error("wait threadSet"); + } finally { + lock.unlock(); + } + + if (thrd.get() == null) + throw new Error("thread not set"); + + try { + Thread.sleep(100); + } catch (InterruptedException e) { + } + thrd.get().interrupt(); + + lock.lock(); + try { + if (!finish.await(1, TimeUnit.SECONDS)) + throw new Error("wait finish"); + } finally { + lock.unlock(); + } + + if (!interrupted.get()) + throw new Error("InterruptedException not happened"); + + } +} diff --git a/test/com/alibaba/wisp/thread/InterruptedSleepTest.java b/test/com/alibaba/wisp/thread/InterruptedSleepTest.java new file mode 100644 index 0000000000000000000000000000000000000000..1deb7eb47a9fb3649323b023413b66ce86e92e7d --- /dev/null +++ b/test/com/alibaba/wisp/thread/InterruptedSleepTest.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * @test + * @summary test InterruptedException was thrown by sleep() + * @requires os.family == "linux" + * @library /lib/testlibrary + * @run main/othervm -XX:+EnableCoroutine -Dcom.alibaba.wisp.transparentWispSwitch=true InterruptedSleepTest + */ + +import com.alibaba.wisp.engine.WispEngine; + +import static jdk.testlibrary.Asserts.assertFalse; +import static jdk.testlibrary.Asserts.assertLessThan; +import static jdk.testlibrary.Asserts.assertTrue; + +public class InterruptedSleepTest { + public static void main(String[] args) { + Thread mainCoro = Thread.currentThread(); + WispEngine.dispatch(() -> { + try { + Thread.sleep(100); + } catch (InterruptedException e) { + } + mainCoro.interrupt(); + }); + long start = System.currentTimeMillis(); + boolean ie = false; + try { + Thread.sleep(1000L); + } catch (InterruptedException e) { + ie = true; + } + assertLessThan((int) (System.currentTimeMillis() - start), 1000); + assertTrue(ie); + assertFalse(mainCoro.isInterrupted()); + } +} diff --git a/test/com/alibaba/wisp/thread/IsAliveTest.java b/test/com/alibaba/wisp/thread/IsAliveTest.java new file mode 100644 index 0000000000000000000000000000000000000000..827b06b2078fff0f2c5328524cc43e8fa9ebe635 --- /dev/null +++ b/test/com/alibaba/wisp/thread/IsAliveTest.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * @test + * @summary test thread.isAlive() of wispTask + * @requires os.family == "linux" + * @library /lib/testlibrary + * @run main/othervm -XX:+EnableCoroutine -Dcom.alibaba.wisp.transparentWispSwitch=true IsAliveTest + */ + +import com.alibaba.wisp.engine.WispEngine; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; + +import static jdk.testlibrary.Asserts.assertFalse; +import static jdk.testlibrary.Asserts.assertTrue; + + +public class IsAliveTest { + public static void main(String[] args) throws Exception { + AtomicBoolean isAlive = new AtomicBoolean(); + AtomicReference t = new AtomicReference<>(); + final CountDownLatch cond = new CountDownLatch(1); + + WispEngine.dispatch(() -> { + t.set(Thread.currentThread()); + isAlive.set(t.get().isAlive()); + cond.countDown(); + }); + try { + cond.await(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + assertTrue(isAlive.get()); + assertFalse(t.get().isAlive()); + } +} diff --git a/test/com/alibaba/wisp/thread/PreemptTest.java b/test/com/alibaba/wisp/thread/PreemptTest.java new file mode 100644 index 0000000000000000000000000000000000000000..2ece050c355183188b843b3ee54cfbfc03cd950a --- /dev/null +++ b/test/com/alibaba/wisp/thread/PreemptTest.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * @test + * @summary test wisp time slice preempt + * @requires os.family == "linux" + * @library /lib/testlibrary + * @run main/othervm -XX:+EnableCoroutine -Dcom.alibaba.wisp.transparentWispSwitch=true -Dcom.alibaba.wisp.carrierEngines=1 PreemptTest + + */ + +import com.alibaba.wisp.engine.WispEngine; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import static jdk.testlibrary.Asserts.assertTrue; + +public class PreemptTest { + public static void main(String[] args) throws Exception { + doTest(PreemptTest::simpleLoop); + } + + private static void doTest(Runnable r) throws Exception { + WispEngine.dispatch(r); + CountDownLatch latch = new CountDownLatch(1); + WispEngine.dispatch(latch::countDown); + assertTrue(latch.await(5, TimeUnit.SECONDS)); + } + + private static void complexLoop() { + MessageDigest md; + try { + md = MessageDigest.getInstance("MD5"); + } catch (NoSuchAlgorithmException e) { + throw new Error(e); + } + + while (true) { + md.update("welcome to the wisp world!".getBytes()); + n = md.digest()[0]; + } + } + + private static void simpleLoop() { + int x; + do { + x = n; + } while (x == n); + } + + // TODO: handle safepoint consumed by state switch + + volatile static int n; +} diff --git a/test/com/alibaba/wisp/thread/ThrowErrorTest.java b/test/com/alibaba/wisp/thread/ThrowErrorTest.java new file mode 100644 index 0000000000000000000000000000000000000000..2402eab260e2e4fe185d4af8a8e72e8503deb75a --- /dev/null +++ b/test/com/alibaba/wisp/thread/ThrowErrorTest.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * @test + * @summary test coroutine throw Error + * @requires os.family == "linux" + * @library /lib/testlibrary + * @run main/othervm -XX:+EnableCoroutine -Dcom.alibaba.wisp.transparentWispSwitch=true ThrowErrorTest + */ + +import com.alibaba.wisp.engine.WispEngine; + +import static jdk.testlibrary.Asserts.assertTrue; + +public class ThrowErrorTest { + public static void main(String[] args) { + WispEngine.dispatch(() -> { + throw new Error(); + }); + + boolean[] executed = new boolean[]{false}; + + WispEngine.dispatch(() -> executed[0] = true); + try { + Thread.sleep(100); + } catch (InterruptedException e) { + } + assertTrue(executed[0]); + } +} diff --git a/test/com/alibaba/wisp/timer/OverflowTest.java b/test/com/alibaba/wisp/timer/OverflowTest.java new file mode 100644 index 0000000000000000000000000000000000000000..63e4741e7ef3bf94275dbf18ea78b11c0139ec41 --- /dev/null +++ b/test/com/alibaba/wisp/timer/OverflowTest.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * @test + * @library /lib/testlibrary + * @summary Test timer implementation + * @requires os.family == "linux" + * @run main/othervm -XX:+EnableCoroutine OverflowTest + */ + + +import com.alibaba.wisp.engine.WispEngine; +import com.alibaba.wisp.engine.WispTask; +import sun.misc.SharedSecrets; +import sun.misc.WispEngineAccess; + +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; + +import static jdk.testlibrary.Asserts.assertTrue; + + +/** + * test the time out implementation + */ +public class OverflowTest { + public static void main(String... arg) throws Exception { + + WispEngineAccess access = SharedSecrets.getWispEngineAccess(); + + AtomicReference task1 = new AtomicReference<>(); + AtomicBoolean doUnpark = new AtomicBoolean(false); + AtomicBoolean hasError = new AtomicBoolean(false); + + WispEngine.dispatch(() -> { + task1.set(access.getCurrentTask()); + access.park(Long.MAX_VALUE); + // if timeout is negative(< now()), this task is selected in doSchedule + // and park returns immediately + hasError.set(!doUnpark.get()); // should not reach here before doing unpark + }); + + access.sleep(100); // switch task + // let task exit + doUnpark.set(true); + access.unpark(task1.get()); + assertTrue(!hasError.get(), "hasError.get() should be false."); + } +} diff --git a/test/com/alibaba/wisp/timer/PriorityQueueSortTest.java b/test/com/alibaba/wisp/timer/PriorityQueueSortTest.java new file mode 100644 index 0000000000000000000000000000000000000000..bb7276ba50ea8236a5d04eea634a94de9c6657f6 --- /dev/null +++ b/test/com/alibaba/wisp/timer/PriorityQueueSortTest.java @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * @test + * @library /lib/testlibrary + * @summary Test TimeOut.Queue's offer and remove function, make sure it's consistent with the behavior of the jdk's priority queue + * @requires os.family == "linux" + * @run main/othervm PriorityQueueSortTest + */ + +import com.alibaba.wisp.engine.TimeOut; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.*; + + +import static jdk.testlibrary.Asserts.assertTrue; + +public class PriorityQueueSortTest { + + static Class classQ; + static Class classT; + + static Method poll = null; + static Method offer = null; + + static TimeOut ILLEGAL_FLAG = new TimeOut(null, 1, false); + + static class MyComparator implements Comparator { + public int compare(TimeOut x, TimeOut y) { + return Long.compare(getDeadNano(x), getDeadNano(y)); + } + } + + static Long getDeadNano(TimeOut t) { + try { + Field deadline = TimeOut.class.getDeclaredField("deadlineNano"); + deadline.setAccessible(true); + return (Long) deadline.get(t); + } catch (Exception e) { + throw new Error(e); + } + } + + static Object getTimerQueue() { + try { + classQ = Class.forName("com.alibaba.wisp.engine.TimeOut$TimerManager$Queue"); + classT = Class.forName("com.alibaba.wisp.engine.TimeOut$TimerManager"); + Constructor c1 = classQ.getDeclaredConstructor(); + c1.setAccessible(true); + Constructor c2 = classT.getDeclaredConstructor(); + c2.setAccessible(true); + poll = classQ.getDeclaredMethod("poll"); + poll.setAccessible(true); + offer = classQ.getDeclaredMethod("offer", TimeOut.class); + offer.setAccessible(true); + return c1.newInstance(); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + static boolean add(Object pq, TimeOut timeOut) { + try { + offer.invoke(pq, timeOut); + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + static TimeOut poll(Object pq) { + try { + return (TimeOut)poll.invoke(pq); + } catch (Exception e) { + e.printStackTrace(); + return ILLEGAL_FLAG; + } + } + + public static void main(String[] args) { + int n = 10000; + + List sorted = new ArrayList<>(n); + for (int i = 0; i < n; i++) + sorted.add(new Long(i)); + List shuffled = new ArrayList<>(sorted); + Collections.shuffle(shuffled); + + Object pq = getTimerQueue(); + + + for (Iterator i = shuffled.iterator(); i.hasNext(); ) + add(pq, new TimeOut(null, i.next(), false)); + + List recons = new ArrayList<>(); + + while (true) { + TimeOut t = poll(pq); + if (t == null) { + break; + } + recons.add(getDeadNano(t)); + } + assertTrue(recons.equals(sorted), "Sort failed"); + + for (Long val : recons) { + add(pq, new TimeOut(null, val, false)); + } + recons.clear(); + while(true){ + TimeOut timeOut = poll(pq); + if (timeOut == null) { + break; + } + if(getDeadNano(timeOut) % 2 == 1) { + recons.add(getDeadNano(timeOut)); + } + } + + Collections.sort(recons); + + for (Iterator i = sorted.iterator(); i.hasNext(); ) + if ((i.next().intValue() % 2) != 1) + i.remove(); + + assertTrue(recons.equals(sorted), "Odd Sort failed"); + } +} diff --git a/test/com/alibaba/wisp/timer/SleepRPCTest.java b/test/com/alibaba/wisp/timer/SleepRPCTest.java new file mode 100644 index 0000000000000000000000000000000000000000..d4c38f3bdc22834d3840ed1d4f14ce6ec4b43a3c --- /dev/null +++ b/test/com/alibaba/wisp/timer/SleepRPCTest.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * @test + * @summary test use sleep in RPC senorina + * @requires os.family == "linux" + * @library /lib/testlibrary + * @run main/othervm -XX:+EnableCoroutine -XX:+UseWispMonitor -Dcom.alibaba.wisp.transparentWispSwitch=true SleepRPCTest + * @run main/othervm -XX:+EnableCoroutine -XX:+UseWispMonitor -Dcom.alibaba.wisp.transparentWispSwitch=true -Dcom.alibaba.wisp.highPrecisionTimer=true SleepRPCTest + */ + +import com.alibaba.wisp.engine.WispEngine; + +import java.util.List; +import java.util.Random; +import java.util.concurrent.Future; +import java.util.concurrent.FutureTask; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +import static jdk.testlibrary.Asserts.assertTrue; + +public class SleepRPCTest { + public static void main(String[] args) { + + List> rpcs = IntStream.range(0, 50).mapToObj(i -> { + AtomicBoolean done = new AtomicBoolean(false); + FutureTask ft = new FutureTask<>(() -> { + for (int n = 0; !done.get() && n < 40; n++) { + Thread.sleep(5); + } // 200 ms timeout + return done.get(); + }); + WispEngine.dispatch(ft); + WispEngine.dispatch(new FutureTask<>(() -> { + Thread.sleep(new Random().nextInt(100) + 1); + done.set(true); + return 0; + })); + + return ft; + }).collect(Collectors.toList()); + + assertTrue(rpcs.stream().allMatch(ft -> { + try { + return ft.get(1, TimeUnit.SECONDS); + } catch (Throwable e) { + return false; + } + })); + } +} diff --git a/test/com/alibaba/wisp/timer/SleepTest.java b/test/com/alibaba/wisp/timer/SleepTest.java new file mode 100644 index 0000000000000000000000000000000000000000..33e15bf2a8ae6ca7b050b2aabdf995c2e65cfd13 --- /dev/null +++ b/test/com/alibaba/wisp/timer/SleepTest.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * @test + * @summary test sleep + * @requires os.family == "linux" + * @library /lib/testlibrary + * @run main/othervm -XX:+EnableCoroutine -XX:+UseWispMonitor -Dcom.alibaba.wisp.transparentWispSwitch=true SleepTest + * @run main/othervm -XX:+EnableCoroutine -XX:+UseWispMonitor -Dcom.alibaba.wisp.transparentWispSwitch=true -Dcom.alibaba.wisp.highPrecisionTimer=true SleepTest + */ + +import com.alibaba.wisp.engine.WispEngine; + +import java.util.concurrent.FutureTask; +import java.util.concurrent.TimeUnit; +import java.util.stream.IntStream; + +import static jdk.testlibrary.Asserts.assertTrue; + +public class SleepTest { + public static void main(String[] args) { + assertTrue(IntStream.range(0, 100).parallel().allMatch(ms -> { + long start = System.currentTimeMillis(); + FutureTask ft = new FutureTask<>(() -> { + Thread.sleep(ms); + return 0; + }); + WispEngine.dispatch(ft); + try { + ft.get(200, TimeUnit.MILLISECONDS); + } catch (Throwable e) { + throw new Error(e); + } + + long interval = System.currentTimeMillis() - start; + + if (Math.abs(interval - ms) < 100) { + return true; + } else { + System.out.println("ms = " + ms); + System.out.println("interval = " + interval); + return false; + } + })); + } +} diff --git a/test/com/alibaba/wisp/timer/TimerTest.java b/test/com/alibaba/wisp/timer/TimerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..42b0de9b5ed96f1bca6fdfe0ccd6894c83b40b70 --- /dev/null +++ b/test/com/alibaba/wisp/timer/TimerTest.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * @test + * @summary Test timer implement + * @requires os.family == "linux" + * @run main/othervm -XX:+EnableCoroutine TimerTest + * @run main/othervm -XX:+EnableCoroutine -Dcom.alibaba.wisp.highPrecisionTimer=true TimerTest + */ + + +import com.alibaba.wisp.engine.WispEngine; +import sun.misc.SharedSecrets; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + + +/** + * test the time out implement + */ +public class TimerTest { + public static void main(String... arg) throws Exception { + CountDownLatch latch = new CountDownLatch(1); + WispEngine.dispatch(() -> { + long ts = System.currentTimeMillis(); + for (int i = 0; i < 10; i++) { + SharedSecrets.getWispEngineAccess().sleep(100); + long now = System.currentTimeMillis(); + if (Math.abs(now - ts - 100) > 10) + throw new Error(); + ts = now; + } + latch.countDown(); + }); + latch.await(5, TimeUnit.SECONDS); + } +} diff --git a/test/com/alibaba/wisp2/AdjustCarrierTest.java b/test/com/alibaba/wisp2/AdjustCarrierTest.java new file mode 100644 index 0000000000000000000000000000000000000000..241d89424dde03bfaad7767ab0f1251777c7391e --- /dev/null +++ b/test/com/alibaba/wisp2/AdjustCarrierTest.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * @test + * @library /lib/testlibrary + * @summary test for adjusting carrier number at runtime + * @requires os.family == "linux" + * @run main/othervm -XX:+UseWisp2 -Dcom.alibaba.wisp.growCarrierTickUs=200000 AdjustCarrierTest + */ + + +import com.alibaba.wisp.engine.WispEngine; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; + +import static jdk.testlibrary.Asserts.assertTrue; + +public class AdjustCarrierTest { + public static void main(String[] args) throws Exception { + Thread.currentThread().setName("Wisp-Sysmon"); + Class claz = Class.forName("com.alibaba.wisp.engine.WispScheduler"); + Method method = claz.getDeclaredMethod("checkAndGrowWorkers", int.class); + method.setAccessible(true); + ExecutorService g = WispEngine.createEngine(4, Thread::new); + + CountDownLatch latch = new CountDownLatch(100); + CountDownLatch grow = new CountDownLatch(1); + + for (int i = 0; i < 50; i++) { + g.execute(() -> { + try { + grow.await(); + latch.countDown(); + } catch (Exception e) { + e.printStackTrace(); + } + }); + } + + Field scheduler = Class.forName("com.alibaba.wisp.engine.WispEngine").getDeclaredField("scheduler"); + scheduler.setAccessible(true); + method.invoke(scheduler.get(g), 100); + grow.countDown(); + + for (int i = 0; i < 50; i++) { + g.execute(latch::countDown); + } + + latch.await(); + Field f = claz.getDeclaredField("workers"); + f.setAccessible(true); + System.out.println(f.get(scheduler.get(g)).getClass().toString()); + assertTrue(((Object[]) (f.get(scheduler.get(g)))).length == 100); + Field name = Class.forName("com.alibaba.wisp.engine.WispSysmon").getDeclaredField("WISP_SYSMON_NAME"); + name.setAccessible(true); + assertTrue(name.get(null).equals("Wisp-Sysmon")); + } +} diff --git a/test/com/alibaba/wisp2/AllThreadAsWispTest.java b/test/com/alibaba/wisp2/AllThreadAsWispTest.java new file mode 100644 index 0000000000000000000000000000000000000000..45d526d44a8da13404c65cbef99130c029e0c55d --- /dev/null +++ b/test/com/alibaba/wisp2/AllThreadAsWispTest.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * @test + * @library /lib/testlibrary + * @summary convert all thread to wisp + * @requires os.family == "linux" + * @run main/othervm -XX:+EnableCoroutine -Dcom.alibaba.wisp.transparentWispSwitch=true -Dcom.alibaba.wisp.version=2 -XX:+UseWispMonitor -Dcom.alibaba.wisp.enableThreadAsWisp=true -Dcom.alibaba.wisp.allThreadAsWisp=true AllThreadAsWispTest + */ + +import sun.misc.SharedSecrets; + +import java.util.Timer; +import java.util.TimerTask; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +import static jdk.testlibrary.Asserts.assertTrue; + +public class AllThreadAsWispTest { + public static void main(String[] args) throws Exception { + CountDownLatch done = new CountDownLatch(60); + + Runnable r = () -> { + if (SharedSecrets.getJavaLangAccess().currentThread0() != Thread.currentThread()) { + done.countDown(); + } + }; + + for (int i = 0; i < 10; i++) { + Executors.newSingleThreadExecutor().submit(r); + Executors.newWorkStealingPool().submit(r); + Executors.newScheduledThreadPool(100).submit(r); + new Thread(r).start(); + new Timer().schedule(new TimerTask() { + @Override + public void run() { + r.run(); + } + }, 0); + new Thread() { + @Override + public void run() { + r.run(); + } + }.start(); + } + + assertTrue(done.await(10, TimeUnit.SECONDS)); + } +} diff --git a/test/com/alibaba/wisp2/CarrierAsPollerTest.java b/test/com/alibaba/wisp2/CarrierAsPollerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..be9922c563da3ed7fe35aeabebd5b02329abed6e --- /dev/null +++ b/test/com/alibaba/wisp2/CarrierAsPollerTest.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * @test + * @library /lib/testlibrary + * @summary verify carrier is doing epoll instead of poller when useCarrierAsPoller is enabled + * @requires os.family == "linux" + * @run main/othervm -XX:+UseWisp2 -Dcom.alibaba.wisp.useCarrierAsPoller=true CarrierAsPollerTest + * @run main/othervm -XX:+UseWisp2 -Dcom.alibaba.wisp.useCarrierAsPoller=false CarrierAsPollerTest + */ + + +import java.lang.reflect.Field; + +import static jdk.testlibrary.Asserts.*; + +public class CarrierAsPollerTest { + public static void main(String[] args) throws Exception { + new Thread(() -> {}).start(); // if we're owner..let another carrier become owner + long start = System.currentTimeMillis(); + while (System.currentTimeMillis() - start < 200) { + // wait acquire + } + + Class clazz = Class.forName("com.alibaba.wisp.engine.WispEventPump$Pool"); + Field pumps = clazz.getDeclaredField("pumps"); + pumps.setAccessible(true); + Object[] a = (Object[]) pumps.get(clazz.getEnumConstants()[0]); + Field ownerField = Class.forName("com.alibaba.wisp.engine.WispEventPump").getDeclaredField("owner"); + ownerField.setAccessible(true); + Object owner = ownerField.get(a[0]); + if (!Boolean.getBoolean("com.alibaba.wisp.useCarrierAsPoller")) { + assertNull(owner); + return; + } + + assertNotNull(owner); + Field threadField = owner.getClass().getDeclaredField("thread"); + threadField.setAccessible(true); + Thread thread = (Thread) threadField.get(owner); + System.out.println(thread.getName()); + assertTrue(thread.getName().startsWith("Wisp-Root-Worker")); + } +} diff --git a/test/com/alibaba/wisp2/CtxClassLoaderInheritanceTest.java b/test/com/alibaba/wisp2/CtxClassLoaderInheritanceTest.java new file mode 100644 index 0000000000000000000000000000000000000000..c3830d96c087962f7a95eb8b0fdf49e2aa8c54c6 --- /dev/null +++ b/test/com/alibaba/wisp2/CtxClassLoaderInheritanceTest.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * @test + * @library /lib/testlibrary + * @summary test context ClassLoader inherit. + * @requires os.family == "linux" + * @run main/othervm -XX:+EnableCoroutine -Dcom.alibaba.wisp.transparentWispSwitch=true -Dcom.alibaba.wisp.version=2 -XX:+UseWispMonitor -Dcom.alibaba.wisp.enableThreadAsWisp=true -Dcom.alibaba.wisp.allThreadAsWisp=true CtxClassLoaderInheritanceTest + */ + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import static jdk.testlibrary.Asserts.assertEQ; + +public class CtxClassLoaderInheritanceTest { + public static void main(String[] args) throws Exception { + ClassLoader cl = new ClassLoader() { + }; + + Thread.currentThread().setContextClassLoader(cl); + ClassLoader[] childCtxCl = new ClassLoader[1]; + CountDownLatch done = new CountDownLatch(1); + new Thread(() -> { + childCtxCl[0] = Thread.currentThread().getContextClassLoader(); + done.countDown(); + }).start(); + + done.await(1, TimeUnit.SECONDS); + assertEQ(cl, childCtxCl[0]); + } +} diff --git a/test/com/alibaba/wisp2/DispatchTest.java b/test/com/alibaba/wisp2/DispatchTest.java new file mode 100644 index 0000000000000000000000000000000000000000..97a8d5787e0670111e1de1692e59246725d7b701 --- /dev/null +++ b/test/com/alibaba/wisp2/DispatchTest.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * @test + * @summary basic wisp2 + * @requires os.family == "linux" + * @run main/othervm -XX:+EnableCoroutine -Dcom.alibaba.wisp.transparentWispSwitch=true -Dcom.alibaba.wisp.version=2 -XX:+UseWispMonitor DispatchTest + */ + + +import com.alibaba.wisp.engine.WispEngine; +import sun.misc.SharedSecrets; + +public class DispatchTest { + public static void main(String[] args) throws Exception { + WispEngine.dispatch(() -> { + for (int i = 0; i < 9999999; i++) { + try { + Thread.sleep(100); + System.out.println(i + ": " + SharedSecrets.getJavaLangAccess().currentThread0()); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + }); + System.out.println("DispatchTest.main"); + Thread.sleep(1000); + } +} diff --git a/test/com/alibaba/wisp2/EpollWakeupPerfTest.java b/test/com/alibaba/wisp2/EpollWakeupPerfTest.java new file mode 100644 index 0000000000000000000000000000000000000000..1cfc88aa99e337f346f3f904388795b38356db74 --- /dev/null +++ b/test/com/alibaba/wisp2/EpollWakeupPerfTest.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * @test + * @summary test selector.wakeup() performance + * @requires os.family == "linux" + * @run main/othervm -XX:+UseWisp2 EpollWakeupPerfTest + */ + +import java.nio.channels.Selector; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.CyclicBarrier; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; + +public class EpollWakeupPerfTest { + public static void main(String[] args) throws Exception { + Selector selector = Selector.open(); + AtomicLong wakenedTs = new AtomicLong(); + CyclicBarrier barrier = new CyclicBarrier(2); + + Thread io = new Thread(() -> { + try { + while (true) { + barrier.await(); + selector.select(); + wakenedTs.lazySet(System.nanoTime()); + synchronized (lock) { + lock.notify(); + } + } + + } catch (Throwable t) { + } + }, "IO thread"); + + io.start(); + + Executors.newScheduledThreadPool(1).scheduleAtFixedRate(() -> { + System.out.println("tested " + k + " times, " + + "average latency=" + m + "ns"); + synchronized (lock) { + m = k = 0; + } + }, 1, 1, TimeUnit.SECONDS); + long start = System.currentTimeMillis(); + while (System.currentTimeMillis() - start < 3_000) { + wakenedTs.set(0); + selector.selectNow(); // reset + barrier.await(); // start... + new CountDownLatch(1).await(100, TimeUnit.MICROSECONDS); // ensure select() + long wakeupTs = System.nanoTime(); + selector.wakeup(); + + synchronized (lock) { + while (wakenedTs.get() == 0) { + lock.wait(); + } + } + long latency = wakenedTs.get() - wakeupTs; + if (latency > 0) { + synchronized (lock) { + m += k++ == 0 ? latency : (latency - m) / k; + } + } + } + } + + private static long m, k; + private final static Object lock = new Object(); +} diff --git a/test/com/alibaba/wisp2/HandOffTest.java b/test/com/alibaba/wisp2/HandOffTest.java new file mode 100644 index 0000000000000000000000000000000000000000..bddca16d88e0a16d684cafec4529e5bef8ea12a7 --- /dev/null +++ b/test/com/alibaba/wisp2/HandOffTest.java @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * @test + * @library /lib/testlibrary + * @summary test long running or blocking syscall task could be retaken + * @requires os.family == "linux" + * @run main/othervm -Dcom.alibaba.wisp.carrierEngines=1 -XX:-UseBiasedLocking -XX:+EnableCoroutine -XX:+UseWispMonitor -Dcom.alibaba.wisp.transparentWispSwitch=true -Dcom.alibaba.wisp.version=2 -Dcom.alibaba.wisp.enableHandOff=true -Dcom.alibaba.wisp.handoffPolicy=ADAPTIVE -Dcom.alibaba.wisp.sysmonTickUs=100000 HandOffTest + */ + +import com.alibaba.wisp.engine.WispEngine; +import sun.misc.SharedSecrets; +import sun.misc.WispEngineAccess; + +import java.io.IOException; +import java.lang.reflect.Field; +import java.net.InetSocketAddress; +import java.nio.channels.ClosedChannelException; +import java.nio.channels.SocketChannel; +import java.nio.ByteBuffer; +import java.util.Properties; +import java.util.Set; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; + +import static jdk.testlibrary.Asserts.assertTrue; + +public class HandOffTest { + private static WispEngineAccess WEA = SharedSecrets.getWispEngineAccess(); + + static Properties p; + static String socketAddr; + static { + p = java.security.AccessController.doPrivileged( + new java.security.PrivilegedAction() { + public Properties run() { + return System.getProperties(); + } + } + ); + socketAddr = (String)p.get("test.wisp.socketAddress"); + if (socketAddr == null) { + socketAddr = "www.example.com"; + } + } + + public static void main(String[] args) throws Exception { + CountDownLatch cl = new CountDownLatch(10); + for (int i = 0; i < 10; i++) { + WispEngine.dispatch(() -> { + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + cl.countDown(); + }); + } + + AtomicBoolean blockingFinish = new AtomicBoolean(false); + SocketChannel ch[] = new SocketChannel[1]; + + WispEngine.dispatch(() -> { + try { + ch[0] = SocketChannel.open(new InetSocketAddress(socketAddr, 80)); + ch[0].read(ByteBuffer.allocate(4096)); + blockingFinish.set(true); + } catch (IOException e) { + if (! (e instanceof ClosedChannelException)) { + e.printStackTrace(); + } + } + }); + + AtomicInteger integer = new AtomicInteger(-2); + // test we won't lose wisptasks; + CountDownLatch latch = new CountDownLatch(1); + WispEngine.dispatch(() ->{ + try { + latch.await(); + } catch (Exception e) { + e.printStackTrace(); + } + integer.incrementAndGet(); + }); + + + WispEngine.dispatch(() -> { + try { + Thread.sleep(999999999); + } catch (Exception e) { + e.printStackTrace(); + } + integer.set(-99); + }); + + + WispEngine.dispatch(() -> { + try { + Thread.sleep(5000); + } catch (Exception e) { + e.printStackTrace(); + } + integer.incrementAndGet(); + }); + + assertTrue(cl.await(5, TimeUnit.SECONDS)); + assertTrue(!blockingFinish.get()); + + latch.countDown(); + Thread.sleep(100); + while (integer.get() != 0) { + // System.out.println("spin...."); + } + + ch[0].close(); + // wait until hand off carrier die + // TODO: enable handoff exit +// Field field = WispEngine.class.getDeclaredField("carrierThreads"); +// field.setAccessible(true); +// Set set = (Set)field.get(null); +// Thread.sleep(200); +// assertTrue(set.size() == 1); + } +} diff --git a/test/com/alibaba/wisp2/HandOffWakeUpTest.java b/test/com/alibaba/wisp2/HandOffWakeUpTest.java new file mode 100644 index 0000000000000000000000000000000000000000..5ef4b074944cda968eeda53ab54dacd92469fe43 --- /dev/null +++ b/test/com/alibaba/wisp2/HandOffWakeUpTest.java @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * @test + * @library /lib/testlibrary + * @summary test long running or blocking syscall task could be retaken + * @requires os.family == "linux" + * @run main/othervm -Dcom.alibaba.wisp.carrierEngines=1 -XX:-UseBiasedLocking -XX:+EnableCoroutine -XX:+UseWispMonitor -Dcom.alibaba.wisp.transparentWispSwitch=true -Dcom.alibaba.wisp.version=2 -Dcom.alibaba.wisp.enableHandOff=true -Dcom.alibaba.wisp.sysmonTickUs=100000 HandOffWakeUpTest + */ + +import com.alibaba.wisp.engine.WispEngine; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.nio.ByteBuffer; +import java.nio.channels.ClosedChannelException; +import java.nio.channels.SocketChannel; +import java.util.Properties; +import java.util.concurrent.atomic.AtomicBoolean; + +public class HandOffWakeUpTest { + + static Properties p; + static String socketAddr; + static { + p = java.security.AccessController.doPrivileged( + new java.security.PrivilegedAction() { + public Properties run() { + return System.getProperties(); + } + } + ); + socketAddr = (String)p.get("test.wisp.socketAddress"); + if (socketAddr == null) { + socketAddr = "www.example.com"; + } + } + + public static void main(String[] args) throws Exception{ + + boolean[] booleans = new boolean[1]; + booleans[0] = true; + + WispEngine.dispatch(() -> { + try { + Thread.sleep(9999999); + } catch (InterruptedException e) { + e.printStackTrace(); + } + booleans[0] = false; + }); + + AtomicBoolean blockingFinish = new AtomicBoolean(false); + + SocketChannel ch[] = new SocketChannel[1]; + + WispEngine.dispatch(() -> { + try { + ch[0] = SocketChannel.open(new InetSocketAddress(socketAddr, 80)); + ch[0].read(ByteBuffer.allocate(4096)); + blockingFinish.set(true); + } catch (IOException e) { + if (! (e instanceof ClosedChannelException)) { + e.printStackTrace(); + } + } + }); + + Thread.sleep(2000); + ch[0].close(); + if (booleans[0] != true) { + throw new Error("waken up unexpectedly"); + } + } +} diff --git a/test/com/alibaba/wisp2/HandOffWithStealTest.java b/test/com/alibaba/wisp2/HandOffWithStealTest.java new file mode 100644 index 0000000000000000000000000000000000000000..608ea6091b32a99b320291d2084f7536bedb3a40 --- /dev/null +++ b/test/com/alibaba/wisp2/HandOffWithStealTest.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * @test + * @library /lib/testlibrary + * @summary test long running or blocking syscall task could be retaken + * @requires os.family == "linux" + * @run main/othervm -Dcom.alibaba.wisp.carrierEngines=1 -XX:+UseWisp2 -Dcom.alibaba.wisp.handoffPolicy=ADAPTIVE HandOffWithStealTest + */ + +import com.alibaba.wisp.engine.WispEngine; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; + +import static jdk.testlibrary.Asserts.assertTrue; + +public class HandOffWithStealTest { + public static void main(String[] args) throws Exception { + final int N = 100; + CountDownLatch cl = new CountDownLatch(N); + for (int i = 0; i < N; i++) { + WispEngine.dispatch(() -> { + try { + Thread.sleep(2000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + cl.countDown(); + }); + } + + AtomicBoolean blockingFinish = new AtomicBoolean(false); + + WispEngine.dispatch(() -> { + try { + String[] cmdA = { "/bin/sh", "-c", " sleep 200"}; + Process process = Runtime.getRuntime().exec(cmdA); + process.waitFor(); + blockingFinish.set(true); + } catch (Exception e) { + e.printStackTrace(); + } + }); + + assertTrue(cl.await(3, TimeUnit.SECONDS) && !blockingFinish.get()); + } +} diff --git a/test/com/alibaba/wisp2/MassiveIOTest.java b/test/com/alibaba/wisp2/MassiveIOTest.java new file mode 100644 index 0000000000000000000000000000000000000000..bace9e45afd504e746c319327f4644aae814d0e2 --- /dev/null +++ b/test/com/alibaba/wisp2/MassiveIOTest.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * @test + * @library /lib/testlibrary + * @summary test massive IO + * @requires os.family == "linux" + * @run main/othervm -XX:+UseWisp2 MassiveIOTest + * @run main/othervm -XX:+UseWisp2 -Dcom.alibaba.pollerShardingSize=0 MassiveIOTest + * @run main/othervm -XX:+UseWisp2 -Dcom.alibaba.pollerShardingSize=1000 MassiveIOTest + */ +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.ServerSocket; +import java.net.Socket; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +public class MassiveIOTest { + private static final Executor es = Executors.newCachedThreadPool(); + public static void main(String[] args) throws Exception { + ServerSocket server = new ServerSocket(0); + CompletableFuture.runAsync(() -> echoServer(server), es); + IntStream.range(0, Math.max(1, Runtime.getRuntime().availableProcessors() / 2)) + .mapToObj(i -> CompletableFuture.runAsync(() -> client(server.getLocalPort()), es)) + .collect(Collectors.toList()) + .forEach(CompletableFuture::join); + } + private static void client(int serverPort) { + try { + Socket so = new Socket("localhost", serverPort); + byte[] buffer = new byte[100]; + InputStream is = so.getInputStream(); + OutputStream os = so.getOutputStream(); + for (int i = 0; i < 100000; i++) { + os.write(buffer); + is.read(buffer); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + private static void echoServer(ServerSocket server) { + while (true) { + try { + Socket client = server.accept(); + CompletableFuture.runAsync(() -> echoHandler(client), es); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + private static void echoHandler(Socket client) { + System.out.println("Start serving " + client); + byte[] buffer = new byte[1024]; + try { + InputStream is = client.getInputStream(); + OutputStream os = client.getOutputStream(); + while (true) { + os.write(buffer, 0, is.read(buffer)); + } + } catch (IOException e) { + e.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/test/com/alibaba/wisp2/MonolithicPollTest.java b/test/com/alibaba/wisp2/MonolithicPollTest.java new file mode 100644 index 0000000000000000000000000000000000000000..2db277caaba86d2447dd3c4547ef309c75ab4a34 --- /dev/null +++ b/test/com/alibaba/wisp2/MonolithicPollTest.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * @test + * @library /lib/testlibrary + * @summary verify epollArray is set for Selector.select() + * @requires os.family == "linux" + * @run main/othervm -XX:+UseWisp2 -Dcom.alibaba.wisp.monolithicPoll=true MonolithicPollTest + * @run main/othervm -XX:+UseWisp2 -Dcom.alibaba.wisp.monolithicPoll=false MonolithicPollTest + */ + +import com.alibaba.wisp.engine.WispTask; +import sun.misc.SharedSecrets; + +import java.lang.reflect.Field; +import java.nio.channels.Selector; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicReference; + +import static jdk.testlibrary.Asserts.assertEQ; + +public class MonolithicPollTest { + public static void main(String[] args) throws Exception { + AtomicReference task = new AtomicReference<>(); + Executors.newSingleThreadExecutor().submit(() -> { + task.set(SharedSecrets.getWispEngineAccess().getCurrentTask()); + Selector.open().select(); + return null; + }); + + Thread.sleep(200); + + while (task.get() == null) { + } + + Boolean nz = Executors.newSingleThreadExecutor().submit(() -> { + Field arrayField = WispTask.class.getDeclaredField("epollArray"); + arrayField.setAccessible(true); + long array = arrayField.getLong(task.get()); + return array != 0; + }).get(); + + assertEQ(nz, Boolean.getBoolean("com.alibaba.wisp.monolithicPoll")); + } +} diff --git a/test/com/alibaba/wisp2/NioBlockingAcceptTest.java b/test/com/alibaba/wisp2/NioBlockingAcceptTest.java new file mode 100644 index 0000000000000000000000000000000000000000..7da918dc30aca61c2c61eee9d7cfc5870a10a9af --- /dev/null +++ b/test/com/alibaba/wisp2/NioBlockingAcceptTest.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * @test + * @library /lib/testlibrary + * @summary test nio blocking accept + * @requires os.family == "linux" + * @run main/othervm -Dcom.alibaba.wisp.carrierEngines=1 -XX:ActiveProcessorCount=1 -XX:+EnableCoroutine -Dcom.alibaba.wisp.transparentWispSwitch=true -Dcom.alibaba.wisp.version=2 -XX:+UseWispMonitor -Dcom.alibaba.wisp.enableThreadAsWisp=true -Dcom.alibaba.wisp.allThreadAsWisp=true NioBlockingAcceptTest + */ + +import com.alibaba.wisp.engine.WispEngine; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.nio.channels.ServerSocketChannel; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import static jdk.testlibrary.Asserts.assertTrue; + +public class NioBlockingAcceptTest { + public static void main(String[] args) throws Exception { + WispEngine.dispatch(() -> { + try { + final ServerSocketChannel ssc = ServerSocketChannel.open(); + ssc.bind(new InetSocketAddress(0)); + ssc.accept(); + } catch (IOException e) { + e.printStackTrace(); + } + }); + final CountDownLatch latch = new CountDownLatch(1); + WispEngine.dispatch(latch::countDown); + + assertTrue(latch.await(1, TimeUnit.SECONDS)); + } +} diff --git a/test/com/alibaba/wisp2/ProfileWithHandOffTest.java b/test/com/alibaba/wisp2/ProfileWithHandOffTest.java new file mode 100644 index 0000000000000000000000000000000000000000..1842f113476676749a053db6c83b74c03a929846 --- /dev/null +++ b/test/com/alibaba/wisp2/ProfileWithHandOffTest.java @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * @test + * @library /lib/testlibrary + * @summary test after long running or blocking syscall task could be retaken, the new carrier thread can be profiled. + * @requires os.family == "linux" + * @run main/othervm -Dcom.alibaba.wisp.carrierEngines=1 -XX:-UseBiasedLocking -XX:+EnableCoroutine -XX:+UseWispMonitor -Dcom.alibaba.wisp.transparentWispSwitch=true -Dcom.alibaba.wisp.version=2 -Dcom.alibaba.wisp.enableHandOff=true -Dcom.alibaba.wisp.handoffPolicy=ADAPTIVE -Dcom.alibaba.wisp.enablePerfLog=true -Dcom.alibaba.wisp.logTimeInternalMillis=1000 ProfileWithHandOffTest + */ + +import com.alibaba.wisp.engine.WispEngine; +import com.alibaba.management.WispCounterMXBean; +import sun.misc.SharedSecrets; + +import javax.management.MBeanServer; +import java.io.IOException; +import java.lang.management.ManagementFactory; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +import static jdk.testlibrary.Asserts.assertTrue; + +public class ProfileWithHandOffTest { + public static void main(String[] args) throws Exception { + CountDownLatch cl = new CountDownLatch(10); + AtomicInteger cnt = new AtomicInteger(); + for (int i = 0; i < 10; i++) { + WispEngine.dispatch(() -> { + try { + Thread.sleep(1000); + cnt.incrementAndGet(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + cl.countDown(); + }); + } + + WispEngine.dispatch(() -> { + try { + for(int j = 0; j < 30; j++) { + int i = System.in.read(); + System.out.println("your input: " + i); + } + } catch (IOException e) { + e.printStackTrace(); + } + }); + + MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); + WispCounterMXBean mbean = null; + try { + mbean = ManagementFactory.newPlatformMXBeanProxy(mbs, + "com.alibaba.management:type=WispCounter", WispCounterMXBean.class); + } catch (IOException e) { + e.printStackTrace(); + } + + System.out.println(mbean.getRunningStates()); + System.out.println(mbean.getQueueLength()); + System.out.println(mbean.getNumberOfRunningTasks()); + assertTrue(cl.await(5, TimeUnit.SECONDS)); + + System.out.println(mbean.getCreateTaskCount()); + System.out.println(mbean.getCompleteTaskCount()); + } +} diff --git a/test/com/alibaba/wisp2/ProfileWithHandOffTest2.java b/test/com/alibaba/wisp2/ProfileWithHandOffTest2.java new file mode 100644 index 0000000000000000000000000000000000000000..d9de11cadb4b2090bbbc189aad401ba0ae422226 --- /dev/null +++ b/test/com/alibaba/wisp2/ProfileWithHandOffTest2.java @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * @test + * @library /lib/testlibrary + * @summary test after long running or blocking syscall task could be retaken, the new carrier thread can be profiled. + * @requires os.family == "linux" + * @run main/othervm -Dcom.alibaba.wisp.carrierEngines=1 -XX:-UseBiasedLocking -XX:+EnableCoroutine -XX:+UseWispMonitor -Dcom.alibaba.wisp.transparentWispSwitch=true -Dcom.alibaba.wisp.version=2 -Dcom.alibaba.wisp.enableHandOff=true -Dcom.alibaba.wisp.handoffPolicy=ADAPTIVE -Dcom.alibaba.wisp.sysmonTickUs=100000 -Dcom.alibaba.wisp.enablePerfLog=true -Dcom.alibaba.wisp.logTimeInternalMillis=1000 ProfileWithHandOffTest2 + */ + +import com.alibaba.wisp.engine.WispEngine; +import com.alibaba.management.WispCounterMXBean; +import sun.misc.SharedSecrets; + +import javax.management.MBeanServer; +import java.io.IOException; +import java.lang.management.ManagementFactory; +import java.net.InetSocketAddress; +import java.nio.channels.SocketChannel; +import java.nio.ByteBuffer; +import java.util.Properties; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; + +import static jdk.testlibrary.Asserts.assertTrue; + +public class ProfileWithHandOffTest2 { + + static Properties p; + static String socketAddr; + static { + p = java.security.AccessController.doPrivileged( + new java.security.PrivilegedAction() { + public Properties run() { + return System.getProperties(); + } + } + ); + socketAddr = (String)p.get("test.wisp.socketAddress"); + if (socketAddr == null) { + socketAddr = "www.example.com"; + } + } + + public static void main(String[] args) throws Exception { + CountDownLatch cl = new CountDownLatch(10); + for (int i = 0; i < 10; i++) { + WispEngine.dispatch(() -> { + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + cl.countDown(); + }); + } + + AtomicBoolean blockingFinish = new AtomicBoolean(false); + + WispEngine.dispatch(() -> { + try { + SocketChannel ch = SocketChannel.open(new InetSocketAddress(socketAddr, 80)); + ch.read(ByteBuffer.allocate(4096)); + blockingFinish.set(true); + } catch (IOException e) { + e.printStackTrace(); + } + }); + + MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); + WispCounterMXBean mbean = null; + try { + mbean = ManagementFactory.newPlatformMXBeanProxy(mbs, + "com.alibaba.management:type=WispCounter", WispCounterMXBean.class); + } catch (IOException e) { + e.printStackTrace(); + } + + System.out.println(mbean.getRunningStates()); + System.out.println(mbean.getQueueLength()); + System.out.println(mbean.getNumberOfRunningTasks()); + System.out.println(mbean.getCreateTaskCount()); + + assertTrue(cl.await(5, TimeUnit.SECONDS)); + assertTrue(!blockingFinish.get()); + System.out.println(mbean.getNumberOfRunningTasks()); + System.out.println(mbean.getCreateTaskCount()); + System.out.println(mbean.getCompleteTaskCount()); + } +} diff --git a/test/com/alibaba/wisp2/RemoveWispParentTest.java b/test/com/alibaba/wisp2/RemoveWispParentTest.java new file mode 100644 index 0000000000000000000000000000000000000000..4da20a60633cd57fc21d06fc4b8e876b357874cd --- /dev/null +++ b/test/com/alibaba/wisp2/RemoveWispParentTest.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * @test + * @summary Test a WispTask will not block when it created a new one and didn't yield to its parent. + * @requires os.family == "linux" + * @library /lib/testlibrary + * @run main/othervm -XX:+UseWisp2 RemoveWispParentTest + */ + +import com.alibaba.wisp.engine.WispEngine; + +import java.util.*; +import java.util.concurrent.*; + +import static jdk.testlibrary.Asserts.assertTrue; + +public class RemoveWispParentTest { + static CountDownLatch latch = new CountDownLatch(3); + + // mainThreadCreateWisp create a new wisp task in main thread, check whether + // main thread will be blocked. + static void mainThreadCreateWisp() { + WispEngine.dispatch(() -> { + latch.countDown(); + }); + } + + // wispTaskCreateWisp create a new wisp task in wispTask, check whether + // creater wispTask will be blocked. + static void wispTaskCreateWisp() { + WispEngine.dispatch(() -> { + new Thread(() -> { + latch.countDown(); + }).start(); + latch.countDown(); + }); + } + + public static void main(String[] args) throws Exception { + mainThreadCreateWisp(); + wispTaskCreateWisp(); + assertTrue(latch.await(1, TimeUnit.SECONDS)); + } +} \ No newline at end of file diff --git a/test/com/alibaba/wisp2/ReuseWispTaskAfterThreadJoinTest.java b/test/com/alibaba/wisp2/ReuseWispTaskAfterThreadJoinTest.java new file mode 100644 index 0000000000000000000000000000000000000000..3f519094fdec860a621574d9365e0b92cb11963b --- /dev/null +++ b/test/com/alibaba/wisp2/ReuseWispTaskAfterThreadJoinTest.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * @test + * @library /lib/testlibrary + * @summary test wisp task reusing after thread.join() + * @requires os.family == "linux" + * @run main/othervm -XX:+EnableCoroutine -Dcom.alibaba.wisp.transparentWispSwitch=true -Dcom.alibaba.wisp.version=2 -XX:+UseWispMonitor -Dcom.alibaba.wisp.enableThreadAsWisp=true -Dcom.alibaba.wisp.allThreadAsWisp=true ReuseWispTaskAfterThreadJoinTest + */ + +import com.alibaba.wisp.engine.WispEngine; + +public class ReuseWispTaskAfterThreadJoinTest { + public static void main(String[] args) throws Exception { + Thread t = new Thread(() -> { + + + }); + t.start(); + t.join(); + // really exited. + // reuse the wispTask + WispEngine.dispatch(() -> { + try { + Thread.sleep(200000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + }); + Thread.sleep(1000); + t.join(); + } +} diff --git a/test/com/alibaba/wisp2/ThreadAsWispBlackListTest.java b/test/com/alibaba/wisp2/ThreadAsWispBlackListTest.java new file mode 100644 index 0000000000000000000000000000000000000000..d6104f99492c406c86f452b5300f0db8575fca2d --- /dev/null +++ b/test/com/alibaba/wisp2/ThreadAsWispBlackListTest.java @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * @test + * @library /lib/testlibrary + * @summary test all thread as wisp black list + * @requires os.family == "linux" + * @run main/othervm -XX:+UseWisp2 ThreadAsWispBlackListTest + */ + +import sun.misc.SharedSecrets; + +import java.lang.reflect.Method; +import java.util.concurrent.Executors; +import java.util.concurrent.ForkJoinPool; +import java.util.concurrent.FutureTask; + +import static jdk.testlibrary.Asserts.assertFalse; +import static jdk.testlibrary.Asserts.assertTrue; + +public class ThreadAsWispBlackListTest { + public static void main(String[] args) throws Exception { + assertFalse(Executors.newSingleThreadExecutor().submit(ThreadAsWispBlackListTest::isRealThread).get()); + byClass(); + byPackage(); + byName(); + testCommonPool(); + testMultiEntry(); + wildcardTest(); + } + + private static void byPackage() throws Exception { + setBlackList("package:java.util.concurrent"); + assertTrue(Executors.newSingleThreadExecutor().submit(ThreadAsWispBlackListTest::isRealThread).get()); + } + + private static void byClass() throws Exception { + FutureTask future = new FutureTask<>(ThreadAsWispBlackListTest::isRealThread); + class T1 extends Thread { + @Override + public void run() { + future.run(); + } + } + setBlackList("class:" + T1.class.getName()); + new T1().start(); + assertTrue(future.get()); + } + + private static void byName() throws Exception { + wildcardTest1("n1111", "n1111"); + } + + private static void wildcardTest() throws Exception { + wildcardTest1("a-*", "a-1"); + wildcardTest1("a-*", "a-2"); + wildcardTest1("a-*", "a-4999"); + wildcardTest1("a-*", "a-abc"); + + wildcardTest1("a-*-b", "a-1-b"); + wildcardTest1("a-*-b", "a-2-b"); + wildcardTest1("a-*-b", "a-4999-b"); + wildcardTest1("a-*-b", "a-abc-b"); + + wildcardTest1("a-*z", "a-1-bz"); + wildcardTest1("a-*z", "a-2-bz"); + wildcardTest1("a-*z", "a-4999-bz"); + wildcardTest1("a-*z", "a-abc-bz"); + + wildcardTest1("*z", "a-1-bz"); + wildcardTest1("*z", "a-2-bz"); + wildcardTest1("*z", "a-4999-bz"); + wildcardTest1("*z", "a-abc-bz"); + + wildcardTest1("?z", "zz"); + wildcardTest1("a?z", "agz"); + wildcardTest1("a?", "a1"); + } + + private static void wildcardTest1(String pattern, String name) throws Exception { + setBlackList("name:" + pattern); + FutureTask future = new FutureTask<>(ThreadAsWispBlackListTest::isRealThread); + new Thread(future, name).start(); + assertTrue(future.get()); + } + + private static void testCommonPool() throws Exception { + setBlackList("name:ForkJoinPool.commonPool-worker-*"); + FutureTask future = new FutureTask<>(ThreadAsWispBlackListTest::isRealThread); + ForkJoinPool.commonPool().execute(future); + assertTrue(future.get()); + } + + private static void testMultiEntry() throws Exception { + setBlackList("name:m1111;name:m2222"); + FutureTask future = new FutureTask<>(ThreadAsWispBlackListTest::isRealThread); + new Thread(future, "m1111").start(); + assertTrue(future.get()); + future = new FutureTask<>(ThreadAsWispBlackListTest::isRealThread); + new Thread(future, "m2222").start(); + assertTrue(future.get()); + } + + private static boolean isRealThread() { + return SharedSecrets.getJavaLangAccess().currentThread0() == Thread.currentThread(); + } + + private static void setBlackList(String list) throws Exception { + System.setProperty("com.alibaba.wisp.threadAsWisp.black", list); + Method m = Class.forName("com.alibaba.wisp.engine.WispConfiguration").getDeclaredMethod("loadBizConfig"); + m.setAccessible(true); + m.invoke(null); + } +} diff --git a/test/com/alibaba/wisp2/ThreadJoinTest.java b/test/com/alibaba/wisp2/ThreadJoinTest.java new file mode 100644 index 0000000000000000000000000000000000000000..005a73a74a89c786cd4b0d1fadf04493a1dd7fac --- /dev/null +++ b/test/com/alibaba/wisp2/ThreadJoinTest.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * @test + * @library /lib/testlibrary + * @summary test thread.join() + * @requires os.family == "linux" + * @run main/othervm -XX:+EnableCoroutine -Dcom.alibaba.wisp.transparentWispSwitch=true -Dcom.alibaba.wisp.version=2 -XX:+UseWispMonitor -Dcom.alibaba.wisp.enableThreadAsWisp=true -Dcom.alibaba.wisp.allThreadAsWisp=true ThreadJoinTest + */ + +import com.alibaba.wisp.engine.WispEngine; + +public class ThreadJoinTest { + public static void main(String[] args) throws Exception { + Thread t = new Thread(() -> { + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + }); + t.start(); + t.join(); + + t = new Thread(() -> { + + }); + t.start(); + Thread.sleep(1000); + WispEngine.dispatch(() -> { + try { + Thread.sleep(200000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + }); + Thread.sleep(1000); + t.join(); + } +} diff --git a/test/com/alibaba/wisp2/TimedWaitTest.java b/test/com/alibaba/wisp2/TimedWaitTest.java new file mode 100644 index 0000000000000000000000000000000000000000..eed65ea0b9848021193070b5208ac61c3f520172 --- /dev/null +++ b/test/com/alibaba/wisp2/TimedWaitTest.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * @test + * @summary test timed Jvm park + * @requires os.family == "linux" + * @run main/othervm -XX:-UseBiasedLocking -XX:+EnableCoroutine -XX:+UseWispMonitor -Dcom.alibaba.wisp.transparentWispSwitch=true -Dcom.alibaba.wisp.version=2 TimedWaitTest + */ + +import com.alibaba.wisp.engine.WispEngine; + +import java.util.concurrent.CountDownLatch; + +public class TimedWaitTest { + public static void main(String[] args) throws InterruptedException { + CountDownLatch latch = new CountDownLatch(4); + for (int i = 0; i < 4; i++) { + int id = i; + WispEngine.dispatch(() -> { + final Object lock = new Object(); + synchronized (lock) { + try { + lock.wait(1); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + latch.countDown(); + }); + } + latch.await(); + } +} diff --git a/test/com/alibaba/wisp2/Wisp2GroupTest.java b/test/com/alibaba/wisp2/Wisp2GroupTest.java new file mode 100644 index 0000000000000000000000000000000000000000..472541aa19ef56b3931d61fba8e5e8e8c64ef1a8 --- /dev/null +++ b/test/com/alibaba/wisp2/Wisp2GroupTest.java @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * @test + * @summary Test WispCounter removing during the shutdown of Wisp2Group + * @requires os.family == "linux" + * @library /lib/testlibrary + * @run main/othervm -XX:+EnableCoroutine -Dcom.alibaba.wisp.transparentWispSwitch=true -Dcom.alibaba.wisp.version=2 -XX:+UseWispMonitor -Dcom.alibaba.wisp.enableHandOff=false Wisp2GroupTest + */ + +import com.alibaba.management.WispCounterMXBean; +import com.alibaba.wisp.engine.WispEngine; + +import javax.management.MBeanServer; +import java.io.IOException; +import java.lang.management.ManagementFactory; +import java.util.List; +import java.util.concurrent.AbstractExecutorService; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +import static jdk.testlibrary.Asserts.assertTrue; + +public class Wisp2GroupTest { + static WispMultiThreadExecutor executor; + static WispCounterMXBean mbean; + + public static void main(String[] args) throws Exception { + + MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); + try { + mbean = ManagementFactory.newPlatformMXBeanProxy(mbs, + "com.alibaba.management:type=WispCounter", WispCounterMXBean.class); + } catch (IOException e) { + e.printStackTrace(); + } + + testWisp2Group(); + } + + private static void testWisp2Group() throws Exception { + executor = new WispMultiThreadExecutor(4, new ThreadFactory() { + AtomicInteger seq = new AtomicInteger(); + + @Override + public Thread newThread(Runnable r) { + Thread t = new Thread(r, "Wisp2-Group-Test-Carrier-" + seq.getAndIncrement()); + t.setDaemon(true); + return t; + } + }); + Thread.sleep(500); + for (int j = 0; j < 10; ++j) { + executor.execute(RUN_COMPILED_BUSY_LOOP); + } + Thread.sleep(500); + List list = mbean.getRunningStates(); + System.out.println(list); + int size1 = list.size(); + executor.shutdown(); + executor.awaitTermination(5, TimeUnit.SECONDS); + list = mbean.getRunningStates(); + System.out.println(list); + int size2 = list.size(); + assertTrue((size1 - size2) == 4); + } + + // task running in compiled code + private static void decIt(long num) { + while (0 != num--) ; + } + + private static final Runnable RUN_COMPILED_BUSY_LOOP = () -> { + // warmup + for (int i = 0; i < 5000; ++i) { + decIt(i); + } + while (true) { + decIt(0xFFFFFFFFl); + } + }; + + static class WispMultiThreadExecutor extends AbstractExecutorService { + private final WispEngine delegated; + + public WispMultiThreadExecutor(int threadCount, ThreadFactory threadFactory) { + delegated = WispEngine.createEngine(threadCount, threadFactory); + } + + @Override + public void execute(Runnable command) { + delegated.execute(command); + } + + @Override + public void shutdown() { + delegated.shutdown(); + } + + @Override + public List shutdownNow() { + return null; + } + + @Override + public boolean isShutdown() { + return false; + } + + @Override + public boolean isTerminated() { + return false; + } + + @Override + public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException { + return delegated.awaitTermination(timeout, unit); + } + } +} diff --git a/test/com/alibaba/wisp2/Wisp2ShutdownTest.java b/test/com/alibaba/wisp2/Wisp2ShutdownTest.java new file mode 100644 index 0000000000000000000000000000000000000000..f711c3bd8d6994ac14de78f0f1d98d97b1b0169b --- /dev/null +++ b/test/com/alibaba/wisp2/Wisp2ShutdownTest.java @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * @test + * @summary Wisp2ShutdownTest + * @requires os.family == "linux" + * @library /lib/testlibrary + * @run main/othervm -XX:+EnableCoroutine -XX:+UseWispMonitor -Dcom.alibaba.wisp.transparentWispSwitch=true -Dcom.alibaba.wisp.version=2 Wisp2ShutdownTest + */ + +import com.alibaba.wisp.engine.WispEngine; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +import static jdk.testlibrary.Asserts.assertEQ; +import static jdk.testlibrary.Asserts.assertTrue; + +public class Wisp2ShutdownTest { + public static void main(String[] args) throws Exception { + basicShutDownTest(); + multiGroupsShutDownTest(); + } + + private static void sleep(long millis) { + try { + Thread.sleep(millis); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + private static void basicShutDownTest() throws Exception { + AtomicInteger n = new AtomicInteger(); + + WispEngine g = WispEngine.createEngine(4, Thread::new); + + for (int i = 0; i < 888; i++) { + g.execute(() -> { + n.incrementAndGet(); + try { + while (true) { + sleep(1000000); + } + } finally { + n.decrementAndGet(); + } + }); + } + + while (n.get() != 888) { + n.get(); + } + + long start = System.currentTimeMillis(); + + g.shutdown(); + + assertTrue(g.awaitTermination(3, TimeUnit.SECONDS)); + + System.out.println(System.currentTimeMillis() - start + "ms"); + + assertEQ(n.get(), 0); + } + + private static void multiGroupsShutDownTest() throws Exception { + CountDownLatch latch = new CountDownLatch(10); + for (int i = 0; i < 10; i++) { + WispEngine.dispatch(() -> { + try { + basicShutDownTest(); + latch.countDown(); + } catch (Exception e) { + e.printStackTrace(); + } + }); + } + assertTrue(latch.await(5, TimeUnit.SECONDS)); + } +} diff --git a/test/com/alibaba/wisp2/Wisp2TimerRemoveTest.java b/test/com/alibaba/wisp2/Wisp2TimerRemoveTest.java new file mode 100644 index 0000000000000000000000000000000000000000..fe18454dc0e32ee0915be3ab8f29b950a1d5c686 --- /dev/null +++ b/test/com/alibaba/wisp2/Wisp2TimerRemoveTest.java @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * @test + * @library /lib/testlibrary + * @summary verify canceled timers are removed ASAP + * @requires os.family == "linux" + * @run main/othervm -XX:+UseWisp2 Wisp2TimerRemoveTest + * @run main/othervm -XX:+UseWisp2 -Dcom.alibaba.wisp.highPrecisionTimer=true Wisp2TimerRemoveTest + */ + +import com.alibaba.wisp.engine.WispEngine; +import sun.misc.SharedSecrets; + +import java.lang.reflect.Field; +import java.lang.reflect.Member; +import java.lang.reflect.Method; +import java.util.concurrent.*; + +import static jdk.testlibrary.Asserts.*; + +public class Wisp2TimerRemoveTest { + + public static void main(String[] args) throws Exception { + BlockingQueue q1 = new ArrayBlockingQueue<>(1); + BlockingQueue q2 = new ArrayBlockingQueue<>(1); + ExecutorService es = Executors.newCachedThreadPool(); + CountDownLatch latch = new CountDownLatch(2); + + final int WORKERS = Runtime.getRuntime().availableProcessors(); + + for (int i = 0; i < WORKERS; i++) { + es.submit(() -> { + TimeUnit.MINUTES.sleep(10); + return null; + }); + } + + es.submit(() -> pingpong(q1, q2, latch)); + es.submit(() -> pingpong(q2, q1, latch)); + q1.offer(1); + latch.await(); + + for (int i = 0; i < WORKERS * 10; i++) { // iterate all carriers + Integer ql = es.submit(() -> { +// Object carrier = new FieldAccessor + Thread thread = Thread.currentThread(); + TimeUnit.MILLISECONDS.sleep(1); + assertEQ(Thread.currentThread(), thread); + return new FieldAccessor(SharedSecrets.getJavaLangAccess().getWispTask(thread)) + .access("carrier") + .access("worker") + .access("timerManager") + .access("queue") + .getInt("size"); + }).get(); + assertLessThanOrEqual(ql, WORKERS); + } + } + + private static Void pingpong(BlockingQueue pingQ, + BlockingQueue pongQ, + CountDownLatch latch) throws Exception { + for (int i = 0; i < 100000; i++) { + pingQ.poll(1, TimeUnit.HOURS); + pongQ.offer(1); + } + stealInfo(); + latch.countDown(); + return null; + } + + private static void stealInfo() throws ReflectiveOperationException { + int stealCount = new FieldAccessor(SharedSecrets.getWispEngineAccess().getCurrentTask()) + .getInt("stealCount"); + System.out.println("stealCount = " + stealCount); + } + + static class FieldAccessor { + final Object obj; + + FieldAccessor(Object o) { + this.obj = o; + } + + FieldAccessor access(String fieldName) throws ReflectiveOperationException { + Field field = obj.getClass().getDeclaredField(fieldName); + field.setAccessible(true); + return new FieldAccessor(field.get(obj)); + } + + int getInt(String fieldName) throws ReflectiveOperationException { + Field field = obj.getClass().getDeclaredField(fieldName); + field.setAccessible(true); + return field.getInt(obj); + } + } +} diff --git a/test/com/alibaba/wisp2/Wisp2WaitNotifyTest.java b/test/com/alibaba/wisp2/Wisp2WaitNotifyTest.java new file mode 100644 index 0000000000000000000000000000000000000000..c10608083df5ece8e84369077024818f61569639 --- /dev/null +++ b/test/com/alibaba/wisp2/Wisp2WaitNotifyTest.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * @test + * @library /lib/testlibrary + * @summary Test Object.wait/notify with coroutine in wisp2 + * @requires os.family == "linux" + * @run main/othervm -XX:-UseBiasedLocking -XX:+EnableCoroutine -XX:+UseWispMonitor -Dcom.alibaba.wisp.transparentWispSwitch=true -Dcom.alibaba.wisp.version=2 Wisp2WaitNotifyTest + */ + +import com.alibaba.wisp.engine.WispEngine; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.atomic.AtomicInteger; + +import static jdk.testlibrary.Asserts.assertEQ; + +public class Wisp2WaitNotifyTest { + + private AtomicInteger seq = new AtomicInteger(); + private int finishCnt = 0; + private CountDownLatch latch = new CountDownLatch(1); + private boolean fooCond = false; + + public static void main(String[] args) throws Exception { + Wisp2WaitNotifyTest s = new Wisp2WaitNotifyTest(); + synchronized (s) { + WispEngine.dispatch(s::foo); + assertEQ(s.seq.getAndIncrement(), 0); + } + + s.latch.await(); + + assertEQ(s.seq.getAndIncrement(), 5); + synchronized (s) { + while (s.finishCnt < 2) { + s.wait(); + } + } + assertEQ(s.seq.getAndIncrement(), 6); + } + + private synchronized void foo() { + assertEQ(seq.getAndIncrement(), 1); + + WispEngine.dispatch(this::bar); + assertEQ(seq.getAndIncrement(), 2); + try { + while (!fooCond) { + wait(); + } + } catch (InterruptedException e) { + e.printStackTrace(); + } + assertEQ(seq.getAndIncrement(), 4); + + latch.countDown(); + finishCnt++; + notifyAll(); + } + + private void bar() { + synchronized (this) { + assertEQ(seq.getAndIncrement(), 3); + fooCond = true; + notifyAll(); + } + synchronized (this) { + finishCnt++; + notifyAll(); + } + } +} diff --git a/test/com/alibaba/wisp2/Wisp2WithGlobalCacheTest.java b/test/com/alibaba/wisp2/Wisp2WithGlobalCacheTest.java new file mode 100644 index 0000000000000000000000000000000000000000..8d0f99ecfefe933657489c0081809bc5df14961c --- /dev/null +++ b/test/com/alibaba/wisp2/Wisp2WithGlobalCacheTest.java @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * @test + * @library /lib/testlibrary + * @summary test the task exit flow for ThreadAsWisp task + * @requires os.family == "linux" + * @run main/othervm -XX:ActiveProcessorCount=2 -XX:+EnableCoroutine -Dcom.alibaba.wisp.transparentWispSwitch=true -Dcom.alibaba.wisp.version=2 -XX:+UseWispMonitor -Dcom.alibaba.wisp.enableThreadAsWisp=true -Dcom.alibaba.wisp.allThreadAsWisp=true -Dcom.alibaba.wisp.engineTaskCache=2 Wisp2WithGlobalCacheTest + */ + +import sun.misc.SharedSecrets; + +import java.util.Timer; +import java.util.TimerTask; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +import static jdk.testlibrary.Asserts.assertTrue; + +public class Wisp2WithGlobalCacheTest { + public static void main(String[] args) throws Exception { + CountDownLatch done = new CountDownLatch(80); + + Runnable r = () -> { + try { + Thread.sleep(100); + } catch(Throwable t) { + } + if (SharedSecrets.getJavaLangAccess().currentThread0() != Thread.currentThread()) { + done.countDown(); + } + }; + + for (int i = 0; i < 10; i++) { + new Thread(r).start(); + new Timer().schedule(new TimerTask() { + @Override + public void run() { + r.run(); + } + }, 0); + new Thread() { + @Override + public void run() { + r.run(); + } + }.start(); + new Thread() { + @Override + public void run() { + r.run(); + } + }.start(); + new Thread() { + @Override + public void run() { + r.run(); + } + }.start(); + new Thread() { + @Override + public void run() { + r.run(); + } + }.start(); + new Thread() { + @Override + public void run() { + r.run(); + } + }.start(); + new Thread() { + @Override + public void run() { + r.run(); + } + }.start(); + } + + assertTrue(done.await(10, TimeUnit.SECONDS)); + } +} diff --git a/test/com/alibaba/wisp2/Wisp2WorkStealTest.java b/test/com/alibaba/wisp2/Wisp2WorkStealTest.java new file mode 100644 index 0000000000000000000000000000000000000000..c68019e4eafa7c1c81f32c729d029d4de3ce6e5b --- /dev/null +++ b/test/com/alibaba/wisp2/Wisp2WorkStealTest.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * @test + * @library /lib/testlibrary + * @summary verification of work stealing really happened + * @requires os.family == "linux" + * @run main/othervm -XX:+UseWisp2 -Dcom.alibaba.wisp.schedule.stealRetry=100 -Dcom.alibaba.wisp.schedule.helpStealRetry=100 Wisp2WorkStealTest + */ + +import com.alibaba.wisp.engine.WispEngine; +import sun.misc.SharedSecrets; + +import java.util.concurrent.atomic.AtomicReference; + +import static jdk.testlibrary.Asserts.assertNE; + +public class Wisp2WorkStealTest { + public static void main(String[] args) { + Object lock = new Object(); + AtomicReference t = new AtomicReference<>(); + AtomicReference t2 = new AtomicReference<>(); + WispEngine.dispatch(() -> { + t.set(SharedSecrets.getJavaLangAccess().currentThread0()); + synchronized (lock) { + try { + lock.wait(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + t2.set(SharedSecrets.getJavaLangAccess().currentThread0()); + }); + + // letting a coroutine occupy the carrier + while (true) { + AtomicReference found = new AtomicReference<>(null); + WispEngine.dispatch(() -> { + if (SharedSecrets.getJavaLangAccess().currentThread0() == t.get()) { + found.set(true); + long start = System.currentTimeMillis(); + while (System.currentTimeMillis() - start < 1000) { + // occupy the carrier + } + } else { + found.set(false); + } + }); + while (found.get() == null) { + } + if (found.get()) { + break; + } + } + + synchronized (lock) { + lock.notify(); + } + + while (t2.get() == null) { + } + + assertNE(t.get(), t2.get()); + } +} diff --git a/test/com/alibaba/wisp2/WispEngineCurrentTest.java b/test/com/alibaba/wisp2/WispEngineCurrentTest.java new file mode 100644 index 0000000000000000000000000000000000000000..ec512c8f3a5343ae56c652ae3eba9757b13bb4f5 --- /dev/null +++ b/test/com/alibaba/wisp2/WispEngineCurrentTest.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * @test + * @summary Test WispEngine semantic change after refactor + * @requires os.family == "linux" + * @library /lib/testlibrary + * @run main/othervm -XX:+UseWisp2 WispEngineCurrentTest + */ + +import com.alibaba.wisp.engine.WispEngine; + +import java.util.*; +import java.util.concurrent.*; + +import static jdk.testlibrary.Asserts.assertTrue; + +public class WispEngineCurrentTest { + public static void main(String[] args) throws Exception { + WispEngine engine = WispEngine.createEngine(4, r -> { + Thread t = new Thread(r); + t.setDaemon(true); + return t; + }); + Set wispEngineSet = Collections.newSetFromMap(new ConcurrentHashMap<>()); + CountDownLatch latch0 = new CountDownLatch(10); + for (int i = 0; i < 10; i++) { + engine.execute(() -> { + wispEngineSet.add(WispEngine.current()); + latch0.countDown(); + }); + } + + assertTrue(latch0.await(1, TimeUnit.SECONDS)); + assertTrue(wispEngineSet.size() == 1); + CountDownLatch latch1 = new CountDownLatch(1); + WispEngine.dispatch(() -> { + wispEngineSet.add(WispEngine.current()); + latch1.countDown(); + }); + assertTrue(latch1.await(1, TimeUnit.SECONDS)); + assertTrue(wispEngineSet.size() == 2); + } +} diff --git a/test/com/alibaba/wisp2/WispInitShutdownTest.java b/test/com/alibaba/wisp2/WispInitShutdownTest.java new file mode 100644 index 0000000000000000000000000000000000000000..f37c2798ec4305ede4db724c31c8f45b56930fc0 --- /dev/null +++ b/test/com/alibaba/wisp2/WispInitShutdownTest.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * @test + * @summary WispInitShutdownTest + * @requires os.family == "linux" + * @library /lib/testlibrary + * @run main/othervm -XX:+EnableCoroutine -XX:+UseWispMonitor -Dcom.alibaba.wisp.transparentWispSwitch=true -Dcom.alibaba.wisp.version=2 WispInitShutdownTest + */ + +import com.alibaba.wisp.engine.WispEngine; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; + +import static jdk.testlibrary.Asserts.assertEQ; +import static jdk.testlibrary.Asserts.assertTrue; + +public class WispInitShutdownTest { + public static CountDownLatch shutddownLatch = new CountDownLatch(1); + public static AtomicBoolean suc = new AtomicBoolean(false); + + public static void main(String[] args) throws Exception { + CountDownLatch finish = new CountDownLatch(1); + CountDownLatch cinit = new CountDownLatch(1); + WispEngine engine = WispEngine.createEngine(1, new ThreadFactory() { + @Override + public Thread newThread(Runnable r) { + Thread t = new Thread(r); + t.setName("test-thread"); + return t; + } + }); + + engine.execute(new Runnable() { + @Override + public void run() { + try { + cinit.countDown(); + Class.forName("SomeC"); + suc.set(true); + } catch (Exception e) { + e.printStackTrace(); + assertTrue(false); + } finally { + finish.countDown(); + } + } + }); + + shutddownLatch.countDown(); + cinit.await(); + engine.shutdown(); + finish.await(); + assertTrue(suc.get()); + } +} + +class SomeC { + static { + try { + WispInitShutdownTest.shutddownLatch.await(); + for (int i = 0; i < 10; i++) { + Thread.sleep(40); + } + } catch (Exception e) { + e.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/test/com/alibaba/wisp2/bug/ConcurrentThreadJoinTest.java b/test/com/alibaba/wisp2/bug/ConcurrentThreadJoinTest.java new file mode 100644 index 0000000000000000000000000000000000000000..e9b4cbd26e05e7b3755293f489d598d0fc4bb852 --- /dev/null +++ b/test/com/alibaba/wisp2/bug/ConcurrentThreadJoinTest.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * @test + * @library /lib/testlibrary + * @summary ensure thread.isAlive() is false after thread.join() + * @requires os.family == "linux" + * @run main/othervm -XX:+UseWisp2 ConcurrentThreadJoinTest + */ + +import static jdk.testlibrary.Asserts.assertFalse; + +public class ConcurrentThreadJoinTest { + public static void main(String[] args) throws Exception { + long start = System.currentTimeMillis(); + + while (System.currentTimeMillis() - start < 3000) { + Thread thread = new Thread(() -> { + }); + thread.start(); + thread.join(1000); + assertFalse(thread.isAlive()); + } + } +} diff --git a/test/com/alibaba/wisp2/bug/DisableStealBugTest.java b/test/com/alibaba/wisp2/bug/DisableStealBugTest.java new file mode 100644 index 0000000000000000000000000000000000000000..8e9fc638313ddb84bb125b04e78b3c8fdf768b3f --- /dev/null +++ b/test/com/alibaba/wisp2/bug/DisableStealBugTest.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * @test + * @library /lib/testlibrary + * @summary test bug of update stealEnable fail + * @requires os.family == "linux" + * @run main/othervm -XX:+UseWisp2 DisableStealBugTest + */ + +import com.alibaba.wisp.engine.WispEngine; +import com.alibaba.wisp.engine.WispTask; +import sun.misc.SharedSecrets; + +import java.lang.reflect.Field; +import java.util.concurrent.atomic.AtomicReference; + +import static jdk.testlibrary.Asserts.assertTrue; + +public class DisableStealBugTest { + public static void main(String[] args) throws Exception { + AtomicReference task = new AtomicReference<>(); + + WispEngine.dispatch(() -> { + task.set(SharedSecrets.getWispEngineAccess().getCurrentTask()); + setOrGetStealEnable(task.get(), true, false); + try { + Thread.sleep(10); + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + }); + + Thread.sleep(2000); + boolean stealEnable = setOrGetStealEnable(task.get(), false, false); + assertTrue(stealEnable); + } + + static boolean setOrGetStealEnable(WispTask task, boolean isSet, boolean b) { + try { + Field resumeEntryField = task.getClass().getDeclaredField("resumeEntry"); + resumeEntryField.setAccessible(true); + final Object resumeEntry = resumeEntryField.get(task); + + Field stealEnableField = resumeEntry.getClass().getDeclaredField("stealEnable"); + stealEnableField.setAccessible(true); + if (isSet) { + stealEnableField.setBoolean(resumeEntry, b); + return b; + } else { + return stealEnableField.getBoolean(resumeEntry); + } + } catch (ReflectiveOperationException e) { + throw new Error(e); + } + } +} diff --git a/test/com/alibaba/wisp2/bug/EpollNPETest.java b/test/com/alibaba/wisp2/bug/EpollNPETest.java new file mode 100644 index 0000000000000000000000000000000000000000..2d1e9e5a032c9636b2fc5bd200f4f9ac297e1b2e --- /dev/null +++ b/test/com/alibaba/wisp2/bug/EpollNPETest.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * @test + * @library /lib/testlibrary + * @summary verify epoll without wisp will not produce NPE + * @requires os.family == "linux" + * @run main EpollNPETest + */ + +import java.nio.channels.Selector; + +public class EpollNPETest { + + public static void main(String[] args) throws Exception { + Selector sel = Selector.open(); + sel.wakeup(); + sel.select(100); + } +} diff --git a/test/com/alibaba/wisp2/bug/EpollWakeupTest.java b/test/com/alibaba/wisp2/bug/EpollWakeupTest.java new file mode 100644 index 0000000000000000000000000000000000000000..353b25011da79bfe0c790aaed5412afee73b7f74 --- /dev/null +++ b/test/com/alibaba/wisp2/bug/EpollWakeupTest.java @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * @test + * @library /lib/testlibrary + * @summary test selector.wakeup() dispatched + * @requires os.family == "linux" + * @run main/othervm -XX:+UseWisp2 -verbose:class EpollWakeupTest 3000 + */ + +import java.nio.channels.Selector; +import java.util.Random; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.CyclicBarrier; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +import static jdk.testlibrary.Asserts.assertTrue; + +/** + * Sleep random us before wakeup/select, + * To help us coverage all branches, including pre-wakeup, normal-wakeup, concurrent-wakeup + */ +public class EpollWakeupTest { + public static void main(String[] args) throws Exception { + Selector selector = Selector.open(); + CyclicBarrier barrier = new CyclicBarrier(2); + Random random = new Random(); + final Object lock = new Object(); + boolean[] wakened = new boolean[1]; + int[] cnt = new int[1]; + + Thread io = new Thread(() -> { + try { + while (true) { + barrier.await(); + new CountDownLatch(1).await(random.nextInt(10), TimeUnit.MICROSECONDS); + selector.select(); + synchronized (lock) { + cnt[0]++; + wakened[0] = true; + lock.notifyAll(); + } + } + } catch (Throwable e) { + e.printStackTrace(); + } + }, "IO thread"); + + Thread biz = new Thread(() -> { + try { + while (true) { + selector.selectNow(); // clean up + wakened[0] = false; + barrier.await(); + new CountDownLatch(1).await(random.nextInt(10), TimeUnit.MICROSECONDS); + selector.wakeup(); + synchronized (lock) { + while (!wakened[0]) { + lock.wait(); + } + } + } + } catch (Throwable e) { + e.printStackTrace(); + } + }, "biz thread"); + io.setDaemon(true); + io.start(); + biz.setDaemon(true); + biz.start(); + + Executors.newScheduledThreadPool(1).scheduleAtFixedRate(() -> { + System.out.println(cnt[0]); + synchronized (lock) { + cnt[0] = 0; + } + }, 1, 1, TimeUnit.SECONDS); + + Thread.sleep(Integer.valueOf(args[0])); + long deadline = System.currentTimeMillis() + 1000; + synchronized (lock) { + while (!wakened[0]) { + long timeout = deadline - System.currentTimeMillis(); + assertTrue(timeout > 0, "io thread hang..."); + lock.wait(timeout); + } + } + } +} diff --git a/test/com/alibaba/wisp2/bug/IsInNativeTest.java b/test/com/alibaba/wisp2/bug/IsInNativeTest.java new file mode 100644 index 0000000000000000000000000000000000000000..c17b97db2806a5a5d098e762a51271565e74ff0c --- /dev/null +++ b/test/com/alibaba/wisp2/bug/IsInNativeTest.java @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * @test + * @library /lib/testlibrary + * @summary test Thread.isInNative() is correct + * @requires os.family == "linux" + * @run main/othervm -XX:+EnableCoroutine IsInNativeTest + */ + +import sun.misc.SharedSecrets; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.nio.ByteBuffer; +import java.nio.channels.SocketChannel; +import java.util.Properties; + +import static jdk.testlibrary.Asserts.assertFalse; +import static jdk.testlibrary.Asserts.assertTrue; + +public class IsInNativeTest { + + static Properties p; + static String socketAddr; + static { + p = java.security.AccessController.doPrivileged( + new java.security.PrivilegedAction() { + public Properties run() { + return System.getProperties(); + } + } + ); + socketAddr = (String)p.get("test.wisp.socketAddress"); + if (socketAddr == null) { + socketAddr = "www.example.com"; + } + } + + public static void main(String[] args) throws Exception { + Thread nthread = new Thread(() -> { + try { + SocketChannel ch = SocketChannel.open(new InetSocketAddress(socketAddr, 80)); + ch.read(ByteBuffer.allocate(4096)); + } catch (IOException e) { + e.printStackTrace(); + } + }); + nthread.start(); + Thread thread = new Thread(() -> { + while (true) { + + } + }); + thread.start(); + Thread thread2 = new Thread(() -> { + }); + thread2.start(); + Thread.sleep(500); + assertFalse(SharedSecrets.getJavaLangAccess().isInSameNative(nthread)); + assertFalse(SharedSecrets.getJavaLangAccess().isInSameNative(thread)); + assertFalse(SharedSecrets.getJavaLangAccess().isInSameNative(thread2)); + Thread.sleep(1000); + assertFalse(SharedSecrets.getJavaLangAccess().isInSameNative(thread)); + } +} diff --git a/test/com/alibaba/wisp2/bug/PreemptWispInternalBugTest.java b/test/com/alibaba/wisp2/bug/PreemptWispInternalBugTest.java new file mode 100644 index 0000000000000000000000000000000000000000..d64015d348fa833423fb590338e2f56932bcfbdd --- /dev/null +++ b/test/com/alibaba/wisp2/bug/PreemptWispInternalBugTest.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * @test + * @summary Verify wisp internal logic can not be preempted + * @requires os.family == "linux" + * @library /lib/testlibrary + * @run main PreemptWispInternalBugTest + */ + +import com.alibaba.wisp.engine.WispEngine; +import sun.misc.SharedSecrets; + +import java.util.concurrent.FutureTask; + + +public class PreemptWispInternalBugTest { + private static final int timeOut = 1000; + private static String[] tasks = new String[] {"toString", "addTimer"}; + + public static void main(String[] args) throws Exception { + if (args.length == 0) { + for (int i = 0; i < tasks.length; i++) { + ProcessBuilder pb = jdk.testlibrary.ProcessTools.createJavaProcessBuilder( + "-XX:+UseWisp2", "-XX:+UnlockDiagnosticVMOptions", "-XX:+VerboseWisp", "-XX:-Inline", + "-Xcomp", "-Dcom.alibaba.wisp.sysmonTickUs=100000", + PreemptWispInternalBugTest.class.getName(), tasks[i]); + jdk.testlibrary.OutputAnalyzer output = new jdk.testlibrary.OutputAnalyzer(pb.start()); + output.shouldContain("[WISP] preempt was blocked, because wisp internal method on the stack"); + } + return; + } + FutureTask future = getTask(args[0]); + WispEngine.dispatch(future); + future.get(); + } + + private static FutureTask getFutureTask(Runnable runnable) { + return new FutureTask<>(() -> { + long start = System.currentTimeMillis(); + while (System.currentTimeMillis() - start < timeOut) { + runnable.run(); + } + return null; + }); + } + + private static FutureTask getTask(String taskName) { + switch (taskName) { + case "toString": + return getFutureTask(() -> SharedSecrets.getWispEngineAccess().getCurrentTask().toString()); + case "addTimer": + return getFutureTask(() -> { + SharedSecrets.getWispEngineAccess().addTimer(System.nanoTime() + 1008611); + SharedSecrets.getWispEngineAccess().cancelTimer(); + }); + default: + throw new IllegalArgumentException(); + } + } +} \ No newline at end of file diff --git a/test/com/alibaba/wisp2/bug/SchedulerQLBugTest.java b/test/com/alibaba/wisp2/bug/SchedulerQLBugTest.java new file mode 100644 index 0000000000000000000000000000000000000000..e6519aedee0baa53cadbd33e6853051cbfae08d7 --- /dev/null +++ b/test/com/alibaba/wisp2/bug/SchedulerQLBugTest.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * @test + * @library /lib/testlibrary + * @summary verify queue length not growth infinity + * @requires os.family == "linux" + * @run main/othervm -XX:+UseWisp2 DisableStealBugTest + */ + +import com.alibaba.wisp.engine.WispEngine; +import sun.misc.SharedSecrets; + +import java.lang.reflect.Method; +import java.util.concurrent.CountDownLatch; + +import static jdk.testlibrary.Asserts.assertLT; + +public class SchedulerQLBugTest { + public static void main(String[] args) throws Exception { + WispEngine g = WispEngine.createEngine(2, Thread::new); + CountDownLatch latch = new CountDownLatch(1); + g.execute(() -> { + DisableStealBugTest.setOrGetStealEnable(SharedSecrets.getWispEngineAccess().getCurrentTask(), true, false); + latch.countDown(); + while (true) { + try { + Thread.sleep(1); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + }); + latch.await(); + for (int i = 0; i < 10; i++) { // trigger steal + g.execute(() -> { /**/}); + } + + Thread.sleep(100); + for (int i = 0; i < 10; i++) { + int ql = g.submit(() -> { + try { + Method getCurrent = WispCarrier.class.getDeclaredMethod("current"); + getCurrent.setAccessible(true); + + Method m = WispCarrier.class.getDeclaredMethod("getTaskQueueLength"); + m.setAccessible(true); + return (int) m.invoke(getCurrent.invoke(null)); + } catch (ReflectiveOperationException e) { + throw new Error(e); + } + }).get(); + assertLT(ql, 100); + } + } +} diff --git a/test/com/alibaba/wisp2/bug/Wisp2ThreadObjLeakInThreadGroupTest.java b/test/com/alibaba/wisp2/bug/Wisp2ThreadObjLeakInThreadGroupTest.java new file mode 100644 index 0000000000000000000000000000000000000000..f4bd0c05161f6c6326fb0ac1c03bf1859d7d5ee2 --- /dev/null +++ b/test/com/alibaba/wisp2/bug/Wisp2ThreadObjLeakInThreadGroupTest.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * @test + * @library /lib/testlibrary + * @summary test bug fix of thread object leak in thread group + * @requires os.family == "linux" + * @run main/othervm -XX:+UseWisp2 Wisp2ThreadObjLeakInThreadGroupTest + */ + +import java.util.concurrent.CountDownLatch; + +import static jdk.testlibrary.Asserts.assertEQ; + +public class Wisp2ThreadObjLeakInThreadGroupTest { + public static void main(String[] args) throws Exception { + ThreadGroup tg = Thread.currentThread().getThreadGroup(); + int count = tg.activeCount(); + CountDownLatch c1 = new CountDownLatch(1), c2 = new CountDownLatch(1); + Thread thread = new Thread(tg, () -> { + c1.countDown(); + try { + c2.await(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + }, Wisp2ThreadObjLeakInThreadGroupTest.class.getSimpleName()); + thread.start(); + c1.await(); // wait start + assertEQ(tg.activeCount(), count + 1); + c2.countDown(); // notify finish + thread.join(); + assertEQ(tg.activeCount(), count); + } +} diff --git a/test/com/alibaba/wisp2/yield/Wisp2YieldTest.java b/test/com/alibaba/wisp2/yield/Wisp2YieldTest.java new file mode 100644 index 0000000000000000000000000000000000000000..a314cf2d0ab00f749c0347b58c1a39efd77ae969 --- /dev/null +++ b/test/com/alibaba/wisp2/yield/Wisp2YieldTest.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * @test + * @library /lib/testlibrary + * @summary Test yield in wisp2 + * @requires os.family == "linux" + * @run main/othervm -XX:-UseBiasedLocking -XX:+EnableCoroutine -XX:+UseWispMonitor -Dcom.alibaba.wisp.transparentWispSwitch=true -Dcom.alibaba.wisp.version=2 -Dcom.alibaba.wisp.workerEngines=1 Wisp2YieldTest + */ + +import com.alibaba.wisp.engine.WispEngine; + +import static jdk.testlibrary.Asserts.assertTrue; + + +public class Wisp2YieldTest { + public static void main(String[] args) { + boolean success[] = new boolean[1]; + + WispEngine.dispatch(() -> { + WispEngine.dispatch(() -> { + success[0] = true; + }); + long start = System.currentTimeMillis(); + while (System.currentTimeMillis() - start < 100) { + } + Thread.yield(); + while (true) { + + } + }); + + sleep(1000); + + assertTrue(success[0]); + } + + private static void sleep(long ms) { + try { + Thread.sleep(ms); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } +} diff --git a/test/com/alibaba/wisp2/yield/YieldEmptyQueueTest.java b/test/com/alibaba/wisp2/yield/YieldEmptyQueueTest.java new file mode 100644 index 0000000000000000000000000000000000000000..6d1b0b2d11c0ef5c6a017d6895074c3e1ac1fbe3 --- /dev/null +++ b/test/com/alibaba/wisp2/yield/YieldEmptyQueueTest.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * @test + * @library /lib/testlibrary + * @summary Verify yield not really happened when queue is empty + * @requires os.family == "linux" + * @run main/othervm -XX:+UseWisp2 YieldEmptyQueueTest + */ + +import com.alibaba.wisp.engine.WispEngine; +import sun.misc.JavaLangAccess; +import sun.misc.SharedSecrets; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.concurrent.Executors; + +import static jdk.testlibrary.Asserts.assertTrue; + +public class YieldEmptyQueueTest { + public static void main(String[] args) throws Exception { + + assertTrue(Executors.newSingleThreadExecutor().submit(() -> { + long sc = (long) new ObjAccess(SharedSecrets.getJavaLangAccess().getWispTask(Thread.currentThread())) + .ref("carrier").ref("counter").ref("switchCount").obj; + Thread.yield(); + return (long) new ObjAccess(SharedSecrets.getJavaLangAccess().getWispTask(Thread.currentThread())) + .ref("carrier").ref("counter").ref("switchCount").obj == sc; + }).get()); + } + + static class ObjAccess { + Object obj; + + ObjAccess(Object obj) { + this.obj = obj; + } + + ObjAccess ref(String field) { + try { + Field f; + try { + f = obj.getClass().getDeclaredField(field); + } catch (NoSuchFieldException e) { + f = obj.getClass().getSuperclass().getDeclaredField(field); + } + f.setAccessible(true); + return new ObjAccess(f.get(obj)); + } catch (ReflectiveOperationException e) { + throw new Error(e); + } + } + } +} diff --git a/test/com/alibaba/wisp2/yield/YieldFewNanosTest.java b/test/com/alibaba/wisp2/yield/YieldFewNanosTest.java new file mode 100644 index 0000000000000000000000000000000000000000..ca5ef3384d0900068571bc23cc998cdb842069e1 --- /dev/null +++ b/test/com/alibaba/wisp2/yield/YieldFewNanosTest.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * @test + * @library /lib/testlibrary + * @summary Verify park not happened for a very small interval + * @requires os.family == "linux" + * @run main/othervm -XX:+UseWisp2 YieldFewNanosTest + */ + +import com.alibaba.wisp.engine.WispEngine; + +import sun.misc.SharedSecrets; + +import java.lang.reflect.Method; +import java.util.concurrent.Executors; +import java.util.concurrent.locks.LockSupport; + +import static jdk.testlibrary.Asserts.assertTrue; + +public class YieldFewNanosTest { + public static void main(String[] args) throws Exception { + assertTrue(Executors.newSingleThreadExecutor().submit(() -> { + long pc = (long) new YieldEmptyQueueTest.ObjAccess(SharedSecrets.getJavaLangAccess().getWispTask(Thread.currentThread())) + .ref("carrier").ref("counter").ref("parkCount").obj; + LockSupport.parkNanos(1); + return (long) new YieldEmptyQueueTest.ObjAccess(SharedSecrets.getJavaLangAccess().getWispTask(Thread.currentThread())) + .ref("carrier").ref("counter").ref("parkCount").obj == pc; + }).get()); + } +} diff --git a/test/com/alibaba/wisp2/yield/YieldTimerTest.java b/test/com/alibaba/wisp2/yield/YieldTimerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..94daac662debd2bb615aa390747d1b1728e4faaa --- /dev/null +++ b/test/com/alibaba/wisp2/yield/YieldTimerTest.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * @test + * @library /lib/testlibrary + * @summary Verify timer could be processed when we're yielding + * @requires os.family == "linux" + * @run main/othervm -XX:+UseWisp2 -Dcom.alibaba.wisp.carrierEngines=1 YieldTimerTest + * @run main/othervm -XX:+UseWisp2 -Dcom.alibaba.wisp.carrierEngines=1 -Dcom.alibaba.wisp.highPrecisionTimer=true YieldTimerTest + */ + + +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; + +public class YieldTimerTest { + public static void main(String[] args) throws Exception { + Future future = Executors.newSingleThreadExecutor().submit(() -> { + try { + Thread.sleep(200); + } catch (InterruptedException e) { + e.printStackTrace(); + } + }); + + new Thread(() -> { + while (true) { + Thread.yield(); + } + }).start(); + + future.get(1, TimeUnit.SECONDS); + } +} diff --git a/test/java/beans/XMLDecoder/8028054/Task.java b/test/java/beans/XMLDecoder/8028054/Task.java index 9aa477c0310fcc980dc7f42f46f7182e4b822f32..b4226998a0d21daa2f17b68daa72bca73b27d396 100644 --- a/test/java/beans/XMLDecoder/8028054/Task.java +++ b/test/java/beans/XMLDecoder/8028054/Task.java @@ -87,6 +87,10 @@ abstract class Task implements Runnable { while (entries.hasMoreElements()) { String name = entries.nextElement().getName(); if (name.startsWith("java") && name.endsWith(".class")) { + if (name.startsWith("java/dyn")) { + // skip coroutine classes + continue; + } classes.add(Class.forName(name.substring(0, name.indexOf(".")).replace('/', '.'))); if (count == classes.size()) { break; diff --git a/test/java/dyn/BasicStealTest.java b/test/java/dyn/BasicStealTest.java new file mode 100644 index 0000000000000000000000000000000000000000..a3a76ea566ba9385b303cbb3d886c200c985fcc0 --- /dev/null +++ b/test/java/dyn/BasicStealTest.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * @test + * @summary test basic coroutine steal mechanism + * @library /lib/testlibrary + * @run main/othervm -XX:+EnableCoroutine BasicStealTest + */ + +import java.dyn.Coroutine; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Consumer; + +import static jdk.testlibrary.Asserts.assertEQ; +import static jdk.testlibrary.Asserts.assertTrue; + +public class BasicStealTest { + public static void main(String[] args) { + AtomicReference toBeStolen = new AtomicReference<>(); + Thread main = Thread.currentThread(); + + Thread t = new Thread(() -> { + Coroutine threadCoro = Thread.currentThread().getCoroutineSupport().threadCoroutine(); + + Coroutine coro = new Coroutine(() -> { + AtomicReference> foo = new AtomicReference<>(); + foo.set((x) -> { + if (x == 10) { + Coroutine.yieldTo(threadCoro); + } else { + System.out.println(x + " enter " + Thread.currentThread()); + foo.get().accept(x + 1); + System.out.println(x + " exit" + Thread.currentThread()); + assertEQ(Thread.currentThread(), main); + } + }); + foo.get().accept(0); + }); + + Coroutine.yieldTo(coro); + // switch from foo()... + toBeStolen.set(coro); + try { + Thread.sleep(100000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + }, "new_thread"); + t.setDaemon(true); + t.start(); + + while (toBeStolen.get() == null) { + } + + assertEQ(toBeStolen.get().steal(false), Coroutine.StealResult.SUCCESS); + Coroutine.yieldTo(toBeStolen.get()); + + } +} diff --git a/test/java/dyn/ConcurrentStealTest.java b/test/java/dyn/ConcurrentStealTest.java new file mode 100644 index 0000000000000000000000000000000000000000..65e9d035f15db6cdd7c7afed7449a5ae629da5f9 --- /dev/null +++ b/test/java/dyn/ConcurrentStealTest.java @@ -0,0 +1,202 @@ +/* + * Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* @test + * @summary unit tests for steal in concurrent situation + * @run junit/othervm/timeout=300 -XX:+EnableCoroutine ConcurrentStealTest + */ + +import org.junit.Test; + +import java.dyn.Coroutine; +import java.dyn.CoroutineSupport; +import java.util.Arrays; +import java.util.Comparator; +import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; + +import static org.junit.Assert.*; + +public class ConcurrentStealTest { + + @Test + public void stealRunning() throws Exception { + Coroutine[] coro = new Coroutine[1]; + + CountDownLatch cdl = new CountDownLatch(1); + + Thread t = new Thread(() -> { + coro[0] = new Coroutine(() -> { + try { + cdl.countDown(); + Thread.sleep(10000000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + }); + Coroutine.yieldTo(coro[0]); + }, "stealRunning"); + t.setDaemon(true); + t.start(); + + cdl.await(); + + Thread.sleep(500); + + assertEquals(coro[0].steal(false), Coroutine.StealResult.FAIL_BY_STATUS); + } + + final static int THREADS = Math.min(Runtime.getRuntime().availableProcessors(), 8); + final static int CORO_PER_TH = 20; + final static int TIMEOUT = 10; + + @Test + public void randomSteal() throws Exception { + CoroutineAdaptor[] coro = new CoroutineAdaptor[THREADS * CORO_PER_TH]; + Stat[] cnt = new Stat[THREADS]; + StealWorker[] stealWorkers = new StealWorker[THREADS]; + + AtomicInteger sync = new AtomicInteger(); + + for (int th = 0; th < THREADS; th++) { + cnt[th] = new Stat(); + stealWorkers[th] = new StealWorker(coro, cnt , sync, th, stealWorkers); + Thread t = new Thread(stealWorkers[th], "randomSteal-" + th); + t.setDaemon(true); + t.start(); + } + + while (sync.get() != THREADS) { + } + + long start = System.nanoTime(); + while (System.nanoTime() - start < TimeUnit.SECONDS.toNanos(TIMEOUT)) { + } + + for (int i = 0; i < cnt.length; i++) { + cnt[i].stealCnt /= TIMEOUT; + cnt[i].yieldCnt /= TIMEOUT; + cnt[i].stealFailedCnt /= TIMEOUT; + } + System.out.println(Arrays.toString(cnt)); + } + + class StealWorker implements Runnable { + CoroutineAdaptor[] coro; + Coroutine threadCoro; + public ConcurrentSkipListSet stealables = new ConcurrentSkipListSet<>(new Comparator() { + @Override + public int compare(CoroutineAdaptor o1, CoroutineAdaptor o2) { + return Long.compare(o1.id, o2.id); + } + }); + Stat[] cnt; + StealWorker[] workers; + AtomicInteger sync; + int cth; + + StealWorker(CoroutineAdaptor[] coro, Stat[] cnt, AtomicInteger sync, int cth, StealWorker[] workers) { + this.coro = coro; + this.cnt = cnt; + this.sync = sync; + this.cth = cth; + this.workers = workers; + } + + @Override + public void run() { + threadCoro = Thread.currentThread().getCoroutineSupport().threadCoroutine(); + + for (int i = 0; i < CORO_PER_TH; i++) { + coro[CORO_PER_TH * cth + i] = new CoroutineAdaptor(() -> { + while (true) { + Coroutine.yieldTo(threadCoro); + cnt[cth].yieldCnt++; + } + }); + } + for (int i = 0; i < CORO_PER_TH; i++) { + CoroutineAdaptor coroutineAdaptor = new CoroutineAdaptor(() ->{ + while (true) { + yield(); + } + }); + Coroutine.yieldTo(coroutineAdaptor); + stealables.add(coroutineAdaptor); + } + + + sync.incrementAndGet(); + while (sync.get() != THREADS) { + } + + runRandom(System.nanoTime(), (cth + 1) % THREADS); + } + + + void yield() { + Coroutine.yieldTo(coro[CORO_PER_TH * cth + ThreadLocalRandom.current().nextInt(CORO_PER_TH)]); + } + + void runRandom(long start, int nxt) { + while (System.nanoTime() - start < TimeUnit.SECONDS.toNanos(TIMEOUT)) { + for (int i = 0; i < 2; i ++) { + if(i != 1) { + yield(); + cnt[cth].yieldCnt++; + } else { + CoroutineAdaptor target = workers[nxt].stealables.pollFirst(); + if (target != null && target.steal(true) == Coroutine.StealResult.SUCCESS) { + stealables.add(target); + cnt[cth].stealCnt++; + } else { + if (target != null) + workers[nxt].stealables.add(target); + cnt[cth].stealFailedCnt++; + } + } + } + } + } + } + + static AtomicInteger seq = new AtomicInteger(0); + + class CoroutineAdaptor extends Coroutine { + long id; + CoroutineAdaptor(Runnable runnable) { + super(runnable); + id = seq.addAndGet(1); + } + } + + class Stat { + int stealCnt; + int yieldCnt; + int stealFailedCnt; + + @Override + public String toString() { + return "\n < steal Cnt " + stealCnt + ">, < yieldCnt" + yieldCnt + "> , < stealFailedCnt " + stealFailedCnt + " > "; + } + } +} diff --git a/test/java/dyn/CoroutineTest.java b/test/java/dyn/CoroutineTest.java index 968fc9ac1e1281f242c514a63df97c13657962f3..94c8888b223963654e4ee9b258a225ea8f32e6b3 100644 --- a/test/java/dyn/CoroutineTest.java +++ b/test/java/dyn/CoroutineTest.java @@ -31,7 +31,6 @@ package test.java.dyn; import java.dyn.Coroutine; -import java.dyn.AsymCoroutine; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; @@ -51,188 +50,78 @@ public class CoroutineTest { @Test public void symSequence() { + Coroutine threadCoro = Thread.currentThread().getCoroutineSupport().threadCoroutine(); Coroutine coro = new Coroutine() { protected void run() { seq.append("c"); for (int i = 0; i < 3; i++) { - yield(); + Coroutine.yieldTo(threadCoro); seq.append("e"); } } }; seq.append("b"); assertFalse(coro.isFinished()); - Coroutine.yield(); + Coroutine.yieldTo(coro); for (int i = 0; i < 3; i++) { seq.append("d"); assertFalse(coro.isFinished()); - Coroutine.yield(); + Coroutine.yieldTo(coro); } seq.append("f"); assertTrue(coro.isFinished()); - Coroutine.yield(); seq.append("g"); assertEquals("abcdededefg", seq.toString()); } - @Test - public void symMultiSequence() { - for (int i = 0; i < 10; i++) - new Coroutine() { - protected void run() { - seq.append("c"); - yield(); - seq.append("e"); - } - }; - seq.append("b"); - Coroutine.yield(); - seq.append("d"); - Coroutine.yield(); - seq.append("f"); - Coroutine.yield(); - seq.append("g"); - assertEquals("abccccccccccdeeeeeeeeeefg", seq.toString()); - } - - @Test - public void asymSequence() { - AsymCoroutine coro = new AsymCoroutine() { - protected Void run(Void value) { - seq.append(value + "b"); - Object o = ret(); - seq.append(o + "d"); - return null; - } - }; - assertFalse(coro.isFinished()); - coro.call(); - assertFalse(coro.isFinished()); - seq.append("c"); - coro.call(); - seq.append("e"); - assertTrue(coro.isFinished()); - - RuntimeException exception = null; - try { - coro.call(); - } catch (RuntimeException e) { - exception = e; - } - assertNotNull(exception); - assertEquals("anullbcnullde", seq.toString()); - } - - @Test - public void asymMultiSequence() { - AsymCoroutine coro = null; - for (int j = 4; j >= 0; j--) { - final AsymCoroutine last = coro; - final int i = j; - coro = new AsymCoroutine() { - protected Void run(Void value) { - seq.append("b" + i); - if (last != null) - last.call(); - seq.append("c" + i); - ret(); - seq.append("e" + i); - if (last != null) - last.call(); - seq.append("f" + i); - return null; - } - }; - } - seq.append("_"); - assertFalse(coro.isFinished()); - coro.call(); - assertFalse(coro.isFinished()); - seq.append("d"); - coro.call(); - seq.append("g"); - assertTrue(coro.isFinished()); - - RuntimeException exception = null; - try { - coro.call(); - } catch (RuntimeException e) { - exception = e; - } - assertNotNull(exception); - assertEquals("a_b0b1b2b3b4c4c3c2c1c0de0e1e2e3e4f4f3f2f1f0g", seq.toString()); - } - - @Test - public void asymReturnValue() { - AsymCoroutine coro = new AsymCoroutine() { - protected Integer run(Integer value) { - value = ret(value * 2 + 1); - value = ret(value * 2 + 2); - value = ret(value * 2 + 3); - value = ret(value * 2 + 4); - value = ret(value * 2 + 5); - return value * 2 + 6; - } - }; - assertFalse(coro.isFinished()); - assertEquals(2001, (int) coro.call(1000)); - assertEquals(4002, (int) coro.call(2000)); - assertEquals(6003, (int) coro.call(3000)); - assertEquals(8004, (int) coro.call(4000)); - assertEquals(10005, (int) coro.call(5000)); - assertEquals(12006, (int) coro.call(6000)); - assertTrue(coro.isFinished()); - } @Test public void gcTest1() { - new Coroutine() { + Coroutine threadCoro = Thread.currentThread().getCoroutineSupport().threadCoroutine(); + Coroutine coro = new Coroutine() { protected void run() { seq.append("c"); Integer v1 = 1; Integer v2 = 14555668; - yield(); + yieldTo(threadCoro); seq.append("e"); seq.append("(" + v1 + "," + v2 + ")"); } }; seq.append("b"); System.gc(); - Coroutine.yield(); + Coroutine.yieldTo(coro); System.gc(); seq.append("d"); - Coroutine.yield(); - seq.append("f"); - Coroutine.yield(); - seq.append("g"); + Coroutine.yieldTo(coro); + seq.append("fg"); assertEquals("abcde(1,14555668)fg", seq.toString()); } @Test public void exceptionTest1() { + Coroutine threadCoro = Thread.currentThread().getCoroutineSupport().threadCoroutine(); Coroutine coro = new Coroutine() { protected void run() { seq.append("c"); long temp = System.nanoTime(); if (temp != 0) throw new RuntimeException(); - yield(); seq.append("e"); } }; seq.append("b"); assertFalse(coro.isFinished()); - Coroutine.yield(); + Coroutine.yieldTo(coro); seq.append("d"); - Coroutine.yield(); seq.append("f"); assertEquals("abcdf", seq.toString()); } @Test public void largeStackframeTest() { - new Coroutine() { + Coroutine threadCoro = Thread.currentThread().getCoroutineSupport().threadCoroutine(); + Coroutine coro = new Coroutine() { protected void run() { seq.append("c"); Integer v0 = 10000; @@ -255,66 +144,71 @@ public class CoroutineTest { Integer v17 = 10017; Integer v18 = 10018; Integer v19 = 10019; - yield(); + yieldTo(threadCoro); int sum = v0 + v1 + v2 + v3 + v4 + v5 + v6 + v7 + v8 + v9 + v10 + v11 + v12 + v13 + v14 + v15 + v16 + v17 + v18 + v19; seq.append("e" + sum); } }; seq.append("b"); System.gc(); - Coroutine.yield(); + Coroutine.yieldTo(coro); System.gc(); seq.append("d"); - Coroutine.yield(); + Coroutine.yieldTo(coro); seq.append("f"); assertEquals("abcde200190f", seq.toString()); } @Test public void shaTest() { + Coroutine threadCoro = Thread.currentThread().getCoroutineSupport().threadCoroutine(); Coroutine coro = new Coroutine(65536) { protected void run() { try { MessageDigest digest = MessageDigest.getInstance("SHA"); digest.update("TestMessage".getBytes()); seq.append("b"); - yield(); + yieldTo(threadCoro); seq.append(digest.digest()[0]); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } } }; - Coroutine.yield(); + Coroutine.yieldTo(coro); seq.append("c"); assertFalse(coro.isFinished()); - Coroutine.yield(); + Coroutine.yieldTo(coro); assertTrue(coro.isFinished()); assertEquals("abc72", seq.toString()); } public void stackoverflowTest() { - for (int i = 0; i < 10; i++) { - new Coroutine(65536) { - int i = 0; - - protected void run() { - System.out.println("start"); - try { - iter(); - } catch (StackOverflowError e) { - System.out.println("i: " + i); - } - System.out.println("asdf"); - } + Coroutine threadCoro = Thread.currentThread().getCoroutineSupport().threadCoroutine(); + new Coroutine(255) { + int i = 0; - private void iter() { - System.out.print("."); - i++; + protected void run() { + System.out.println("start"); + try { iter(); + } catch (StackOverflowError e) { + System.out.println("i: " + i); } - }; - } - Coroutine.yield(); + System.out.println("asdf"); + } + + private void iter() { + System.out.print("."); + i++; + iter(); + } + }; + Coroutine.yieldTo(threadCoro); } + + @Test + public void destroyNonInitedTest() { + new Coroutine(); + } }