GlueGen v2.6.0-rc-20250712
GlueGen, Native Binding Generator for Java™ (public API).
UnsafeUtil.java
Go to the documentation of this file.
1/**
2 * Copyright 2019 JogAmp Community. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without modification, are
5 * permitted provided that the following conditions are met:
6 *
7 * 1. Redistributions of source code must retain the above copyright notice, this list of
8 * conditions and the following disclaimer.
9 *
10 * 2. Redistributions in binary form must reproduce the above copyright notice, this list
11 * of conditions and the following disclaimer in the documentation and/or other materials
12 * provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED
15 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
16 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
19 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
20 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
21 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
22 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 *
24 * The views and conclusions contained in the software and documentation are those of the
25 * authors and should not be interpreted as representing official policies, either expressed
26 * or implied, of JogAmp Community.
27 */
28package com.jogamp.common.util;
29
30import java.lang.reflect.Field;
31import java.lang.reflect.InvocationTargetException;
32import java.lang.reflect.Method;
33import java.nio.ByteBuffer;
34import java.security.PrivilegedAction;
35
36import com.jogamp.common.ExceptionUtils;
37
38import jogamp.common.Debug;
39import jogamp.common.os.PlatformPropsImpl;
40
41/**
42 * Utility methods allowing easy access to certain {@link sun.misc.Unsafe} functionality.
43 */
44public class UnsafeUtil {
45
46 static final boolean DEBUG;
47 static {
48 DEBUG = Debug.debug("UnsafeUtil");
49 }
50
51 protected UnsafeUtil() {}
52
53 private static final Object theUnsafe;
54 private static final Method unsafeCleanBB;
55 private static volatile boolean hasUnsafeCleanBBError; /** OK to be lazy on thread synchronization, just for early out **/
56
57 private static final Method staticFieldOffset;
58 private static final Method getObjectVolatile;
59 private static final Method putObjectVolatile;
60 private static volatile boolean hasGetPutObjectVolatile;
61
62 private static final Class<?> illegalAccessLoggerClass;
63 private static final Long illegalAccessLoggerOffset;
64 private static final Object illegalAccessLoggerSync = new Object();
65 private static volatile boolean hasIllegalAccessError;
66
67 static {
68 final Object[] _theUnsafe = { null };
69 final Method[] _cleanBB = { null };
70 final Method[] _staticFieldOffset = { null };
71 final Method[] _objectVolatile = { null, null }; // unsafeGetObjectVolatile, unsafePutObjectVolatile
72 final Class<?>[] _illegalAccessLoggerClass = { null };
73 final Long[] _loggerOffset = { null };
74
75 SecurityUtil.doPrivileged(new PrivilegedAction<Object>() {
76 @Override
77 public Object run() {
78 Class<?> unsafeClass = null;
79 try {
80 // Using: sun.misc.Unsafe { public void invokeCleaner(java.nio.ByteBuffer directBuffer); }
81 unsafeClass = Class.forName("sun.misc.Unsafe");
82 {
83 final Field f = unsafeClass.getDeclaredField("theUnsafe");
84 f.setAccessible(true);
85 _theUnsafe[0] = f.get(null);
86 }
87 _cleanBB[0] = unsafeClass.getMethod("invokeCleaner", java.nio.ByteBuffer.class);
88 _cleanBB[0].setAccessible(true);
89 } catch(final Throwable t) {
90 if( DEBUG ) {
91 ExceptionUtils.dumpThrowable("UnsafeUtil", t);
92 }
93 }
94 if( null != _theUnsafe[0] && PlatformPropsImpl.JAVA_9 ) {
95 try {
96 _staticFieldOffset[0] = unsafeClass.getDeclaredMethod("staticFieldOffset", Field.class);
97 _objectVolatile[0] = unsafeClass.getDeclaredMethod("getObjectVolatile", Object.class, long.class);
98 _objectVolatile[1] = unsafeClass.getDeclaredMethod("putObjectVolatile", Object.class, long.class, Object.class);
99
100 if( PlatformPropsImpl.JAVA_9 ) {
101 _illegalAccessLoggerClass[0] = Class.forName("jdk.internal.module.IllegalAccessLogger");
102 final Field loggerField = _illegalAccessLoggerClass[0].getDeclaredField("logger");
103 _loggerOffset[0] = (Long) _staticFieldOffset[0].invoke(_theUnsafe[0], loggerField);
104 }
105 } catch(final Throwable t) {
106 if( DEBUG ) {
107 ExceptionUtils.dumpThrowable("UnsafeUtil", t);
108 }
109 }
110 }
111 return null;
112 } } );
113 theUnsafe = _theUnsafe[0];
114 unsafeCleanBB = _cleanBB[0];
115 hasUnsafeCleanBBError = null == theUnsafe || null == unsafeCleanBB;
116 if( DEBUG ) {
117 System.err.println("UnsafeUtil.init: hasTheUnsafe: "+(null!=theUnsafe)+", hasInvokeCleaner: "+!hasUnsafeCleanBBError);
118 }
119
120 staticFieldOffset = _staticFieldOffset[0];
121 getObjectVolatile = _objectVolatile[0];
122 putObjectVolatile = _objectVolatile[1];
123 hasGetPutObjectVolatile = null != staticFieldOffset && null != getObjectVolatile && null != putObjectVolatile;
124 illegalAccessLoggerClass = _illegalAccessLoggerClass[0];
125 illegalAccessLoggerOffset = _loggerOffset[0];
126 hasIllegalAccessError = !hasGetPutObjectVolatile || null == illegalAccessLoggerClass || null == illegalAccessLoggerOffset;
127 if( DEBUG ) {
128 System.err.println("UnsafeUtil.init: hasUnsafeGetPutObjectVolatile: "+hasGetPutObjectVolatile+", hasUnsafeIllegalAccessLogger: "+!hasIllegalAccessError);
129 }
130 }
131
132 /**
133 * Returns {@code true} if {@code sun.misc.Unsafe.invokeCleaner(java.nio.ByteBuffer)}
134 * is available and has not caused an exception.
135 * @see #invokeCleaner(ByteBuffer)
136 */
137 public static boolean hasInvokeCleaner() { return !hasUnsafeCleanBBError; }
138
139 /**
140 * Access to {@code sun.misc.Unsafe.invokeCleaner(java.nio.ByteBuffer)}.
141 * <p>
142 * If {@code b} is an direct NIO buffer, i.e {@link sun.nio.ch.DirectBuffer},
143 * calls it's {@link sun.misc.Cleaner} instance {@code clean()} method once.
144 * </p>
145 * @return {@code true} if successful, otherwise {@code false}.
146 * @see #hasInvokeCleaner()
147 */
148 public static boolean invokeCleaner(final ByteBuffer bb) {
149 if( hasUnsafeCleanBBError || !bb.isDirect() ) {
150 return false;
151 }
152 try {
153 unsafeCleanBB.invoke(theUnsafe, bb);
154 return true;
155 } catch(final Throwable t) {
156 hasUnsafeCleanBBError = true;
157 if( DEBUG ) {
158 ExceptionUtils.dumpThrowable("UnsafeUtil", t);
159 }
160 return false;
161 }
162 }
163
164 /**
165 * Returns {@code true} if access to {@code jdk.internal.module.IllegalAcessLogger}'s {@code logger} field
166 * is available and has not caused an exception.
167 * @see #doWithoutIllegalAccessLogger(PrivilegedAction)
168 */
169 public static boolean hasIllegalAccessLoggerAccess() { return !hasIllegalAccessError; }
170
171 /**
172 * Issue the given user {@code action} while {@code jdk.internal.module.IllegalAcessLogger}'s {@code logger} has been temporarily disabled.
173 * <p>
174 * The caller shall place this call into their own {@link SecurityUtil#doPrivileged(PrivilegedAction)} block.
175 * </p>
176 * <p>
177 * In case the runtime is not {@link PlatformPropsImpl#JAVA_9} or the logger is not accessible or disabling caused an exception,
178 * the user {@code action} is just executed w/o temporary logger modifications.
179 * </p>
180 * @param action the user action task
181 * @throws RuntimeException is thrown for a caught {@link Throwable} while executing the user {@code action}
182 * @see #hasIllegalAccessLoggerAccess()
183 */
184 public static <T> T doWithoutIllegalAccessLogger(final PrivilegedAction<T> action) throws RuntimeException {
185 if( !hasIllegalAccessError ) {
186 synchronized(illegalAccessLoggerSync) {
187 final Object newLogger = null;
188 Object oldLogger = null;
189 try {
190 oldLogger = getObjectVolatile.invoke(theUnsafe, illegalAccessLoggerClass, illegalAccessLoggerOffset);
191 putObjectVolatile.invoke(theUnsafe, illegalAccessLoggerClass, illegalAccessLoggerOffset, newLogger);
192 } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
193 // unaccessible ..
194 hasIllegalAccessError = true;
195 if( DEBUG ) {
196 ExceptionUtils.dumpThrowable("UnsafeUtil", e);
197 }
198 return action.run();
199 }
200 try {
201 return action.run();
202 } catch (final Throwable t) {
203 if( DEBUG ) {
204 t.printStackTrace();
205 }
206 throw new RuntimeException(t);
207 } finally {
208 try {
209 putObjectVolatile.invoke(theUnsafe, illegalAccessLoggerClass, illegalAccessLoggerOffset, oldLogger);
210 } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
211 // should not happen, worked above @ logger setup
212 hasIllegalAccessError = true;
213 throw new InternalError(e);
214 }
215 }
216 }
217 } else {
218 return action.run();
219 }
220 }
221}
static void dumpThrowable(final String additionalDescr, final Throwable t)
Dumps a Throwable to System.err in a decorating message including the current thread name,...
Utility methods allowing easy access to certain sun.misc.Unsafe functionality.
Definition: UnsafeUtil.java:44
static boolean hasIllegalAccessLoggerAccess()
Returns true if access to jdk.internal.module.IllegalAcessLogger's logger field is available and has ...
static boolean invokeCleaner(final ByteBuffer bb)
Access to sun.misc.Unsafe.invokeCleaner(java.nio.ByteBuffer).
static< T > T doWithoutIllegalAccessLogger(final PrivilegedAction< T > action)
Issue the given user action while jdk.internal.module.IllegalAcessLogger's logger has been temporaril...
static boolean hasInvokeCleaner()
Returns true if sun.misc.Unsafe.invokeCleaner(java.nio.ByteBuffer) is available and has not caused an...