1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package io.netty.util.test;
18
19 import io.netty.util.LeakPresenceDetector;
20 import org.junit.jupiter.api.extension.AfterAllCallback;
21 import org.junit.jupiter.api.extension.AfterEachCallback;
22 import org.junit.jupiter.api.extension.BeforeAllCallback;
23 import org.junit.jupiter.api.extension.BeforeEachCallback;
24 import org.junit.jupiter.api.extension.ExtensionContext;
25
26 import java.util.concurrent.TimeUnit;
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45 public final class LeakPresenceExtension
46 implements BeforeAllCallback, BeforeEachCallback, AfterEachCallback, AfterAllCallback {
47
48 private static final Object SCOPE_KEY = new Object();
49 private static final Object PREVIOUS_SCOPE_KEY = new Object();
50
51 static {
52 System.setProperty("io.netty.customResourceLeakDetector", WithTransferableScope.class.getName());
53 }
54
55 @Override
56 public void beforeAll(ExtensionContext context) {
57 ExtensionContext.Store store = context.getStore(ExtensionContext.Namespace.GLOBAL);
58 LeakPresenceDetector.ResourceScope existingScope = (LeakPresenceDetector.ResourceScope) store.get(SCOPE_KEY);
59 if (existingScope != null) {
60 existingScope.retain();
61 return;
62 }
63 LeakPresenceDetector.ResourceScope scope = new LeakPresenceDetector.ResourceScope(context.getDisplayName());
64 store.put(SCOPE_KEY, scope);
65
66 WithTransferableScope.SCOPE.set(scope);
67 }
68
69 @Override
70 public void beforeEach(ExtensionContext context) {
71 LeakPresenceDetector.ResourceScope outerScope;
72 ExtensionContext outerContext = context;
73 while (true) {
74 outerScope = (LeakPresenceDetector.ResourceScope)
75 outerContext.getStore(ExtensionContext.Namespace.GLOBAL).get(SCOPE_KEY);
76 if (outerScope != null) {
77 break;
78 }
79 outerContext = outerContext.getParent()
80 .orElseThrow(() -> new IllegalStateException("No resource scope found"));
81 }
82
83 LeakPresenceDetector.ResourceScope previousScope = WithTransferableScope.SCOPE.get();
84 WithTransferableScope.SCOPE.set(outerScope);
85 if (previousScope != null) {
86 context.getStore(ExtensionContext.Namespace.GLOBAL).put(PREVIOUS_SCOPE_KEY, previousScope);
87 }
88 }
89
90 @Override
91 public void afterEach(ExtensionContext context) {
92 LeakPresenceDetector.ResourceScope previousScope = (LeakPresenceDetector.ResourceScope)
93 context.getStore(ExtensionContext.Namespace.GLOBAL).get(PREVIOUS_SCOPE_KEY);
94 if (previousScope != null) {
95 WithTransferableScope.SCOPE.set(previousScope);
96 }
97 }
98
99 @Override
100 public void afterAll(ExtensionContext context) throws InterruptedException {
101 LeakPresenceDetector.ResourceScope scope =
102 (LeakPresenceDetector.ResourceScope) context.getStore(ExtensionContext.Namespace.GLOBAL).get(SCOPE_KEY);
103
104
105 long start = System.nanoTime();
106 while (scope.hasOpenResources() && System.nanoTime() - start < TimeUnit.SECONDS.toNanos(5)) {
107 TimeUnit.MILLISECONDS.sleep(100);
108 }
109
110 scope.close();
111 }
112
113 public static final class WithTransferableScope<T> extends LeakPresenceDetector<T> {
114 static final InheritableThreadLocal<ResourceScope> SCOPE = new InheritableThreadLocal<>();
115
116 @SuppressWarnings("unused")
117 public WithTransferableScope(Class<?> resourceType, int samplingInterval) {
118 super(resourceType);
119 }
120
121 @SuppressWarnings("unused")
122 public WithTransferableScope(Class<?> resourceType, int samplingInterval, long maxActive) {
123 super(resourceType);
124 }
125
126 @Override
127 protected ResourceScope currentScope() throws AllocationProhibitedException {
128 ResourceScope scope = SCOPE.get();
129 if (scope == null) {
130 throw new AllocationProhibitedException("Resource created outside test?");
131 }
132 return scope;
133 }
134 }
135 }