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 void freeDirectBuffer(ByteBuffer buffer) {
86          // Try to minimize overhead when there is no SecurityManager present.
87          // See https://bugs.openjdk.java.net/browse/JDK-8191053.
88          if (System.getSecurityManager() == null) {
89              try {
90                  INVOKE_CLEANER.invokeExact(buffer);
91              } catch (Throwable cause) {
92                  PlatformDependent0.throwException(cause);
93              }
94          } else {
95              freeDirectBufferPrivileged(buffer);
96          }
97      }
98  
99      private static void freeDirectBufferPrivileged(final ByteBuffer buffer) {
100         Throwable error = AccessController.doPrivileged(new PrivilegedAction<Throwable>() {
101             @Override
102             public Throwable run() {
103                 try {
104                     INVOKE_CLEANER.invokeExact(buffer);
105                 } catch (Throwable e) {
106                     return e;
107                 }
108                 return null;
109             }
110         });
111         if (error != null) {
112             PlatformDependent0.throwException(error);
113         }
114     }
115 }