1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty5.buffer.api.internal;
17
18 import io.netty5.buffer.api.AllocatorControl;
19 import io.netty5.buffer.api.Buffer;
20 import io.netty5.buffer.api.BufferAllocator;
21 import io.netty5.buffer.api.Drop;
22 import io.netty5.buffer.api.MemoryManager;
23
24 import java.lang.ref.Cleaner;
25 import java.util.concurrent.atomic.AtomicReference;
26
27
28
29
30
31 public final class CleanerDrop<T extends Buffer> implements Drop<T> {
32 private Cleaner.Cleanable cleanable;
33 private GatedRunner<T> runner;
34
35
36
37
38
39 public static <T extends Buffer> Drop<T> wrap(Drop<T> drop, MemoryManager manager) {
40 return innerWrap(drop, manager);
41 }
42
43 private static <T extends Buffer> CleanerDrop<T> innerWrap(Drop<T> drop, MemoryManager manager) {
44 CleanerDrop<T> cleanerDrop = new CleanerDrop<>();
45 GatedRunner<T> runner = new GatedRunner<>(drop, manager);
46 cleanerDrop.cleanable = Statics.CLEANER.register(cleanerDrop, runner);
47 cleanerDrop.runner = runner;
48 return cleanerDrop;
49 }
50
51 private CleanerDrop() {
52 }
53
54 @Override
55 public void attach(T obj) {
56 runner.prepareRecover(obj);
57 runner.drop.attach(obj);
58 }
59
60 @Override
61 public void drop(T obj) {
62 runner.dropping = true;
63 runner.set(obj);
64 cleanable.clean();
65 }
66
67 @Override
68 public Drop<T> fork() {
69 CleanerDrop<T> drop = innerWrap(runner.drop.fork(), runner.manager);
70 drop.runner.tracerFromSplitParent = true;
71 drop.runner.tracer = runner.tracer;
72 return drop;
73 }
74
75 @Override
76 public String toString() {
77 return "CleanerDrop(" + runner.drop + ')';
78 }
79
80 private static final class GatedRunner<T extends Buffer> extends AtomicReference<Object> implements Runnable {
81 private static final NoOpAllocatorControl ALLOC_CONTROL = new NoOpAllocatorControl();
82 private static final long serialVersionUID = 2685535951915798850L;
83 final Drop<T> drop;
84 final MemoryManager manager;
85 volatile boolean dropping;
86 volatile boolean tracerFromSplitParent;
87 LifecycleTracer tracer;
88
89 private GatedRunner(Drop<T> drop, MemoryManager manager) {
90 this.drop = drop;
91 this.manager = manager;
92 }
93
94 @SuppressWarnings("unchecked")
95 @Override
96 public void run() {
97 Object obj = getAndSet(null);
98 if (obj != null) {
99 if (dropping) {
100 drop.drop((T) obj);
101 } else {
102 try (Buffer recoveredBuffer = manager.recoverMemory(ALLOC_CONTROL, obj, (Drop<Buffer>) drop)) {
103 LeakDetection.reportLeak(tracer, "buffer (" + recoveredBuffer.capacity() + " bytes)");
104 }
105 }
106 }
107 }
108
109 public void prepareRecover(T obj) {
110 LifecycleTracer recoveredTracer = ResourceSupport.getTracer((ResourceSupport<?, ?>) obj);
111 if (tracerFromSplitParent) {
112 tracerFromSplitParent = false;
113 tracer.splitTo(recoveredTracer);
114 }
115 tracer = recoveredTracer;
116 set(manager.unwrapRecoverableMemory(obj));
117 }
118 }
119
120 private static final class NoOpAllocatorControl implements AllocatorControl {
121 @Override
122 public BufferAllocator getAllocator() {
123 throw new UnsupportedOperationException();
124 }
125 }
126 }