Skip to content

Commit

Permalink
Add a VertxOptions 'disableTCCL' that configures whether Vertx will s…
Browse files Browse the repository at this point in the history
…et the thread context classloader when running actions on a Vert.x context. This options is set by default to the legacy 'vertx.disableTCCL' system property.

The warn logged when the TCCL is disabled and the current thread classloader is not the system classloader is removed.

fixes #4035
fixes #4036
  • Loading branch information
vietj committed Jul 27, 2021
1 parent d07329c commit 417919b
Show file tree
Hide file tree
Showing 11 changed files with 91 additions and 19 deletions.
6 changes: 6 additions & 0 deletions src/main/generated/io/vertx/core/VertxOptionsConverter.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ static void fromJson(Iterable<java.util.Map.Entry<String, Object>> json, VertxOp
obj.setBlockedThreadCheckIntervalUnit(java.util.concurrent.TimeUnit.valueOf((String)member.getValue()));
}
break;
case "disableTCCL":
if (member.getValue() instanceof Boolean) {
obj.setDisableTCCL((Boolean)member.getValue());
}
break;
case "eventBusOptions":
if (member.getValue() instanceof JsonObject) {
obj.setEventBusOptions(new io.vertx.core.eventbus.EventBusOptions((io.vertx.core.json.JsonObject)member.getValue()));
Expand Down Expand Up @@ -132,6 +137,7 @@ static void toJson(VertxOptions obj, java.util.Map<String, Object> json) {
if (obj.getBlockedThreadCheckIntervalUnit() != null) {
json.put("blockedThreadCheckIntervalUnit", obj.getBlockedThreadCheckIntervalUnit().name());
}
json.put("disableTCCL", obj.getDisableTCCL());
if (obj.getEventBusOptions() != null) {
json.put("eventBusOptions", obj.getEventBusOptions().toJson());
}
Expand Down
33 changes: 33 additions & 0 deletions src/main/java/io/vertx/core/VertxOptions.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@
@DataObject(generateConverter = true, publicConverter = false)
public class VertxOptions {

private static final String DISABLE_TCCL_PROP_NAME = "vertx.disableTCCL";

/**
* The default number of event loop threads to be used = 2 * number of cores on the machine
*/
Expand Down Expand Up @@ -109,6 +111,8 @@ public class VertxOptions {
*/
public static final TimeUnit DEFAULT_WARNING_EXCEPTION_TIME_UNIT = TimeUnit.NANOSECONDS;

public static final boolean DEFAULT_DISABLE_TCCL = Boolean.getBoolean(DISABLE_TCCL_PROP_NAME);

private int eventLoopPoolSize = DEFAULT_EVENT_LOOP_POOL_SIZE;
private int workerPoolSize = DEFAULT_WORKER_POOL_SIZE;
private int internalBlockingPoolSize = DEFAULT_INTERNAL_BLOCKING_POOL_SIZE;
Expand All @@ -130,6 +134,7 @@ public class VertxOptions {
private TimeUnit maxWorkerExecuteTimeUnit = DEFAULT_MAX_WORKER_EXECUTE_TIME_UNIT;
private TimeUnit warningExceptionTimeUnit = DEFAULT_WARNING_EXCEPTION_TIME_UNIT;
private TimeUnit blockedThreadCheckIntervalUnit = DEFAULT_BLOCKED_THREAD_CHECK_INTERVAL_UNIT;
private boolean disableTCCL = DEFAULT_DISABLE_TCCL;

/**
* Default constructor
Expand Down Expand Up @@ -163,6 +168,7 @@ public VertxOptions(VertxOptions other) {
this.warningExceptionTimeUnit = other.warningExceptionTimeUnit;
this.blockedThreadCheckIntervalUnit = other.blockedThreadCheckIntervalUnit;
this.tracingOptions = other.tracingOptions != null ? other.tracingOptions.copy() : null;
this.disableTCCL = other.disableTCCL;
}

/**
Expand Down Expand Up @@ -643,6 +649,32 @@ public VertxOptions setTracingOptions(TracingOptions tracingOptions) {
return this;
}

/**
* @return whether Vert.x sets the {@link Context} classloader as the thread context classloader on actions executed on that {@link Context}
*/
public boolean getDisableTCCL() {
return disableTCCL;
}

/**
* Configures whether Vert.x sets the {@link Context} classloader as the thread context classloader on actions executed on that {@link Context}.
*
* When a {@link Context} is created the current thread classloader is captured and associated with this classloader.
*
* Likewise when a Verticle is created, the Verticle's {@link Context} classloader is set to the current thread classloader
* unless this classloader is overriden by {@link DeploymentOptions#getClassLoader()}.
*
* This setting overrides the (legacy) system property {@code vertx.disableTCCL} and provides control at the
* Vertx instance level.
*
* @param disableTCCL {@code true} to disable thread context classloader update by Vertx
* @return a reference to this, so the API can be used fluently
*/
public VertxOptions setDisableTCCL(boolean disableTCCL) {
this.disableTCCL = disableTCCL;
return this;
}

public JsonObject toJson() {
JsonObject json = new JsonObject();
VertxOptionsConverter.toJson(this, json);
Expand Down Expand Up @@ -673,6 +705,7 @@ public String toString() {
", eventbus=" + eventBusOptions.toJson() +
", warningExceptionTimeUnit=" + warningExceptionTimeUnit +
", warningExceptionTime=" + warningExceptionTime +
", disableTCCL=" + disableTCCL +
'}';
}
}
12 changes: 8 additions & 4 deletions src/main/java/io/vertx/core/impl/AbstractContext.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@

import java.util.List;

import static io.vertx.core.impl.VertxThread.DISABLE_TCCL;

/**
* A context implementation that does not hold any specific state.
*
Expand All @@ -29,6 +27,12 @@
*/
abstract class AbstractContext implements ContextInternal {

final boolean disableTCCL;

public AbstractContext(boolean disableTCCL) {
this.disableTCCL = disableTCCL;
}

@Override
public abstract boolean isEventLoopContext();

Expand Down Expand Up @@ -63,15 +67,15 @@ public final ContextInternal beginDispatch() {
ContextInternal prev;
VertxThread th = (VertxThread) Thread.currentThread();
prev = th.beginEmission(this);
if (!DISABLE_TCCL) {
if (!disableTCCL) {
th.setContextClassLoader(classLoader());
}
return prev;
}

public final void endDispatch(ContextInternal previous) {
VertxThread th = (VertxThread) Thread.currentThread();
if (!DISABLE_TCCL) {
if (!disableTCCL) {
th.setContextClassLoader(previous != null ? previous.classLoader() : null);
}
th.endEmission(previous);
Expand Down
7 changes: 3 additions & 4 deletions src/main/java/io/vertx/core/impl/ContextImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,9 @@ static void executeIsolated(Handler<Void> task) {
WorkerPool workerPool,
Deployment deployment,
CloseFuture closeFuture,
ClassLoader tccl) {
if (VertxThread.DISABLE_TCCL && tccl != ClassLoader.getSystemClassLoader()) {
log.warn("You have disabled TCCL checks but you have a custom TCCL to set.");
}
ClassLoader tccl,
boolean disableTCCL) {
super(disableTCCL);
this.deployment = deployment;
this.config = deployment != null ? deployment.config() : new JsonObject();
this.eventLoop = eventLoop;
Expand Down
1 change: 1 addition & 0 deletions src/main/java/io/vertx/core/impl/DuplicatedContext.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ class DuplicatedContext extends AbstractContext {
private ConcurrentMap<Object, Object> localData;

DuplicatedContext(ContextImpl delegate) {
super(delegate.disableTCCL);
this.delegate = delegate;
}

Expand Down
5 changes: 3 additions & 2 deletions src/main/java/io/vertx/core/impl/EventLoopContext.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,9 @@ public class EventLoopContext extends ContextImpl {
WorkerPool workerPool,
Deployment deployment,
CloseFuture closeFuture,
ClassLoader tccl) {
super(vertx, eventLoop, internalBlockingPool, workerPool, deployment, closeFuture, tccl);
ClassLoader tccl,
boolean disableTCCL) {
super(vertx, eventLoop, internalBlockingPool, workerPool, deployment, closeFuture, tccl, disableTCCL);
}

@Override
Expand Down
8 changes: 5 additions & 3 deletions src/main/java/io/vertx/core/impl/VertxImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ public class VertxImpl implements VertxInternal, MetricsProvider {
private final Transport transport;
private final VertxTracer tracer;
private final ThreadLocal<WeakReference<AbstractContext>> stickyContext = new ThreadLocal<>();
private final boolean disableTCCL;

VertxImpl(VertxOptions options, ClusterManager clusterManager, NodeSelector nodeSelector, VertxMetrics metrics,
VertxTracer<?, ?> tracer, Transport transport, FileResolver fileResolver, VertxThreadFactory threadFactory,
Expand Down Expand Up @@ -165,6 +166,7 @@ public class VertxImpl implements VertxInternal, MetricsProvider {
defaultWorkerPoolSize = options.getWorkerPoolSize();
maxWorkerExecTime = options.getMaxWorkerExecuteTime();
maxWorkerExecTimeUnit = options.getMaxWorkerExecuteTimeUnit();
disableTCCL = options.getDisableTCCL();

this.executorServiceFactory = executorServiceFactory;
this.threadFactory = threadFactory;
Expand Down Expand Up @@ -471,12 +473,12 @@ public boolean cancelTimer(long id) {

@Override
public EventLoopContext createEventLoopContext(Deployment deployment, CloseFuture closeFuture, WorkerPool workerPool, ClassLoader tccl) {
return new EventLoopContext(this, eventLoopGroup.next(), internalWorkerPool, workerPool != null ? workerPool : this.workerPool, deployment, closeFuture, tccl);
return new EventLoopContext(this, eventLoopGroup.next(), internalWorkerPool, workerPool != null ? workerPool : this.workerPool, deployment, closeFuture, tccl, disableTCCL);
}

@Override
public EventLoopContext createEventLoopContext(EventLoop eventLoop, WorkerPool workerPool, ClassLoader tccl) {
return new EventLoopContext(this, eventLoop, internalWorkerPool, workerPool != null ? workerPool : this.workerPool, null, closeFuture, tccl);
return new EventLoopContext(this, eventLoop, internalWorkerPool, workerPool != null ? workerPool : this.workerPool, null, closeFuture, tccl, disableTCCL);
}

@Override
Expand All @@ -486,7 +488,7 @@ public EventLoopContext createEventLoopContext() {

@Override
public WorkerContext createWorkerContext(Deployment deployment, CloseFuture closeFuture, WorkerPool workerPool, ClassLoader tccl) {
return new WorkerContext(this, internalWorkerPool, workerPool != null ? workerPool : this.workerPool, deployment, closeFuture, tccl);
return new WorkerContext(this, internalWorkerPool, workerPool != null ? workerPool : this.workerPool, deployment, closeFuture, tccl, disableTCCL);
}

@Override
Expand Down
3 changes: 0 additions & 3 deletions src/main/java/io/vertx/core/impl/VertxThread.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,6 @@
*/
public class VertxThread extends FastThreadLocalThread implements BlockedThreadChecker.Task {

static final String DISABLE_TCCL_PROP_NAME = "vertx.disableTCCL";
static final boolean DISABLE_TCCL = Boolean.getBoolean(DISABLE_TCCL_PROP_NAME);

private final boolean worker;
private final long maxExecTime;
private final TimeUnit maxExecTimeUnit;
Expand Down
5 changes: 3 additions & 2 deletions src/main/java/io/vertx/core/impl/WorkerContext.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,9 @@ public class WorkerContext extends ContextImpl {
WorkerPool workerPool,
Deployment deployment,
CloseFuture closeFuture,
ClassLoader tccl) {
super(vertx, vertx.getEventLoopGroup().next(), internalBlockingPool, workerPool, deployment, closeFuture, tccl);
ClassLoader tccl,
boolean disableTCCL) {
super(vertx, vertx.getEventLoopGroup().next(), internalBlockingPool, workerPool, deployment, closeFuture, tccl, disableTCCL);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public static BenchmarkContext create(Vertx vertx) {
}

public BenchmarkContext(VertxInternal vertx, WorkerPool internalBlockingPool, WorkerPool workerPool, ClassLoader tccl) {
super(vertx, vertx.getEventLoopGroup().next(), internalBlockingPool, workerPool, null, null, tccl);
super(vertx, vertx.getEventLoopGroup().next(), internalBlockingPool, workerPool, null, null, tccl, false);
}

@Override
Expand Down
28 changes: 28 additions & 0 deletions src/test/java/io/vertx/core/VertxTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
import org.openjdk.jmh.runner.options.OptionsBuilder;

import java.lang.ref.WeakReference;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
Expand Down Expand Up @@ -290,4 +292,30 @@ public void testCloseVertxShouldWaitConcurrentCloseHook() throws Exception {
ref.get().complete();
assertWaitUntil(closed::get);
}

@Test
public void testEnableTCCL() {
testTCCL(false);
}

@Test
public void testDisableTCCL() {
testTCCL(true);
}

private void testTCCL(boolean disable) {
VertxOptions options = new VertxOptions().setDisableTCCL(disable);
Vertx vertx = Vertx.vertx(options);
ClassLoader orig = Thread.currentThread().getContextClassLoader();
ClassLoader cl = new URLClassLoader(new URL[0], orig);
Thread.currentThread().setContextClassLoader(cl);
Context ctx = vertx.getOrCreateContext();
Thread.currentThread().setContextClassLoader(orig);
ctx.runOnContext(v -> {
ClassLoader expected = disable ? orig : cl;
assertSame(expected, Thread.currentThread().getContextClassLoader());
testComplete();
});
await();
}
}

0 comments on commit 417919b

Please sign in to comment.