提交 f09d14b6 编写于 作者: C Chuansheng Lu 提交者: Jonathan Lu

[MultiTenant] MultiTenant framework

Summary: ported MultiTenant framework to Dragonwell8

Test Plan: jdk/test/multi-tenant

Reviewed-by: yuleil, sanhong

Issue: https://github.com/alibaba/dragonwell8/issues/84
上级 76ecee17
......@@ -43,7 +43,8 @@ H_TARGET_FILES = $(INCLUDEDIR)/jdwpTransport.h \
$(INCLUDEDIR)/classfile_constants.h \
$(INCLUDEDIR)/jawt.h \
$(OPENJDK_TARGET_OS_INCLUDE)/jni_md.h \
$(OPENJDK_TARGET_OS_INCLUDE)/jawt_md.h
$(OPENJDK_TARGET_OS_INCLUDE)/jawt_md.h \
$(INCLUDEDIR)/tenantenv.h \
$(INCLUDEDIR)/%.h: $(JDK_TOPDIR)/src/share/javavm/export/%.h
$(call install-file)
......
......@@ -560,7 +560,8 @@ EXPORTED_PRIVATE_PKGS = com.oracle.net \
com.alibaba.jwarmup \
com.alibaba.management \
com.alibaba.jvm.gc \
com.alibaba.rcm
com.alibaba.rcm \
com.alibaba.tenant
$(IMAGES_OUTPUTDIR)/symbols/_the.symbols: $(IMAGES_OUTPUTDIR)/lib/rt.jar
$(RM) -r $(IMAGES_OUTPUTDIR)/symbols/META-INF/sym
......
......@@ -2559,4 +2559,10 @@ sun/awt/X11/XErrorEvent
com/alibaba/jwarmup/JWarmUp
com/alibaba/management
com/alibaba/jvm/gc
com/alibaba/tenant/TenantContainer
com/alibaba/tenant/TenantConfiguration
com/alibaba/tenant/TenantException
com/alibaba/tenant/TenantState
com/alibaba/tenant/TenantGlobals
com/alibaba/tenant/TenantContainerFactory
# eea35d9d56e0006e
\ No newline at end of file
......@@ -143,6 +143,7 @@ LIBJAVA_SRC_DIRS += $(JDK_TOPDIR)/src/$(OPENJDK_TARGET_OS_API_DIR)/native/java/l
$(JDK_TOPDIR)/src/share/native/common \
$(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/sun/misc \
$(JDK_TOPDIR)/src/share/native/sun/reflect \
$(JDK_TOPDIR)/src/share/native/java/util \
......
......@@ -215,7 +215,10 @@ SUNWprivate_1.1 {
Java_java_lang_System_setIn0;
Java_java_lang_System_setOut0;
Java_java_lang_Thread_registerNatives;
Java_com_alibaba_tenant_NativeDispatcher_registerNatives0;
Java_com_alibaba_tenant_NativeDispatcher_getThreadsAllocatedMemory;
Java_com_alibaba_jwarmup_JWarmUp_registerNatives;
Java_com_alibaba_tenant_TenantGlobals_getTenantFlags;
Java_com_alibaba_jvm_gc_ElasticHeapMXBeanImpl_registerNatives;
Java_java_lang_Throwable_fillInStackTrace;
Java_java_lang_Throwable_getStackTraceDepth;
......
......@@ -10,6 +10,7 @@ text: .text%collapse: OUTPUTDIR/canonicalize_md.o;
text: .text%Java_java_lang_Object_registerNatives;
text: .text%Java_java_lang_System_registerNatives;
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_java_security_AccessController_getStackAccessControlContext;
......
package com.alibaba.management;
import java.lang.management.PlatformManagedObject;
import java.util.List;
/**
* MXBean interface to interact with TenantContainer object
*/
public interface TenantContainerMXBean extends PlatformManagedObject {
/**
* Retrieve all the IDs of all created {@code TenantContainer}'s
* @return List of TenantContainer IDs
*/
public List<Long> getAllTenantIds();
/**
* Retrieve the accumulated allocated memory size in bytes
* of a {@code TenantContainer} represented by {@code id}
* @param id ID of the {@code TenantContainer} object to be queried
* @return Accumulated allocated memory size in bytes
* @throws IllegalArgumentException If cannot find valid {@code TenantContainer}
* object corresponding to the given ID
*/
public long getTenantAllocatedMemoryById(long id);
/**
* Retrieve name of a {@code TenantContainer} object represent by 'id'
* @param id ID of the {@code TenantContainer} object to be queried
* @return Name of found ID
* @throws IllegalArgumentException If cannot find valid {@code TenantContainer}
* object corresponding to the given ID
*/
public String getTenantNameById(long id);
}
/*
* 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.tenant;
import java.security.AccessController;
/**
* All cgroup operations are in this class
*
*/
class NativeDispatcher {
// Attach the current thread to given {@code tenant}
native void attach(TenantContainer tenant);
// Gets an array containing the amount of memory allocated on the Java heap for a set of threads (in bytes)
native void getThreadsAllocatedMemory(long[] ids, long[] memSizes);
private static native void registerNatives0();
static {
AccessController.doPrivileged(
new java.security.PrivilegedAction<Void>() {
public Void run() {
System.loadLibrary("java");
return null;
}
});
registerNatives0();
}
}
\ No newline at end of file
/*
* 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.tenant;
import sun.misc.SharedSecrets;
import sun.misc.VM;
import sun.security.action.GetPropertyAction;
import java.security.AccessController;
import java.util.HashMap;
import java.util.Map;
import java.util.Collection;
import com.alibaba.rcm.ResourceType;
import com.alibaba.rcm.Constraint;
/**
*
* The configuration used by tenant
*/
public class TenantConfiguration {
/*
* Resource throttling configurations
*/
private Map<ResourceType, Constraint> constraints = new HashMap<>();
/**
* Create an empty TenantConfiguration, no limitations on any resource
*/
public TenantConfiguration() {
}
/**
* Build TenantConfiguration with given constraints
* @param constraints
*/
public TenantConfiguration(Iterable<Constraint> constraints) {
if (constraints != null) {
for (Constraint c : constraints) {
this.constraints.put(c.getResourceType(), c);
}
}
}
/*
* @return all resource limits specified by this configuration
*/
Collection<Constraint> getAllConstraints() {
return constraints.values();
}
void setConstraint(Constraint constraint) {
constraints.put(constraint.getResourceType(), constraint);
}
}
/*
* 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.tenant;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.LinkedList;
import java.util.Map;
import java.util.Properties;
import java.util.WeakHashMap;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Supplier;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import com.alibaba.rcm.ResourceContainer;
import com.alibaba.rcm.Constraint;
import com.alibaba.rcm.internal.AbstractResourceContainer;
import static com.alibaba.tenant.TenantState.*;
/**
* TenantContainer is a "virtual container" for a tenant of application, the
* resource consumption of tenant such as CPU, heap is constrained by the policy
* of this "virtual container". The thread can run in virtual container by
* calling <code>TenantContainer.run</code>
*
*/
public class TenantContainer {
TenantResourceContainer resourceContainer;
private static NativeDispatcher nd = new NativeDispatcher();
/*
* Used to generate the tenant id.
*/
private static AtomicLong nextTenantID = new AtomicLong(0);
/*
* Used to hold the mapping from tenant id to TenantContainer object for all
* tenants
*/
private static Map<Long, TenantContainer> tenantContainerMap = null;
/*
* Holds the threads attached with this tenant container
*/
private List<Thread> attachedThreads = new LinkedList<>();
/*
* Newly created threads which attach to this tenant container
*/
private List<WeakReference<Thread>> spawnedThreads = Collections.synchronizedList(new ArrayList<>());
/*
* Used to contain service threads, including finalizer threads, shutdown hook threads.
*/
private Map<Thread, Void> serviceThreads = Collections.synchronizedMap(new WeakHashMap<>());
/*
* the configuration of this tenant container
*/
private TenantConfiguration configuration = null;
/*
* tenant state
*/
private volatile TenantState state;
/*
* tenant id
*/
private long tenantId;
/*
* tenant name
*/
private String name;
/*
* Used to store the system properties per tenant
*/
private Properties props;
/*
* allocated memory of attached threads, which is accumulated for current tenant
*/
private long accumulatedMemory = 0L;
/**
* Get total allocated memory of this tenant.
* @return the total allocated memory of this tenant.
*/
public synchronized long getAllocatedMemory() {
Thread[] threads = getAttachedThreads();
int size = threads.length;
long[] ids = new long[size];
long[] memSizes = new long[size];
for (int i = 0; i < size; i++) {
ids[i] = threads[i].getId();
}
nd.getThreadsAllocatedMemory(ids, memSizes);
long totalThreadsAllocatedMemory = 0;
for (long s : memSizes) {
totalThreadsAllocatedMemory += s;
}
return totalThreadsAllocatedMemory + accumulatedMemory;
}
/**
* Used to track and run tenant shutdown hooks
*/
private TenantShutdownHooks tenantShutdownHooks = new TenantShutdownHooks();
/**
* Sets the tenant properties to the one specified by argument.
* @param props the properties to be set, CoW the system properties if it is null.
*/
public void setProperties(Properties props) {
if (props == null) {
props = new Properties();
Properties sysProps = System.getProperties();
for(Object key: sysProps.keySet()) {
props.put(key, sysProps.get(key));
}
}
this.props = props;
}
/**
* Gets the properties of tenant
* @return the tenant properties
*/
public Properties getProperties() {
return props;
}
/**
* Sets the property indicated by the specified key.
* @param key the name of the property.
* @param value the value of the property.
* @return the previous value of the property,
* or null if it did not have one.
*/
public String setProperty(String key, String value) {
checkKey(key);
return (String) props.setProperty(key, value);
}
/**
* Gets the property indicated by the specified key.
* @param key the name of the property.
* @return the string value of the property,
* or null if there is no property with that key.
*/
public String getProperty(String key) {
checkKey(key);
return props.getProperty(key);
}
/**
* Removes the property indicated by the specified key.
* @param key the name of the property to be removed.
* @return the previous string value of the property,
* or null if there was no property with that key.
*/
public String clearProperty(String key) {
checkKey(key);
return (String) props.remove(key);
}
private void checkKey(String key) {
if (null == key) {
throw new NullPointerException("key can't be null");
}
if ("".equals(key)) {
throw new IllegalArgumentException("key can't be empty");
}
}
//
// Used to synchronize between destroy() and runThread()
private ReentrantReadWriteLock destroyLock = new ReentrantReadWriteLock();
/**
* Destroy this tenant container and release occupied resources including memory, cpu, FD, etc.
*
*/
public void destroy() {
if (TenantContainer.current() != null) {
throw new RuntimeException("Should only call destroy() in ROOT tenant");
}
destroyLock.writeLock().lock();
try {
if (state != TenantState.STOPPING && state != TenantState.DEAD) {
setState(TenantState.STOPPING);
tenantContainerMap.remove(getTenantId());
// finish all finalizers
resourceContainer.attach();
nd.attach(this);
try {
Runtime.getRuntime().runFinalization();
} finally {
nd.attach(null);
resourceContainer.detach();
}
// execute all shutdown hooks
tenantShutdownHooks.runHooks();
}
} catch (Throwable t) {
System.err.println("Exception from TenantContainer.destroy()");
t.printStackTrace();
} finally {
setState(TenantState.DEAD);
cleanUp();
destroyLock.writeLock().unlock();
}
}
/*
* Release all native resources and Java references
* should be the very last step of {@link #destroy()} operation.
* If cannot kill all threads in {@link #killAllThreads()}, should do this in {@link WatchDogThread}
*
*/
private void cleanUp() {
// clear references
spawnedThreads.clear();
attachedThreads.clear();
tenantShutdownHooks = null;
}
private TenantContainer(TenantContainer parent, String name, TenantConfiguration configuration) {
this.tenantId = nextTenantID.getAndIncrement();
this.resourceContainer = new TenantResourceContainer(
(parent == null ? null : parent.resourceContainer),
this,
configuration.getAllConstraints());
this.name = (name == null ? "Tenant-" + getTenantId() : name);
this.configuration = configuration;
setState(STARTING);
//Initialize the tenant properties.
props = new Properties();
props.putAll(System.getProperties());
tenantContainerMap.put(this.tenantId, this);
}
TenantConfiguration getConfiguration() {
return configuration;
}
/**
* @return the tenant state
*/
public TenantState getState() {
return state;
}
/*
* Set the tenant state
* @param state used to set
*/
void setState(TenantState state) {
this.state = state;
}
/**
* Returns the tenant' id
* @return tenant id
*/
public long getTenantId() {
return tenantId;
}
/**
* Returns this tenant's name.
* @return this tenant's name.
*/
public String getName() {
return name;
}
/**
* @return A collection of all threads attached to the container.
*/
public synchronized Thread[] getAttachedThreads() {
return attachedThreads.toArray(new Thread[attachedThreads.size()]);
}
/**
* Get the tenant container by id
* @param id tenant id.
* @return the tenant specified by id, null if the id doesn't exist.
*/
public static TenantContainer getTenantContainerById(long id) {
checkIfTenantIsEnabled();
return tenantContainerMap.get(id);
}
/**
* Create tenant container by the configuration
* @param configuration used to create tenant
* @return the tenant container
*/
public static TenantContainer create(TenantConfiguration configuration) {
return create(TenantContainer.current(), configuration);
}
/**
* Create tenant container by the configuration
* @param parent parent tenant container
* @param configuration used to create tenant
* @return the tenant container
*/
public static TenantContainer create(TenantContainer parent,
TenantConfiguration configuration) {
checkIfTenantIsEnabled();
//parameter checking
if (null == configuration) {
throw new IllegalArgumentException("Failed to create tenant, illegal arguments: configuration is null");
}
return create(parent, null, configuration);
}
/**
* Create tenant container by the name and configuration
* @param name the tenant name
* @param configuration used to create tenant
* @return the tenant container
*/
public static TenantContainer create(String name, TenantConfiguration configuration) {
return create(TenantContainer.current(), name, configuration);
}
/**
* Create tenant container by the name and configuration
* @param parent parent tenant container
* @param name the tenant name
* @param configuration used to create tenant
* @return the tenant container
*/
public static TenantContainer create(TenantContainer parent, String name, TenantConfiguration configuration) {
checkIfTenantIsEnabled();
//parameter checking
if (null == configuration) {
throw new IllegalArgumentException("Failed to create tenant, illegal arguments: configuration is null");
}
TenantContainer tc = new TenantContainer(parent, name, configuration);
tenantContainerMap.put(tc.getTenantId(), tc);
return tc;
}
/**
* Gets the tenant id list
* @return the tenant id list, Collections.emptyList if no tenant exists.
*/
public static List<Long> getAllTenantIds() {
checkIfTenantIsEnabled();
if (null == tenantContainerMap) {
throw new IllegalStateException("TenantContainer class is not initialized !");
}
if (tenantContainerMap.size() == 0) {
return Collections.EMPTY_LIST;
}
return new ArrayList<>(tenantContainerMap.keySet());
}
/**
* Gets the TenantContainer attached to the current thread.
* @return The TenantContainer attached to the current thread, null if no
* TenantContainer is attached to the current thread.
*/
public static TenantContainer current() {
checkIfTenantIsEnabled();
AbstractResourceContainer curResContainer = TenantResourceContainer.current();
if (TenantResourceContainer.root() == curResContainer) {
return null;
}
assert curResContainer instanceof TenantResourceContainer;
return ((TenantResourceContainer)curResContainer).getTenant();
}
/**
* Gets the cpu time consumed by this tenant
* @return the cpu time used by this tenant, 0 if tenant cpu throttling or accounting feature is disabled.
*/
public long getProcessCpuTime() {
if (!TenantGlobals.isCpuAccountingEnabled()) {
throw new IllegalStateException("-XX:+TenantCpuAccounting is not enabled");
}
long cpuTime = 0;
return cpuTime;
}
/**
* Runs the code in the target tenant container
* @param task the code to run
*/
public void run(Runnable task) throws TenantException {
if (getState() == DEAD || getState() == STOPPING) {
throw new TenantException("Tenant is dead");
}
TenantContainer container = current();
assert container != null;
if (container == this) {
task.run();
} else {
if (container != null) {
throw new TenantException("must be in root container " +
"before running into non-root container.");
}
attach();
try {
task.run();
} finally {
detach();
}
}
}
/*
* Get accumulatedMemory value of current thread
*/
private long getThreadAllocatedMemory() {
long[] memSizes = new long[1];
nd.getThreadsAllocatedMemory(null, memSizes);
return memSizes[0];
}
private void attach() {
// This is the first thread which runs in this tenant container
if (getState() == TenantState.STARTING) {
// move the tenant state to RUNNING
this.setState(TenantState.RUNNING);
}
Thread curThread = Thread.currentThread();
long curAllocBytes = getThreadAllocatedMemory();
synchronized (this) {
attachedThreads.add(curThread);
accumulatedMemory -= curAllocBytes;
resourceContainer.attach();
nd.attach(this);
}
}
private void detach() {
Thread curThread = Thread.currentThread();
long curAllocBytes = getThreadAllocatedMemory();
synchronized (this) {
nd.attach(null);
resourceContainer.detach();
attachedThreads.remove(curThread);
accumulatedMemory += curAllocBytes;
}
}
/*
* Check if the tenant feature is enabled.
*/
private static void checkIfTenantIsEnabled() {
if (!TenantGlobals.isTenantEnabled()) {
throw new UnsupportedOperationException("The multi-tenant feature is not enabled!");
}
}
/*
* Invoked by the VM to run a thread in multi-tenant mode.
*
* NOTE: please ensure relevant logic has been fully understood before changing any code
*
* @throws TenantException
*/
private void runThread(final Thread thread) throws TenantException {
if (destroyLock.readLock().tryLock()) {
if (getState() != STOPPING && getState() != DEAD) {
spawnedThreads.add(new WeakReference<>(thread));
this.run(() -> {
destroyLock.readLock().unlock();
thread.run();
});
} else {
destroyLock.readLock().unlock();
}
// try to clean up once
if (destroyLock.readLock().tryLock()) {
if (getState() != STOPPING && getState() != DEAD) {
spawnedThreads.removeIf(ref -> ref.get() == null || ref.get() == thread);
}
destroyLock.readLock().unlock();
}
} else {
// shutdown in progress
if (serviceThreads.containsKey(thread)) {
// attach to current thread to run without registering
resourceContainer.attach();
nd.attach(this);
try {
thread.run();
} finally {
nd.attach(null);
resourceContainer.detach();
removeServiceThread(thread);
}
}
}
}
/*
* Initialize the TenantContainer class, called after System.initializeSystemClass by VM.
*/
private static void initializeTenantContainerClass() {
//Initialize this field after the system is booted.
tenantContainerMap = Collections.synchronizedMap(new HashMap());
try {
// force initialization of TenantConfiguration
Class.forName("com.alibaba.tenant.TenantConfiguration");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
/**
* Runs {@code Supplier.get} in the root tenant.
* @param supplier target used to call
* @return the result of {@code Supplier.get}
*/
public static <T> T primitiveRunInRoot(Supplier<T> supplier) {
// thread is already in root tenant.
if(null == TenantContainer.current()) {
return supplier.get();
} else{
TenantContainer tenant = TenantContainer.current();
//Force to root tenant.
tenant.resourceContainer.detach();
nd.attach(null);
try {
T t = supplier.get();
return t;
} finally {
nd.attach(tenant);
tenant.resourceContainer.attach();
}
}
}
/**
* Runs a block of code in the root tenant.
* @param runnable the code to run
*/
public static void primitiveRunInRoot(Runnable runnable) {
primitiveRunInRoot(() -> {
runnable.run();
return null;
});
}
/**
* Register a new tenant shutdown hook.
* When the tenant begins its destroy it will
* start all registered shutdown hooks in some unspecified order and let
* them run concurrently.
* @param hook
* An initialized but unstarted <tt>{@link Thread}</tt> object
*/
public void addShutdownHook(Thread hook) {
addServiceThread(hook);
tenantShutdownHooks.add(hook);
}
/**
* De-registers a previously-registered tenant shutdown hook.
* @param hook the hook to remove
* @return true if the specified hook had previously been
* registered and was successfully de-registered, false
* otherwise.
*/
public boolean removeShutdownHook(Thread hook) {
removeServiceThread(hook);
return tenantShutdownHooks.remove(hook);
}
// add a thread to the service thread list
private void addServiceThread(Thread thread) {
if (thread != null) {
serviceThreads.put(thread, null);
}
}
// remove a thread from the service thread list
private void removeServiceThread(Thread thread) {
serviceThreads.remove(thread);
}
/**
* Try to modify resource limit of current tenant,
* for resource whose limit cannot be changed after creation of {@code TenantContainer}, its limit will be ignored.
* @param config new TenantConfiguration to
*/
public void update(TenantConfiguration config) {
for (Constraint constraint : config.getAllConstraints()) {
updateConstraint(constraint);
}
}
void updateConstraint(Constraint constraint) {
resourceContainer.updateConstraint(constraint);
getConfiguration().setConstraint(constraint);
}
/**
* @return {@code ResourceContainer} of this tenant
*/
public ResourceContainer getResourceContainer() {
return resourceContainer;
}
}
/*
* 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.tenant;
import com.alibaba.rcm.Constraint;
import com.alibaba.rcm.ResourceContainer;
import com.alibaba.rcm.ResourceContainerFactory;
/**
* Singleton factory specialized for multi-tenant {@code ResourceContainer}
* With support of new RCM API (com.alibaba.rcm)
*
*/
public class TenantContainerFactory implements ResourceContainerFactory {
/**
* Create a {@code ResourceContainer} which is capable of throttling resource
* using MuliTenant facitlities.
* A {@code TenantContainer} object will be created implicitly for each successful
* call to {@code TenantContainerFactory.createContainer}
* @param constraints the target {@code Constraint}s
* @return
*/
@Override
public TenantResourceContainer createContainer(Iterable<Constraint> constraints) {
TenantContainer tenant = TenantContainer.create(new TenantConfiguration(constraints));
return tenant.resourceContainer;
}
/**
* Retrieve the {@code TenantContainer} object associated with given {@code ResourceContainer}
*
* @param resourceContainer
* @return
*/
public static TenantContainer tenantContainerOf(ResourceContainer resourceContainer) {
if (!(resourceContainer instanceof TenantResourceContainer)) {
throw new IllegalArgumentException("Incoming ResourceContainer is not for MultiTenant");
}
return ((TenantResourceContainer) resourceContainer).getTenant();
}
private TenantContainerFactory() { }
private static final class Holder {
private static final TenantContainerFactory INSTANCE = new TenantContainerFactory();
}
/**
*
* @return Singleton instance of TenantContainerFactory
*/
public static TenantContainerFactory instance() {
return Holder.INSTANCE;
}
}
\ No newline at end of file
package com.alibaba.tenant;
import com.alibaba.management.TenantContainerMXBean;
import sun.management.Util;
import javax.management.ObjectName;
import java.util.List;
/**
* Implementation class for TenantContainerMXBean.
*/
public class TenantContainerMXBeanImpl implements TenantContainerMXBean {
private final static String TENANT_CONTAINER_MXBEAN_NAME = "com.alibaba.management:type=TenantContainer";
@Override
public List<Long> getAllTenantIds() {
return TenantContainer.getAllTenantIds();
}
public long getTenantAllocatedMemoryById(long id) {
TenantContainer container = TenantContainer.getTenantContainerById(id);
if (null == container) {
throw new IllegalArgumentException("The id of tenant is invalid !");
}
return container.getAllocatedMemory();
}
@Override
public ObjectName getObjectName() {
return Util.newObjectName(TENANT_CONTAINER_MXBEAN_NAME);
}
@Override
public String getTenantNameById(long id) {
TenantContainer container = TenantContainer.getTenantContainerById(id);
if (null == container) {
throw new IllegalArgumentException("The id of tenant is invalid !");
}
return container.getName();
}
}
/*
* 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.tenant;
/**
* Will be thrown when problems found with running a given code snippet
* in a {@code TenantContainer} with method {@code TenantContainer.run()},
* or from newly created thread in a TenantContainer.
*
*/
public class TenantException extends Exception {
private static final long serialVersionUID = 436814125546098458L;
/**
* Construct a {@code TenantException} object with given message
*/
public TenantException(String msg) {
super(msg);
}
}
/*
* 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.tenant;
/**
* This class defines the constants used by multi-tenant JDK
*/
public class TenantGlobals {
/**
* Retrieves the flags used by multi-tenant module.
* @return the flags
*/
private native static int getTenantFlags();
private final static int flags = getTenantFlags();
/**** Be careful: the following bit definitions must be consistent with
**** the ones defined in prims/tenantenv.cpp **/
/**
* Bit to indicate that if the multi-tenant feature is enabled.
*/
public static final int TENANT_FLAG_MULTI_TENANT_ENABLED = 0x1;
/**
* Bit to indicate that if heap throttling feature is enabled
*/
public static final int TENANT_FLAG_HEAP_THROTTLING_ENABLED = 0x2;
/**
* Bit to indicate that if cpu throttling feature is enabled
*/
public static final int TENANT_FLAG_CPU_THROTTLING_ENABLED = 0x4;
/**
* Bit to indicate that if cpu accounting feature is enabled
*/
public static final int TENANT_FLAG_CPU_ACCOUNTING_ENABLED = 0x40;
/**
* Bit to indicate that if heap isolation feature is enabled
*/
public static final int TENANT_FLAG_HEAP_ISOLATION_ENABLED = 0x80;
private TenantGlobals() { }
/**
* Test if multi-tenant feature is enabled.
* @return true if enabled otherwise false
*/
public static boolean isTenantEnabled() {
return 0 != (flags & TENANT_FLAG_MULTI_TENANT_ENABLED);
}
/**
* Test if heap throttling feature is enabled.
* @return true if enabled otherwise false
*/
public static boolean isHeapThrottlingEnabled() {
return 0 != (flags & TENANT_FLAG_HEAP_THROTTLING_ENABLED);
}
/**
* Test if heap isolation feature is enabled.
* @return true if enabled otherwise false
*/
public static boolean isHeapIsolationEnabled() {
return 0 != (flags & TENANT_FLAG_HEAP_ISOLATION_ENABLED);
}
/**
* Test if cpu throttling feature is enabled.
* @return true if enabled otherwise false
*/
public static boolean isCpuThrottlingEnabled() {
return 0 != (flags & TENANT_FLAG_CPU_THROTTLING_ENABLED);
}
/**
* Test if cpu accounting feature is enabled.
* @return true if enabled otherwise false
*/
public static boolean isCpuAccountingEnabled() {
return 0 != (flags & TENANT_FLAG_CPU_ACCOUNTING_ENABLED);
}
}
/*
* 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.tenant;
import com.alibaba.rcm.Constraint;
import com.alibaba.rcm.ResourceType;
import com.alibaba.rcm.internal.AbstractResourceContainer;
import java.util.HashMap;
import java.util.Map;
import static com.alibaba.tenant.TenantState.*;
class TenantResourceContainer extends AbstractResourceContainer {
TenantResourceContainer(TenantResourceContainer parent,
TenantContainer tenant,
Iterable<Constraint> constraints) {
super();
this.parent = parent;
this.constraints = new HashMap<>();
this.tenant = tenant;
if (constraints != null) {
constraints.forEach(c -> this.constraints.put(c.getResourceType(), c));
}
init();
}
private void init() {
if (TenantGlobals.isCpuThrottlingEnabled() || TenantGlobals.isCpuAccountingEnabled()) {
if (constraints.containsKey(ResourceType.CPU_PERCENT)) {
Constraint c = translate(constraints.remove(ResourceType.CPU_PERCENT));
constraints.put(c.getResourceType(), c);
}
}
}
private static Constraint translate(Constraint constraint) {
return constraint;
}
// cached constraints
private Map<ResourceType, Constraint> constraints;
/*
* The parent container.
*/
private TenantResourceContainer parent;
/*
* Associated TenantContainer object
*/
private TenantContainer tenant;
TenantResourceContainer getParent() {
return parent;
}
TenantContainer getTenant() {
return this.tenant;
}
@Override
protected void attach() {
super.attach();
}
@Override
protected void detach() {
super.detach();
}
@Override
public void run(Runnable command) {
// This is the first thread which runs in this tenant container
if (tenant.getState() == STARTING) {
// move the tenant state to RUNNING
tenant.setState(RUNNING);
}
super.run(command);
}
@Override
public State getState() {
return TenantState.translate(tenant.getState());
}
@Override
public void updateConstraint(Constraint constraint) {
Constraint c = translate(constraint);
constraints.put(c.getResourceType(), c);
}
@Override
public Iterable<Constraint> getConstraints() {
return constraints.values();
}
@Override
public void destroy() {
throw new UnsupportedOperationException("Should not call TenantResourceContainer::destroy() directly");
}
void destroyImpl() {
parent = null;
tenant = null;
}
// exposed to TenantContainer implementation
long getProcessCpuTime() {
return 0;
}
}
/*
* 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.tenant;
import com.alibaba.rcm.ResourceType;
/*
* Type of resource that can be throttled when MultiTenant feature enabled
*/
class TenantResourceType extends ResourceType {
private TenantResourceType(String name, boolean isJGroup) {
super(name);
}
}
/*
* 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.tenant;
import java.util.Collection;
import java.util.IdentityHashMap;
/*
* Class to track and run tenant level shutdown hooks registered through
* <tt>{@link Runtime#addShutdownHook Runtime.addShutdownHook}</tt> or
* <tt>{@link TenantContainer#addShutdownHook TenantContainer.addShutdownHook}</tt>
*
*/
class TenantShutdownHooks {
private IdentityHashMap<Thread, Thread> hooks = new IdentityHashMap<>();
private Collection<Thread> threads = null;
TenantShutdownHooks() {
}
//Add a new shutdown hook.
synchronized void add(Thread hook) {
if(hooks == null) {
throw new IllegalStateException("Shutdown in progress");
}
if (hook.isAlive()) {
throw new IllegalArgumentException("Hook already running");
}
if (hooks.containsKey(hook)) {
throw new IllegalArgumentException("Hook previously registered");
}
hooks.put(hook, hook);
}
//Remove a previously-registered hook.
synchronized boolean remove(Thread hook) {
if(hooks == null) {
throw new IllegalStateException("Shutdown in progress");
}
if (hook == null) {
throw new NullPointerException();
}
return hooks.remove(hook) != null;
}
/* Iterates over all hooks creating a new thread for each
* to run in. Hooks are running concurrently and this method waits for
* them to finish. This function will be called by the tenant destroying
* thread at the beginning of {@code TenantContainer.destroy}.
*/
void runHooks() {
synchronized(this) {
threads = hooks.keySet();
hooks = null;
}
for (Thread hook : threads) {
hook.start();
}
for (Thread hook : threads) {
try {
hook.join();
} catch (InterruptedException x) { }
}
}
}
/*
* 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.tenant;
import com.alibaba.rcm.ResourceContainer.State;
/**
* Defines the state used by TenantContainer
*/
public enum TenantState {
/**
* Just created
*/
STARTING,
/**
* At least one task has been submitted after creation
*/
RUNNING,
/**
* {@code TenantContainer.destroy()} has been called, no new thread
* can be created in this container.
*/
STOPPING,
/**
* All resource has been released fromm this tenant.
* No new calls to {@code TenantContainer.run()} can happen.
*/
DEAD;
static TenantState translate(State state) {
return TenantState.values()[state.ordinal()];
}
static State translate(TenantState tenantState) {
return State.values()[tenantState.ordinal()];
}
}
......@@ -37,10 +37,13 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.locks.LockSupport;
import com.alibaba.rcm.internal.AbstractResourceContainer;
import sun.misc.VM;
import sun.nio.ch.Interruptible;
import sun.reflect.CallerSensitive;
import sun.reflect.Reflection;
import sun.security.util.SecurityConstants;
import com.alibaba.tenant.TenantContainer;
import com.alibaba.tenant.TenantGlobals;
/**
......@@ -214,6 +217,11 @@ class Thread implements Runnable {
private volatile int threadStatus = 0;
/**
* The tenant container which creates this thread object
*/
TenantContainer inheritedTenantContainer;
/**
* The thread attached {@code ResourceContainer}
*/
......@@ -430,8 +438,12 @@ class Thread implements Runnable {
tid = nextThreadID();
/* com.alibaba.rcm API */
this.resourceContainer = parent.resourceContainer != null ?
parent.resourceContainer : AbstractResourceContainer.root();
this.resourceContainer = AbstractResourceContainer.root();
/* Set the tenant container */
if (VM.isBooted() && TenantGlobals.isTenantEnabled()) {
inheritedTenantContainer = TenantContainer.current();
}
}
/**
......@@ -775,6 +787,7 @@ class Thread implements Runnable {
inheritedAccessControlContext = null;
blocker = null;
uncaughtExceptionHandler = null;
inheritedTenantContainer = null;
}
/**
......
......@@ -35,6 +35,7 @@ import java.util.Set;
import javax.management.MBeanServerConnection;
import javax.management.ObjectName;
import com.alibaba.management.TenantContainerMXBean;
import com.alibaba.management.ElasticHeapMXBean;
import com.sun.management.HotSpotDiagnosticMXBean;
import com.sun.management.UnixOperatingSystemMXBean;
......@@ -276,6 +277,24 @@ enum PlatformComponent {
}
}),
/**
* Tenant Container.
*/
TENANT_CONTAINER(
"com.alibaba.management.TenantContainerMXBean",
"com.alibaba.management", "TenantContainer", defaultKeyProperties(),
true,
new MXBeanFetcher<TenantContainerMXBean>() {
public List<TenantContainerMXBean> getMXBeans() {
TenantContainerMXBean m = ManagementFactoryHelper.getTenantContainerMXBean();
if (null == m) {
return Collections.emptyList();
} else {
return Collections.singletonList(m);
}
}
}),
/**
* Flight Recorder.
*/
......
......@@ -39,7 +39,9 @@ import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import com.alibaba.management.TenantContainerMXBean;
import com.alibaba.management.ElasticHeapMXBean;
import com.alibaba.tenant.TenantContainerMXBeanImpl;
import com.alibaba.jvm.gc.ElasticHeapMXBeanImpl;
import sun.util.logging.LoggingSupport;
......@@ -70,6 +72,7 @@ public class ManagementFactoryHelper {
private static RuntimeImpl runtimeMBean = null;
private static CompilationImpl compileMBean = null;
private static OperatingSystemImpl osMBean = null;
private static TenantContainerMXBeanImpl tenantContainerMBean = null;
private static FlightRecorderMXBeanImpl flightRecorderMBean = null;
private static ElasticHeapMXBeanImpl elasticHeapMXBean = null;
......@@ -115,6 +118,13 @@ public class ManagementFactoryHelper {
return osMBean;
}
public static synchronized TenantContainerMXBean getTenantContainerMXBean() {
if (tenantContainerMBean == null) {
tenantContainerMBean = new TenantContainerMXBeanImpl();
}
return tenantContainerMBean;
}
public static synchronized FlightRecorderMXBean getFlightRecorderMXBean() {
if (flightRecorderMBean == null) {
flightRecorderMBean = new FlightRecorderMXBeanImpl();
......
......@@ -361,6 +361,12 @@ JVM_ElasticHeapGetSoftmxPercent(JNIEnv *env, jclass clazz);
JNIEXPORT jlong JNICALL
JVM_ElasticHeapGetTotalUncommittedBytes(JNIEnv *env, jclass clazz);
/*
* com.alibaba.tenant.TenantContainer
*/
JNIEXPORT void JNICALL
JVM_AttachToTenant(JNIEnv *env, jobject tenant);
/*
* java.lang.reflect.Array
*/
......
/*
* 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.
*/
#ifndef _JAVASOFT_TENANT_ENV_H
#define _JAVASOFT_TENANT_ENV_H
#include "jni.h"
// 0x00200000 represents tenant module and the last 10 represents version 1.0
#define TENANT_ENV_VERSION_1_0 0x00200010
/*
* Tenant Native Method Interface.
*/
struct TenantNativeInterface_;
struct TenantEnv_;
#ifdef __cplusplus
typedef TenantEnv_ TenantEnv;
#else
typedef const struct TenantNativeInterface_* TenantEnv;
#endif
/*
* We use inlined functions for C++ so that programmers can write:
* tenantEnv->GetTenantFlags(cls);
* in C++ rather than:
* (*tenantEnv)->GetTenantFlags(tenantEnv, cls);
* in C.
*/
struct TenantNativeInterface_ {
jint (JNICALL *GetTenantFlags)(TenantEnv *env, jclass cls);
};
struct TenantEnv_ {
const struct TenantNativeInterface_ *functions;
#ifdef __cplusplus
jint GetTenantFlags(jclass cls) {
return functions->GetTenantFlags(this, cls);
}
#endif
};
#endif // _JAVASOFT_TENANT_ENV_H_
#include "jni.h"
#include "jni_util.h"
#include "jvm.h"
#include "jmm.h"
#include "com_alibaba_tenant_NativeDispatcher.h"
#define TENANT "Lcom/alibaba/tenant/TenantContainer;"
#define THREAD "Ljava/lang/Thread;"
#define ARRAY_LENGTH(a) (sizeof(a)/sizeof(a[0]))
static const JmmInterface* jmm_interface = NULL;
static JNINativeMethod methods[] = {
{"attach", "(" TENANT ")V", (void *)&JVM_AttachToTenant},
};
JNIEXPORT void JNICALL
Java_com_alibaba_tenant_NativeDispatcher_registerNatives0(JNIEnv *env, jclass cls)
{
(*env)->RegisterNatives(env, cls, methods, ARRAY_LENGTH(methods));
}
JNIEXPORT void JNICALL
Java_com_alibaba_tenant_NativeDispatcher_getThreadsAllocatedMemory(JNIEnv *env,
jobject obj,
jlongArray ids,
jlongArray sizeArray)
{
if (NULL == jmm_interface) {
jmm_interface = (JmmInterface*) JVM_GetManagement(JMM_VERSION_1_0);
}
jmm_interface->GetThreadAllocatedMemory(env, ids, sizeArray);
}
/*
* 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 "tenantenv.h"
#include "jni_util.h"
#include "jvm.h"
#include "com_alibaba_tenant_TenantGlobals.h"
static TenantEnv*
getTenantEnv(JNIEnv *env)
{
jint rc = JNI_ERR;
JavaVM *jvm = NULL;
TenantEnv *tenantEnv = NULL;
(*env)->GetJavaVM(env, &jvm);
if(NULL != jvm) {
/* Get tenant environment */
rc = (*jvm)->GetEnv(jvm, (void **)&tenantEnv, TENANT_ENV_VERSION_1_0);
if (JNI_OK != rc) {
tenantEnv = NULL;
}
}
return tenantEnv;
}
JNIEXPORT jint JNICALL
Java_com_alibaba_tenant_TenantGlobals_getTenantFlags(JNIEnv *env, jclass cls)
{
jint rc = JNI_ERR;
TenantEnv* tenantEnv = getTenantEnv(env);
if(NULL != tenantEnv) {
rc = (*tenantEnv)->GetTenantFlags(tenantEnv, cls);
} else {
//throw exception
JNU_ThrowByName(env, "java/lang/InternalError", "Can not get tenant environment.");
}
return rc;
}
/*
* 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 jdk.testlibrary;
import java.lang.reflect.Method;
import java.util.Arrays;
import static jdk.testlibrary.Asserts.fail;
public class TestUtils {
/**
* Run all methods of {@code clazz} whose names start with {@code prefix}
* @param prefix
* @param clazz
* @param object
*/
public static void runWithPrefix(String prefix, Class clazz, Object object) {
if (prefix == null || clazz == null || object == null) {
throw new IllegalArgumentException("Bad arguments");
}
long totalTests = Arrays.stream(clazz.getDeclaredMethods())
.filter(m -> m.getName().startsWith(prefix))
.count();
long passed = 0;
long failed = 0;
for (Method method : clazz.getDeclaredMethods()) {
if (method.getName().startsWith(prefix)) {
method.setAccessible(true);
String name = clazz.getName() + "." + method.getName();
println("=== Begin test " + name + " ===");
try {
method.invoke(object);
++passed;
println("=== PASSED (" + passed + " passed, " + failed +" failed, "
+ totalTests + " total) ===");
} catch (Throwable e) {
e.printStackTrace();
++failed;
println("=== FAILED ( " + passed + " passed, " + failed +" failed"
+ totalTests + " total) ===");
}
}
}
if (failed != 0) {
fail("Total " + failed + "/" + totalTests + " testcases failed, class " + clazz.getName());
} else {
println("All " + totalTests + " testcases passed, class " + clazz.getName());
}
}
private static void println(String msg) {
System.err.println(msg);
System.out.println(msg);
}
}
/*
* 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 test.com.alibaba.tenant;
import com.alibaba.tenant.*;
import org.junit.Test;
import static org.junit.Assert.*;
import java.lang.management.ManagementFactory;
import java.lang.management.ThreadMXBean;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.IntStream;
/* @test
* @summary unit tests for com.alibaba.tenant.TenantContainer
* @library /lib/testlibrary
* @compile TestTenantContainer.java
* @run junit/othervm/timeout=300 -XX:+MultiTenant -XX:+UseG1GC -Xmx600m -Xms200m
* -Dcom.alibaba.tenant.test.prop=root test.com.alibaba.tenant.TestTenantContainer
*/
public class TestTenantContainer {
static private final int MAP_SIZE = 128;
static private final int MAP_ARRAY_LENGTH = 40;
private Map<Integer, String> populateMap(int size) {
Map<Integer, String> map = new HashMap<Integer, String>();
for (int i = 0; i < size; i += 1) {
String valStr = "value is [" + i + "]";
map.put(i, valStr);
}
return map;
}
@Test
public void testRunInRootTenant() throws TenantException {
TenantConfiguration tconfig = new TenantConfiguration();
final TenantContainer tenant = TenantContainer.create(tconfig);
tenant.run(() -> {
assertSame(TenantContainer.current(), tenant);
TenantContainer.primitiveRunInRoot(() -> {
//should be in root tenant.
assertNull(TenantContainer.current());
});
});
}
@Test
public void testTenantSystemProperty() {
String value = System.getProperty("com.alibaba.tenant.enableMultiTenant");
assertTrue(value != null && "true".equalsIgnoreCase(value));
}
@Test
public void testCurrent() throws TenantException {
// should be in the root tenant at the begging
assertNull(TenantContainer.current());
TenantConfiguration tconfig = new TenantConfiguration();
final TenantContainer tenant = TenantContainer.create(tconfig);
tenant.run(() -> {
// run in 'current' tenant
assertSame(TenantContainer.current(), tenant);
System.out.println("testCurrent: thread [" + Thread.currentThread().getName() + "] is running in tenant: "
+ TenantContainer.current().getTenantId());
});
// switch back to root tenant.
assertNull(TenantContainer.current());
tenant.destroy();
}
@Test
public void testCurrentWithGC() throws TenantException {
// should be in the root tenant at the begging
assertNull(TenantContainer.current());
TenantConfiguration tconfig = new TenantConfiguration();
final TenantContainer tenant = TenantContainer.create(tconfig);
tenant.run(() -> {
int arrayIndex = 0;
Object[] array = new Object[MAP_ARRAY_LENGTH];
while (arrayIndex < MAP_ARRAY_LENGTH * 20) {
// run in 'current' tenant
assertSame(TenantContainer.current(), tenant);
Map<Integer, String> map = populateMap(MAP_SIZE);
array[arrayIndex % MAP_ARRAY_LENGTH] = map;
arrayIndex++;
Thread.yield();
System.gc();
}
System.out.println("testCurrent: thread [" + Thread.currentThread().getName() + "] is running in tenant: "
+ TenantContainer.current().getTenantId());
});
// switch back to root tenant.
assertNull(TenantContainer.current());
tenant.destroy();
}
@Test
public void testGetTenantID() throws TenantException {
TenantConfiguration tconfig = new TenantConfiguration();
final TenantContainer tenant = TenantContainer.create(tconfig);
tenant.run(() -> {
assertEquals(TenantContainer.current().getTenantId(), tenant.getTenantId());
System.out.println("testGetTenantID: thread [" + Thread.currentThread().getName()
+ "] is running in tenant: " + TenantContainer.current().getTenantId());
});
tenant.destroy();
}
@Test
public void testGetState() throws TenantException {
TenantConfiguration tconfig = new TenantConfiguration();
final TenantContainer tenant = TenantContainer.create(tconfig);
assertTrue(TenantState.STARTING == tenant.getState());
tenant.run(() -> {
assertTrue(TenantState.RUNNING == tenant.getState());
System.out.println("testGetState: thread [" + Thread.currentThread().getName() + "] is running in tenant: "
+ TenantContainer.current().getTenantId());
});
tenant.destroy();
}
@Test
public void testRun() throws TenantException {
TenantConfiguration tconfig = new TenantConfiguration();
final TenantContainer tenant = TenantContainer.create(tconfig);
final TenantContainer tenant2 = TenantContainer.create(tconfig);
try {
tenant.run(() -> {
// it is not allowed to run into tenant 2 from tenant
try {
tenant2.run(() -> {
// should not reach here.
fail();
});
} catch (TenantException e) {
// Expected
}
// it is allowed to run into same tenant
try {
tenant.run(() -> {
assertEquals(TenantContainer.current().getTenantId(), tenant.getTenantId());
System.out.println("testRun: thread [" + Thread.currentThread().getName()
+ "] is running in tenant: " + TenantContainer.current().getTenantId());
});
} catch (TenantException e) {
fail();
}
});
} catch (TenantException e) {
fail();
}
tenant.destroy();
tenant2.destroy();
}
@Test
public void testGetAttachedThreads() throws TenantException {
TenantConfiguration tconfig = new TenantConfiguration();
final TenantContainer tenant = TenantContainer.create(tconfig);
tenant.run(() -> {
Thread[] threads = tenant.getAttachedThreads();
assertEquals(threads.length, 1);
assertEquals(threads[0].getId(), Thread.currentThread().getId());
System.out.println("testGetAttachedThreads: thread [" + Thread.currentThread().getName()
+ "] is running in tenant: " + TenantContainer.current().getTenantId());
});
tenant.destroy();
}
@Test
public void testTenantInheritance() throws TenantException {
TenantConfiguration tconfig = new TenantConfiguration();
final TenantContainer tenant = TenantContainer.create(tconfig);
tenant.run(() -> {
assertSame(TenantContainer.current(), tenant);
Thread thread = new Thread(() -> {
TenantContainer tc = TenantContainer.current();
assertSame(tc, tenant);
Thread[] threads = tc.getAttachedThreads();
assertEquals(threads.length, 2);
assertTrue(Thread.currentThread().getId() == threads[0].getId() ||
Thread.currentThread().getId() == threads[1].getId());
System.out.println("testTenantInheritance: thread [" + Thread.currentThread().getName()
+ "] is running in tenant: " + TenantContainer.current().getTenantId());
});
thread.start();
try {
thread.join();
} catch (Exception e) {
e.printStackTrace();
fail();
}
Thread[] threads = tenant.getAttachedThreads();
assertEquals(threads.length, 1);
assertEquals(threads[0].getId(), Thread.currentThread().getId());
});
tenant.destroy();
}
@Test
public void testTenantGetAllocatedMemory() throws TenantException, InterruptedException {
TenantConfiguration tconfig = new TenantConfiguration();
final TenantContainer tenant = TenantContainer.create(tconfig);
CountDownLatch p1 = new CountDownLatch(1);
CountDownLatch p2 = new CountDownLatch(1);
CountDownLatch p3 = new CountDownLatch(1);
CountDownLatch p4 = new CountDownLatch(1);
CountDownLatch startTest = new CountDownLatch(2);
CountDownLatch endTest = new CountDownLatch(1);
long allocatedMem0 = 0;
long allocatedMem1 = 0;
long allocatedMem2 = 0;
long allocatedMem3 = 0;
assertTrue(0 == tenant.getAllocatedMemory());
tenant.run(() -> {
Thread thread1 = new Thread(() -> {
assertTrue(TenantContainer.current() == tenant);
System.out.println("thread1 started");
try {
byte[] byteArray0 = new byte[10240];
startTest.countDown();
p1.await();
byte[] byteArray1 = new byte[10240];
endTest.await();
} catch (InterruptedException ee) {
ee.printStackTrace();
fail();
} finally {
System.out.println("thread1 ended");
}
});
thread1.start();
});
Thread thread2 = new Thread(() -> {
System.out.println("thread2 started");
try {
startTest.countDown();
p2.await();
tenant.run(()->{
byte[] byteArray0 = new byte[10240];
});
p3.await();
byte[] byteArray1 = new byte[10240];
tenant.run(()->{
byte[] byteArray0 = new byte[10240];
});
byte[] byteArray2 = new byte[10240];
endTest.await();
} catch (InterruptedException | TenantException ee) {
ee.printStackTrace();
fail();
} finally {
System.out.println("thread2 ended");
}
});
thread2.start();
startTest.await();
allocatedMem0 = tenant.getAllocatedMemory();
System.out.println("allocatedMem0 = " + allocatedMem0);
assertTrue(allocatedMem0 >= 10240);
p1.countDown();
Thread.sleep(1000);
allocatedMem1 = tenant.getAllocatedMemory();
System.out.println("allocatedMem1 = " + allocatedMem1);
assertTrue((allocatedMem1 - allocatedMem0) >= 10240 && (allocatedMem1 - allocatedMem0) < 11240);
p2.countDown();
Thread.sleep(1000);
allocatedMem2 = tenant.getAllocatedMemory();
System.out.println("allocatedMem2 = " + allocatedMem2);
assertTrue((allocatedMem2 - allocatedMem1) >= 10240 && (allocatedMem2 - allocatedMem1) < 11240);
p3.countDown();
Thread.sleep(1000);
allocatedMem3 = tenant.getAllocatedMemory();
System.out.println("allocatedMem3 = " + allocatedMem3);
assertTrue((allocatedMem3 - allocatedMem2) >= 10240 && (allocatedMem3 - allocatedMem2) < 11240);
p4.countDown();
Thread.sleep(1000);
tenant.destroy();
endTest.countDown();
System.out.println("testTenantGetAllocatedMemory passed!");
}
public static void main(String[] args) throws Exception {
TestTenantContainer test = new TestTenantContainer();
test.testCurrent();
test.testGetTenantID();
test.testGetState();
test.testGetAttachedThreads();
test.testRun();
test.testCurrentWithGC();
test.testTenantInheritance();
test.testRunInRootTenant();
test.testTenantGetAllocatedMemory();
}
}
/*
* 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.tenant;
import com.alibaba.rcm.Constraint;
import com.alibaba.rcm.ResourceContainer;
import com.alibaba.rcm.ResourceContainerFactory;
import com.alibaba.rcm.internal.AbstractResourceContainer;
import java.util.Collections;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static com.alibaba.rcm.ResourceType.*;
import static jdk.testlibrary.Asserts.*;
/*
* @test
* @library /lib/testlibrary
* @summary test RCM API based TenantContainerFactory
* @run main/othervm/bootclasspath -XX:+MultiTenant com.alibaba.tenant.TestTenantContainerFactory
*/
public class TestTenantContainerFactory {
private void testSingleton() {
ResourceContainerFactory factory = TenantContainerFactory.instance();
assertTrue(factory == TenantContainerFactory.instance());
ResourceContainerFactory factory2 = TenantContainerFactory.instance();
assertSame(factory, factory2);
}
private void testCreation() {
try {
ResourceContainer container = TenantContainerFactory.instance()
.createContainer(Collections.emptySet());
assertNotNull(container);
assertTrue(container instanceof TenantResourceContainer);
assertNull(TenantContainer.current());
assertNotNull(TenantResourceContainer.root());
} catch (Throwable t) {
fail();
}
}
private void testCreationWithHeapLimit() {
try {
Iterable<Constraint> constraints = Stream.of(HEAP_RETAINED.newConstraint(32 * 1024 * 1024))
.collect(Collectors.toSet());
ResourceContainer container = TenantContainerFactory.instance()
.createContainer(constraints);
assertNotNull(container);
assertTrue(container instanceof TenantResourceContainer);
assertNull(TenantContainer.current());
assertNotNull(TenantResourceContainer.root());
} catch (Throwable t) {
fail();
}
}
private void testDestroy() {
ResourceContainer container = TenantContainerFactory.instance()
.createContainer(null);
try {
container.destroy();
fail("Should throw UnsupportedException");
} catch (UnsupportedOperationException e) {
// expected
}
try {
TenantContainer tenant = TenantContainerFactory.tenantContainerOf(container);
tenant.destroy();
} catch (Throwable t) {
fail();
}
}
private void testImplicitTenantContainer() {
try {
Iterable<Constraint> constraints = Stream.of(CPU_PERCENT.newConstraint(10))
.collect(Collectors.toList());
ResourceContainer rc = TenantContainerFactory.instance().createContainer(constraints);
TenantContainer tenant = TenantContainerFactory.tenantContainerOf(rc);
assertNotNull(tenant);
assertNull(TenantContainer.current());
assertSame(AbstractResourceContainer.root(), AbstractResourceContainer.current());
tenant.run(()->{
System.out.println("Hello");
assertSame(TenantContainer.current(), tenant);
assertSame(AbstractResourceContainer.current(), rc);
});
tenant.destroy();
} catch (Throwable e) {
fail();
}
}
public static void main(String[] args) {
TestTenantContainerFactory test = new TestTenantContainerFactory();
test.testSingleton();
test.testCreation();
test.testDestroy();
test.testImplicitTenantContainer();
if (TenantGlobals.isHeapThrottlingEnabled()) {
test.testCreationWithHeapLimit();
}
}
}
/*
* 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 test.com.alibaba.tenant;
import org.junit.Test;
import com.alibaba.tenant.TenantConfiguration;
import com.alibaba.tenant.TenantContainer;
import com.alibaba.tenant.TenantGlobals;
import static org.junit.Assert.*;
/* @test
* @summary unit tests for com.alibaba.tenant.TenantGlobals
* @library /lib/testlibrary
* @compile TestTenantGlobals.java
* @run junit/othervm test.com.alibaba.tenant.TestTenantGlobals
*/
public class TestTenantGlobals {
@Test
public void testIfTenantIsDisabled() {
assertFalse(TenantGlobals.isTenantEnabled());
String value = System.getProperty("com.alibaba.tenant.enableMultiTenant");
boolean bTenantIsEnabled = false;
if(value != null && "true".equalsIgnoreCase(value)) {
bTenantIsEnabled = true;
}
assertFalse(bTenantIsEnabled);
TenantConfiguration tconfig = new TenantConfiguration();
try {
TenantContainer.create(tconfig);
fail(); // should not reach here.
} catch (UnsupportedOperationException exception) {
// Expected
System.out.println("Can not create tenant without -XX:+MultiTenant enabled.");
exception.printStackTrace();
}
try {
TenantContainer.create(tconfig);
fail(); // should not reach here.
} catch (UnsupportedOperationException exception) {
// Expected
System.out.println("Can not create tenant without -XX:+MultiTenant enabled.");
exception.printStackTrace();
}
try {
TenantContainer.current();
fail(); // should not reach here.
} catch (UnsupportedOperationException exception) {
// Expected
System.out.println("Can not get current tenant without -XX:+MultiTenant enabled.");
exception.printStackTrace();
}
}
public static void main(String[] args) {
TestTenantGlobals test = new TestTenantGlobals();
test.testIfTenantIsDisabled();
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册