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.netty5.util.internal;
17  
18  import io.netty5.util.internal.logging.InternalLogger;
19  import io.netty5.util.internal.logging.InternalLoggerFactory;
20  
21  import java.lang.invoke.MethodHandle;
22  import java.lang.invoke.MethodHandles;
23  import java.lang.invoke.MethodType;
24  import java.nio.ByteBuffer;
25  import java.security.AccessController;
26  import java.security.PrivilegedAction;
27  
28  /**
29   * Provide a way to clean a ByteBuffer on Java9+.
30   */
31  final class CleanerJava9 implements Cleaner {
32      private static final InternalLogger logger = InternalLoggerFactory.getInstance(CleanerJava9.class);
33  
34      private static final MethodHandle INVOKE_CLEANER_HANDLE;
35  
36      static {
37          final MethodHandle invokeCleanerHandle;
38          final Throwable error;
39          if (PlatformDependent0.hasUnsafe()) {
40              final ByteBuffer buffer = ByteBuffer.allocateDirect(1);
41              Object maybeInvokeMethodHandle = AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
42                  try {
43                      MethodHandles.Lookup lookup = MethodHandles.lookup();
44                      // See https://bugs.openjdk.java.net/browse/JDK-8171377
45                      MethodHandle m = lookup.findVirtual(
46                              PlatformDependent0.UNSAFE.getClass(),
47                              "invokeCleaner",
48                              MethodType.methodType(void.class, ByteBuffer.class)).bindTo(PlatformDependent0.UNSAFE);
49                      m.invokeExact(buffer);
50                      return m;
51                  } catch (Throwable cause) {
52                      return cause;
53                  }
54              });
55  
56              if (maybeInvokeMethodHandle instanceof Throwable) {
57                  invokeCleanerHandle = null;
58                  error = (Throwable) maybeInvokeMethodHandle;
59              } else {
60                  invokeCleanerHandle = (MethodHandle) maybeInvokeMethodHandle;
61                  error = null;
62              }
63          } else {
64              invokeCleanerHandle = null;
65              error = new UnsupportedOperationException("sun.misc.Unsafe unavailable");
66          }
67          if (error == null) {
68              logger.debug("java.nio.ByteBuffer.cleaner(): available");
69          } else {
70              logger.debug("java.nio.ByteBuffer.cleaner(): unavailable", error);
71          }
72          INVOKE_CLEANER_HANDLE = invokeCleanerHandle;
73      }
74  
75      static boolean isSupported() {
76          return INVOKE_CLEANER_HANDLE != null;
77      }
78  
79      @Override
80      public void freeDirectBuffer(ByteBuffer buffer) {
81          // Try to minimize overhead when there is no SecurityManager present.
82          // See https://bugs.openjdk.java.net/browse/JDK-8191053.
83          if (System.getSecurityManager() == null) {
84              try {
85                  INVOKE_CLEANER_HANDLE.invokeExact(buffer);
86              } catch (RuntimeException exception) {
87                  throw exception;
88              } catch (Throwable throwable) {
89                  PlatformDependent.throwException(throwable);
90              }
91          } else {
92              freeDirectBufferPrivileged(buffer);
93          }
94      }
95  
96      private static void freeDirectBufferPrivileged(final ByteBuffer buffer) {
97          Exception error = AccessController.doPrivileged((PrivilegedAction<Exception>) () -> {
98              try {
99                  INVOKE_CLEANER_HANDLE.invokeExact(PlatformDependent0.UNSAFE, buffer);
100             } catch (RuntimeException exception) {
101                 return exception;
102             } catch (Throwable throwable) {
103                 PlatformDependent.throwException(throwable);
104             }
105             return null;
106         });
107         if (error != null) {
108             PlatformDependent.throwException(error);
109         }
110     }
111 }