Skip to content

Commit

Permalink
Support FastThreadLocal mode. Resolves #94
Browse files Browse the repository at this point in the history
1. Support FastThreadLocal mode when run with netty by config
   -Dttl.fastthreadlocal.enable=true, default is false
2. Caution: when FastThreadLocal mode is enabled,
   TransmittableThreadLocal can NEVER be inheritable.
3. Disable the tests which depends on the ability of InheritableThreadLocal
   when FastThreadLocal is enabled.
  • Loading branch information
driventokill committed Aug 8, 2018
1 parent 3960572 commit 50a2b6d
Show file tree
Hide file tree
Showing 9 changed files with 186 additions and 32 deletions.
7 changes: 7 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,13 @@
<version>3.23.1-GA</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.0.20.Final</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<!-- Testing frameworks and related dependencies -->
<dependency>
<groupId>junit</groupId>
Expand Down
7 changes: 7 additions & 0 deletions pom4ide.xml
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,13 @@
<version>3.23.1-GA</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.0.20.Final</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<!-- Testing frameworks and related dependencies -->
<dependency>
<groupId>junit</groupId>
Expand Down
28 changes: 25 additions & 3 deletions src/main/java/com/alibaba/ttl/TransmittableThreadLocal.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package com.alibaba.ttl;

