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  
21  import java.lang.invoke.MethodHandle;
22  import java.lang.invoke.MethodHandles;
23  import java.nio.ByteBuffer;
24  import java.security.AccessController;
25  import java.security.PrivilegedAction;
26  
27  import static java.lang.invoke.MethodType.methodType;
28  
29  /**
30   * Provide a way to clean a ByteBuffer on Java9+.
31   */
32  final class CleanerJava9 implements Cleaner {
33      private static final InternalLogger logger = InternalLoggerFactory.getInstance(CleanerJava9.class);
34  
35      private static final MethodHandle INVOKE_CLEANER;
36  
37      static {
38          final MethodHandle method;
39          final Throwable error;
40          if (SunMiscUnsafeAccess.isAvailabile()) {
41              final ByteBuffer buffer = ByteBuffer.allocateDirect(1);
42              Object maybeInvokeMethod = AccessController.doPrivileged(new PrivilegedAction<Object>() {
43                  @Override
44                  public Object run() {
45                      try {
46                          // See https://bugs.openjdk.java.net/browse/JDK-8171377
47                          Class<?> unsafeClass = SunMiscUnsafeAccess.UNSAFE.getClass();
48                          MethodHandles.Lookup lookup = MethodHandles.lookup();
49                          MethodHandle invokeCleaner = lookup.findVirtual(
50                                  unsafeClass, "invokeCleaner", methodType(void.class, ByteBuffer.class));
51                          invokeCleaner = invokeCleaner.bindTo(SunMiscUnsafeAccess.UNSAFE);
52                          invokeCleaner.invokeExact(buffer);
53                          return invokeCleaner;
54                      } catch (Throwable e) {
55                          return e;
56                      }
57                  }
58              });
59  
60              if (maybeInvokeMethod instanceof Throwable) {
61                  method = null;
62                  error = (Throwable) maybeInvokeMethod;
63              } else {
64                  method = (MethodHandle) maybeInvokeMethod;
65                  error = null;
66              }
67          } else {
68              method = null;
69              error = new UnsupportedOperationException("sun.misc.Unsafe unavailable",
70                      SunMiscUnsafeAccess.unavailabilityCause());
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 }