View Javadoc
1   /*
2   * Copyright 2017 The Netty Project
3   *
4   * The Netty Project licenses this file to you under the Apache License,
5   * version 2.0 (the "License"); you may not use this file except in compliance
6   * with the License. You may obtain a copy of the License at:
7   *
8   *   https://www.apache.org/licenses/LICENSE-2.0
9   *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13  * License for the specific language governing permissions and limitations
14  * under the License.
15  */
16  package io.netty.util.internal;
17  
18  import io.netty.util.internal.logging.InternalLogger;
19  import io.netty.util.internal.logging.InternalLoggerFactory;
20  import sun.misc.Unsafe;
21  
22  import java.lang.invoke.MethodHandle;
23  import java.lang.invoke.MethodHandles;
24  import java.nio.ByteBuffer;
25  import java.security.AccessController;
26  import java.security.PrivilegedAction;
27  
28  import static java.lang.invoke.MethodType.methodType;
29  
30  /**
31   * Provide a way to clean a ByteBuffer on Java9+.
32   */
33  final class CleanerJava9 implements Cleaner {
34      private static final InternalLogger logger = InternalLoggerFactory.getInstance(CleanerJava9.class);
35  
36      private static final MethodHandle INVOKE_CLEANER;
37  
38      static {
39          final MethodHandle method;
40          final Throwable error;
41          if (PlatformDependent0.hasUnsafe()) {
42              final ByteBuffer buffer = ByteBuffer.allocateDirect(1);
43              Object maybeInvokeMethod = AccessController.doPrivileged(new PrivilegedAction<Object>() {
44                  @Override
45                  public Object run() {
46                      try {
47                          // See https://bugs.openjdk.java.net/browse/JDK-8171377
48                          Class<? extends Unsafe> unsafeClass = PlatformDependent0.UNSAFE.getClass();
49                          MethodHandles.Lookup lookup = MethodHandles.lookup();
50                          MethodHandle invokeCleaner = lookup.findVirtual(
51                                  unsafeClass, "invokeCleaner", methodType(void.class, ByteBuffer.class));
52                          invokeCleaner = invokeCleaner.bindTo(PlatformDependent0.UNSAFE);
53                          invokeCleaner.invokeExact(buffer);
54                          return invokeCleaner;
55                      } catch (Throwable e) {
56                          return e;
57                      }
58                  }
59              });
60  
61              if (maybeInvokeMethod instanceof Throwable) {
62                  method = null;
63                  error = (Throwable) maybeInvokeMethod;
64              } else {
65                  method = (MethodHandle) maybeInvokeMethod;
66                  error = null;
67              }
68          } else {
69              method = null;
70              error = new UnsupportedOperationException("sun.misc.Unsafe unavailable");
71          }
72          if (error == null) {
73              logger.debug("java.nio.ByteBuffer.cleaner(): available");
74          } else {
75              logger.debug("java.nio.ByteBuffer.cleaner(): unavailable", error);
76          }
77          INVOKE_CLEANER = method;
78      }
79  
80      static boolean isSupported() {
81          return INVOKE_CLEANER != null;
82      }
83  
84      @Override
85      public CleanableDirectBuffer allocate(int capacity) {
86          return new CleanableDirectBufferImpl(ByteBuffer.allocateDirect(capacity));
87      }
88  
89      @Deprecated
90      @Override
91      public void freeDirectBuffer(ByteBuffer buffer) {
92          freeDirectBufferStatic(buffer);
93      }
94  
95      @Override
96      public boolean hasExpensiveClean() {
97          return false;
98      }
99  
100     private static void freeDirectBufferStatic(ByteBuffer buffer) {
101         // Try to minimize overhead when there is no SecurityManager present.
102         // See https://bugs.openjdk.java.net/browse/JDK-8191053.
103         if (System.getSecurityManager() == null) {
104             try {
105                 INVOKE_CLEANER.invokeExact(buffer);
106             } catch (Throwable cause) {
107                 PlatformDependent0.throwException(cause);
108             }
109         } else {
110             freeDirectBufferPrivileged(buffer);
111         }
112     }
113 
114     private static void freeDirectBufferPrivileged(final ByteBuffer buffer) {
115         Throwable error = AccessController.doPrivileged(new PrivilegedAction<Throwable>() {
116             @Override
117             public Throwable run() {
118                 try {
119                     INVOKE_CLEANER.invokeExact(buffer);
120                 } catch (Throwable e) {
121                     return e;
122                 }
123                 return null;
124             }
125         });
126         if (error != null) {
127             PlatformDependent0.throwException(error);
128         }
129     }
130 
131     private static final class CleanableDirectBufferImpl implements CleanableDirectBuffer {
132         private final ByteBuffer buffer;
133 
134         private CleanableDirectBufferImpl(ByteBuffer buffer) {
135             this.buffer = buffer;
136             PlatformDependent.incrementMemoryCounter(buffer.capacity());
137         }
138 
139         @Override
140         public ByteBuffer buffer() {
141             return buffer;
142         }
143 
144         @Override
145         public void clean() {
146             int capacity = buffer.capacity();
147             freeDirectBufferStatic(buffer);
148             PlatformDependent.decrementMemoryCounter(capacity);
149         }
150     }
151 }