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      private static void freeDirectBufferStatic(ByteBuffer buffer) {
96          // Try to minimize overhead when there is no SecurityManager present.
97          // See https://bugs.openjdk.java.net/browse/JDK-8191053.
98          if (System.getSecurityManager() == null) {
99              try {
100                 INVOKE_CLEANER.invokeExact(buffer);
101             } catch (Throwable cause) {
102                 PlatformDependent0.throwException(cause);
103             }
104         } else {
105             freeDirectBufferPrivileged(buffer);
106         }
107     }
108 
109     private static void freeDirectBufferPrivileged(final ByteBuffer buffer) {
110         Throwable error = AccessController.doPrivileged(new PrivilegedAction<Throwable>() {
111             @Override
112             public Throwable run() {
113                 try {
114                     INVOKE_CLEANER.invokeExact(buffer);
115                 } catch (Throwable e) {
116                     return e;
117                 }
118                 return null;
119             }
120         });
121         if (error != null) {
122             PlatformDependent0.throwException(error);
123         }
124     }
125 
126     private static final class CleanableDirectBufferImpl implements CleanableDirectBuffer {
127         private final ByteBuffer buffer;
128 
129         private CleanableDirectBufferImpl(ByteBuffer buffer) {
130             this.buffer = buffer;
131         }
132 
133         @Override
134         public ByteBuffer buffer() {
135             return buffer;
136         }
137 
138         @Override
139         public void clean() {
140             freeDirectBufferStatic(buffer);
141         }
142     }
143 }