import com.alibaba.ttl.internal.TtlValue;
import com.alibaba.ttl.internal.TtlValueFactory;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
Expand All @@ -14,6 +17,10 @@
* <p>
* Note: {@link TransmittableThreadLocal} extends {@link java.lang.InheritableThreadLocal},
* so {@link TransmittableThreadLocal} first is a {@link java.lang.InheritableThreadLocal}.
* <p>
* If you have netty in the runtime and {@link io.netty.util.internal.FastThreadLocal} is supported. You can store
* {@link TransmittableThreadLocal} in {@link io.netty.util.internal.FastThreadLocal} to give up inheritance for better performance.
* </p>
*
* @author Jerry Lee (oldratlee at gmail dot com)
* @see TtlRunnable
Expand All @@ -23,6 +30,9 @@
public class TransmittableThreadLocal<T> extends InheritableThreadLocal<T> {
private static final Logger logger = Logger.getLogger(TransmittableThreadLocal.class.getName());

// Hold the real value to support special optimization for thread local
private final TtlValue<T> ttlValue = TtlValueFactory.create();

/**
* Computes the value for this transmittable thread-local variable
* as a function of the source thread's value at the time the task
Expand Down Expand Up @@ -66,7 +76,7 @@ protected void afterExecute() {

@Override
public final T get() {
T value = super.get();
T value = ttlValue != null ? ttlValue.get() : super.get();
if (null != value) {
addValue();
}
Expand All @@ -75,18 +85,30 @@ public final T get() {

@Override
public final void set(T value) {
super.set(value);
setTtlValue(value);
if (null == value) { // may set null to remove value
removeValue();
} else {
addValue();
}
}

private void setTtlValue(T value) {
if (ttlValue != null) {
ttlValue.set(value);
} else {
super.set(value);
}
}

@Override
public final void remove() {
removeValue();
super.remove();
if (ttlValue != null) {
ttlValue.remove();
} else {
super.remove();
}
}

private void superRemove() {
Expand Down
29 changes: 29 additions & 0 deletions src/main/java/com/alibaba/ttl/internal/FastThreadLocalValue.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.alibaba.ttl.internal;

import io.netty.util.internal.FastThreadLocal;

/**
* {@link FastThreadLocal} implementation for {@link com.alibaba.ttl.TransmittableThreadLocal} value holder
*
* @author Yang Fang (snoop dot fy at gmail dot com)
* @since 2.7.0
*/
public class FastThreadLocalValue<T> implements TtlValue<T> {

private FastThreadLocal<T> holder = new FastThreadLocal<T>();

@Override
public T get() {
return holder.get();
}

@Override
public void set(T t) {
holder.set(t);
}

@Override
public void remove() {
holder.remove();
}
}
17 changes: 17 additions & 0 deletions src/main/java/com/alibaba/ttl/internal/TtlValue.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.alibaba.ttl.internal;

/**
* Hold {@link com.alibaba.ttl.TransmittableThreadLocal} value, can be implemented in varied ways depending on the runtime.
*
* @author Yang Fang (snoop dot fy at gmail dot com)
* @since 2.7.0
*/
public interface TtlValue<T> {

T get();

void set(T t);

void remove();

}
49 changes: 49 additions & 0 deletions src/main/java/com/alibaba/ttl/internal/TtlValueFactory.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package com.alibaba.ttl.internal;

/**
* {@link com.alibaba.ttl.TransmittableThreadLocal} value holder factory.
*
* <p>
* If there is netty in the runtime and
* {@link io.netty.util.internal.FastThreadLocal} is supported. You can just set
*
* <pre>-Dttl.fastthreadlocal.enable=true</pre>
*
* to enable FastThreadLocal mode for better performance.
* </p>
*
* <p>
* Caution: If FastThreadLocal mode is enabled, {@link com.alibaba.ttl.TransmittableThreadLocal}
* will NEVER be inheritable!
* </p>
*
* @author Yang Fang (snoop dot fy at gmail dot com)
* @since 2.7.0
*/
public class TtlValueFactory {

private static final String FAST_THREAD_LOCAL_ENABLE = "ttl.fastthreadlocal.enable";

private static final boolean IS_FAST_THREAD_LOCAL_SUPPORT = isClassPresent("io.netty.util.internal.FastThreadLocal");

public static <T> TtlValue<T> create() {
if (isFastThreadLocalEnabled() && IS_FAST_THREAD_LOCAL_SUPPORT) {
return new FastThreadLocalValue<T>();
} else {
return null;
}
}

public static boolean isFastThreadLocalEnabled() {
return Boolean.parseBoolean(System.getProperty(FAST_THREAD_LOCAL_ENABLE, "false"));
}

private static boolean isClassPresent(String className) {
try {
Class.forName(className);
return true;
} catch (ClassNotFoundException e) {
return false;
}
}
}
13 changes: 8 additions & 5 deletions src/test/java/com/alibaba/ttl/TtlCallableTest.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.alibaba.ttl

import com.alibaba.*
import com.alibaba.ttl.internal.TtlValueFactory
import com.alibaba.ttl.testmodel.Call
import org.hamcrest.CoreMatchers.containsString
import org.hamcrest.CoreMatchers.instanceOf
Expand Down Expand Up @@ -33,12 +34,14 @@ class TtlCallableTest {
val ret = ttlCallable.call()
assertEquals("ok", ret)

// inheritable is not supported by FastThreadLocal mode
if (!TtlValueFactory.isFastThreadLocalEnabled()) {
// child Inheritable
assertChildTtlValues("1", call.copied)

// child Inheritable
assertChildTtlValues("1", call.copied)

// child do not effect parent
assertParentTtlValues(copyTtlValues(ttlInstances))
// child do not effect parent
assertParentTtlValues(copyTtlValues(ttlInstances))
}
}

@Test
Expand Down
46 changes: 31 additions & 15 deletions src/test/java/com/alibaba/ttl/TtlRunnableTest.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.alibaba.ttl

import com.alibaba.*
import com.alibaba.ttl.internal.TtlValueFactory
import com.alibaba.ttl.testmodel.*
import org.hamcrest.CoreMatchers.containsString
import org.hamcrest.CoreMatchers.instanceOf
Expand Down Expand Up @@ -33,11 +34,14 @@ class TtlRunnableTest {
ttlRunnable.run()


// child Inheritable
assertChildTtlValues("1", task.copied)
// inheritable is not supported by FastThreadLocal mode
if (!TtlValueFactory.isFastThreadLocalEnabled()) {
// child Inheritable
assertChildTtlValues("1", task.copied)

// child do not effect parent
assertParentTtlValues(copyTtlValues(ttlInstances))
// child do not effect parent
assertParentTtlValues(copyTtlValues(ttlInstances))
}
}

@Test
Expand All @@ -55,8 +59,11 @@ class TtlRunnableTest {
thread1.join()


// child Inheritable
assertChildTtlValues("1", task.copied)
// inheritable is not supported by FastThreadLocal mode
if (!TtlValueFactory.isFastThreadLocalEnabled()) {
// child Inheritable
assertChildTtlValues("1", task.copied)
}

// child do not effect parent
assertParentTtlValues(copyTtlValues(ttlInstances))
Expand All @@ -77,8 +84,11 @@ class TtlRunnableTest {
submit.get()


// child Inheritable
assertChildTtlValues("1", task.copied)
// inheritable is not supported by FastThreadLocal mode
if (!TtlValueFactory.isFastThreadLocalEnabled()) {
// child Inheritable
assertChildTtlValues("1", task.copied)
}

// child do not effect parent
assertParentTtlValues(copyTtlValues(ttlInstances))
Expand All @@ -102,8 +112,11 @@ class TtlRunnableTest {
submit.get()


// child Inheritable
assertChildTtlValues("1", task.copied)
// inheritable is not supported by FastThreadLocal mode
if (!TtlValueFactory.isFastThreadLocalEnabled()) {
// child Inheritable
assertChildTtlValues("1", task.copied)
}

// child do not effect parent
assertParentTtlValues(copyTtlValues(ttlInstances))
Expand Down Expand Up @@ -168,11 +181,14 @@ class TtlRunnableTest {
val submit = executorService.submit(ttlRunnable)
submit.get()

// child Inheritable
assertEquals(3, task.copied.size.toLong())
assertEquals(FooPojo(PARENT_CREATE_UNMODIFIED_IN_CHILD, 1), task.copied[PARENT_CREATE_UNMODIFIED_IN_CHILD])
assertEquals(FooPojo(PARENT_CREATE_MODIFIED_IN_CHILD + "1", 2), task.copied[PARENT_CREATE_MODIFIED_IN_CHILD])
assertEquals(FooPojo(CHILD_CREATE + 1, 3), task.copied[CHILD_CREATE + 1])
// inheritable is not supported by FastThreadLocal mode
if (!TtlValueFactory.isFastThreadLocalEnabled()) {
// child Inheritable
assertEquals(3, task.copied.size.toLong())
assertEquals(FooPojo(PARENT_CREATE_UNMODIFIED_IN_CHILD, 1), task.copied[PARENT_CREATE_UNMODIFIED_IN_CHILD])
assertEquals(FooPojo(PARENT_CREATE_MODIFIED_IN_CHILD + "1", 2), task.copied[PARENT_CREATE_MODIFIED_IN_CHILD])
assertEquals(FooPojo(CHILD_CREATE + 1, 3), task.copied[CHILD_CREATE + 1])
}

// child do not effect parent
val copied = copyTtlValues(ttlInstances)
Expand Down
22 changes: 13 additions & 9 deletions src/test/java/com/alibaba/ttl/reported_bugs/Bug70_Test.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.alibaba.ttl.reported_bugs

import com.alibaba.ttl.TransmittableThreadLocal
import com.alibaba.ttl.TtlRunnable
import com.alibaba.ttl.internal.TtlValueFactory
import org.junit.Assert.assertEquals
import org.junit.Test
import java.util.concurrent.Executors
Expand Down Expand Up @@ -29,15 +30,18 @@ class Bug70_Test {
.get()
assertEquals(hello, task1.get())

val taskRef = AtomicReference<FutureTask<String>>()
val thread = Thread {
val task2 = FutureTask<String> { threadLocal.get() }
val runnable = TtlRunnable.get(task2, false, false)
executorService.submit(runnable)
taskRef.set(task2)
// inheritable is not supported by FastThreadLocal mode
if (!TtlValueFactory.isFastThreadLocalEnabled()) {
val taskRef = AtomicReference<FutureTask<String>>()
val thread = Thread {
val task2 = FutureTask<String> { threadLocal.get() }
val runnable = TtlRunnable.get(task2, false, false)
executorService.submit(runnable)
taskRef.set(task2)
}
thread.start()
thread.join()
assertEquals(hello, taskRef.get().get())
}
thread.start()
thread.join()
assertEquals(hello, taskRef.get().get())
}
}

0 comments on commit 50a2b6d

Please sign in to comment.